--- title: "Docker Compose" description: "Deploy Pangolin manually using Docker Compose without the automated installer" --- This guide walks you through setting up Pangolin manually using Docker Compose without the automated installer. This approach gives you full control over the configuration and deployment process. This guide assumes you already have a Linux server with Docker and Docker Compose installed. If you don't, please refer to the [official Docker documentation](https://docs.docker.com/get-docker/) for installation instructions. You must also have root access to the server. ## Prerequisites Checkout the [quick install guide](self-host/quick-install) for more info regarding what is needed before you install Pangolin. ## File Structure Create the following directory structure for your Pangolin deployment: ``` . ├── config/ │ ├── config.yml (*) │ ├── db/ │ │ └── db.sqlite │ ├── key │ ├── letsencrypt/ │ │ └── acme.json │ ├── logs/ │ └── traefik/ │ ├── traefik_config.yml (*) │ └── dynamic_config.yml (*) └── docker-compose.yml (*) ``` Files marked with `(*)` must be created manually. Volumes and other files are generated automatically by the services. **`config/config.yml`**: Main Pangolin configuration file - Contains all Pangolin settings and options - See [Configuration Guide](/pangolin/configuration/config) for details **`config/traefik/traefik_config.yml`**: Traefik static configuration - Global Traefik settings and entry points - SSL certificate resolver configuration **`config/traefik/dynamic_config.yml`**: Traefik dynamic configuration - HTTP routers and services for Pangolin - Load balancer and middleware configuration **`config/db/db.sqlite`**: SQLite database file - Created automatically on first startup - Contains all Pangolin data and settings **`config/key`**: Private key file - Generated by Gerbil service - Used for WireGuard tunnel encryption **`config/letsencrypt/acme.json`**: SSL certificate storage - Managed by Traefik - Contains Let's Encrypt certificates **`docker-compose.yml`**: Service definitions - Defines Pangolin, Gerbil, and Traefik services - Network configuration and volume mounts - Health checks and dependencies ```bash mkdir -p config/traefik config/db config/letsencrypt config/logs ``` Create the main configuration files (see below): - `docker-compose.yml` (in project root) - `config/traefik/traefik_config.yml` - `config/traefik/dynamic_config.yml` - `config/config.yml` Edit the configuration files to replace: - `pangolin.example.com` with your actual domain - `admin@example.com` with your email address Ensure your domain DNS is properly configured to point to your server's IP address. ## Starting the Stack ```bash sudo docker compose up -d ``` ```bash sudo docker compose logs -f ``` ```bash sudo docker compose ps ``` All services should show "Up" status after a few minutes. Navigate to `https://your-domain.com/auth/initial-setup` to complete the initial setup. The dashboard should load with SSL certificate automatically configured. ## Docker Compose Configuration Create `docker-compose.yml` in your project root: ```yaml title="docker-compose.yml" services: pangolin: image: fosrl/pangolin:latest # https://github.com/fosrl/pangolin/releases container_name: pangolin restart: unless-stopped volumes: - ./config:/app/config - pangolin-data:/var/certificates - pangolin-data:/var/dynamic healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"] interval: "3s" timeout: "3s" retries: 15 gerbil: image: fosrl/gerbil:latest # https://github.com/fosrl/gerbil/releases container_name: gerbil restart: unless-stopped depends_on: pangolin: condition: service_healthy command: - --reachableAt=http://gerbil:3003 - --generateAndSaveKeyTo=/var/config/key - --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config - --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth volumes: - ./config/:/var/config cap_add: - NET_ADMIN - SYS_MODULE ports: - 51820:51820/udp - 21820:21820/udp - 443:443 # Port for traefik because of the network_mode - 80:80 # Port for traefik because of the network_mode traefik: image: traefik:v3.4.0 container_name: traefik restart: unless-stopped network_mode: service:gerbil # Ports appear on the gerbil service depends_on: pangolin: condition: service_healthy command: - --configFile=/etc/traefik/traefik_config.yml volumes: - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration - ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates # Shared volume for certificates and dynamic config in file mode - pangolin-data:/var/certificates:ro - pangolin-data:/var/dynamic:ro networks: default: driver: bridge name: pangolin volumes: pangolin-data: ``` ## Traefik Static Configuration Create `config/traefik/traefik_config.yml`: ```yaml title="config/traefik/traefik_config.yml" api: insecure: true dashboard: true providers: http: endpoint: "http://pangolin:3001/api/v1/traefik-config" pollInterval: "5s" file: filename: "/etc/traefik/dynamic_config.yml" experimental: plugins: badger: moduleName: "github.com/fosrl/badger" version: "v1.2.0" log: level: "INFO" format: "common" certificatesResolvers: letsencrypt: acme: httpChallenge: entryPoint: web email: admin@example.com # REPLACE WITH YOUR EMAIL storage: "/letsencrypt/acme.json" caServer: "https://acme-v02.api.letsencrypt.org/directory" entryPoints: web: address: ":80" websecure: address: ":443" transport: respondingTimeouts: readTimeout: "30m" http: tls: certResolver: "letsencrypt" serversTransport: insecureSkipVerify: true ping: entryPoint: "web" ``` ## Traefik Dynamic Configuration Create `config/traefik/dynamic_config.yml`: ```yaml title="config/traefik/dynamic_config.yml" http: middlewares: redirect-to-https: redirectScheme: scheme: https routers: # HTTP to HTTPS redirect router main-app-router-redirect: rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN service: next-service entryPoints: - web middlewares: - redirect-to-https # Next.js router (handles everything except API and WebSocket paths) next-router: rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN service: next-service entryPoints: - websecure tls: certResolver: letsencrypt # API router (handles /api/v1 paths) api-router: rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN service: api-service entryPoints: - websecure tls: certResolver: letsencrypt # WebSocket router ws-router: rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN service: api-service entryPoints: - websecure tls: certResolver: letsencrypt services: next-service: loadBalancer: servers: - url: "http://pangolin:3002" # Next.js server api-service: loadBalancer: servers: - url: "http://pangolin:3000" # API/WebSocket server ``` ## Pangolin Configuration Create `config/config.yml` with your Pangolin settings. See the [configuration guide](/self-host/advanced/config-file) for detailed options and examples.