diff --git a/self-host/manual/docker-compose.mdx b/self-host/manual/docker-compose.mdx index b84edc2..6248e20 100644 --- a/self-host/manual/docker-compose.mdx +++ b/self-host/manual/docker-compose.mdx @@ -7,146 +7,71 @@ import PangolinCloudTocCta from "/snippets/pangolin-cloud-toc-cta.mdx"; +This guide walks through a manual deployment using the same file layout the installer generates from `install/config/*` in the Pangolin source tree. Use it if you want the installer's defaults, but you want to create and maintain the files yourself. - -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. +This guide assumes you already have a Linux server with Docker and Docker Compose installed, plus root or sudo access. ## Prerequisites -Checkout the [quick install guide](/self-host/quick-install) for more info regarding what is needed before you install Pangolin. +Review the [quick install guide](/self-host/quick-install) and [DNS & networking](/self-host/dns-and-networking) first. At minimum you need: -## File Structure +- A public Linux server +- A base domain such as `example.com` +- A dashboard hostname such as `pangolin.example.com` +- An email address for Let's Encrypt +- TCP ports `80` and `443` open +- UDP ports `51820` and `21820` open if you are using tunneling -Create the following directory structure for your Pangolin deployment: + +If you do not want tunneling, see [Without Tunneling](/self-host/advanced/without-tunneling). In that mode you will skip the `gerbil` service and expose Traefik directly. + -``` + +`base domain` is the parent domain you will attach resources to, such as `example.com`. `dashboard hostname` is the specific hostname for the Pangolin UI and API, such as `pangolin.example.com`. + + +## File Layout + +Create the following project structure: + +```text . -├── config/ -│ ├── config.yml (*) -│ ├── db/ -│ │ └── db.sqlite -│ ├── key -│ ├── letsencrypt/ -│ │ └── acme.json -│ ├── logs/ -│ └── traefik/ -│ ├── traefik_config.yml (*) -│ └── dynamic_config.yml (*) -└── docker-compose.yml (*) +├── docker-compose.yml +└── config/ + ├── config.yml + ├── db/ + ├── letsencrypt/ + └── traefik/ + ├── dynamic_config.yml + ├── logs/ + └── traefik_config.yml ``` - -Files marked with `(*)` must be created manually. Volumes and other files are generated automatically by the services. - +The following files are created later by the running services or added only when you enable optional features: - - - **`config/config.yml`**: Main Pangolin configuration file - - Contains all Pangolin settings and options - - See [Configuration Guide](/self-host/advanced/config-file) 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` is created by Pangolin on first startup. +- `config/key` is created by Gerbil when tunneling is enabled. +- `config/GeoLite2-Country.mmdb` is optional and only needed for [geo-blocking](/self-host/advanced/enable-geoblocking). It is not downloaded by the running services in a manual install; download it manually before enabling geo-blocking. - - **`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 - +## Create the Directories - - **`docker-compose.yml`**: Service definitions - - Defines Pangolin, Gerbil, and Traefik services - - Network configuration and volume mounts - - Health checks and dependencies - - +Create the project folders: + +```bash +mkdir -p config/db config/letsencrypt config/traefik/logs +``` + +## Create the Configuration Files - - ```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: + + This file defines the Pangolin, Gerbil, and Traefik containers, their shared volumes, and the ports exposed on the host. ```yaml title="docker-compose.yml" name: pangolin services: pangolin: - image: docker.io/fosrl/pangolin:latest # https://github.com/fosrl/pangolin/releases + image: docker.io/fosrl/pangolin:latest container_name: pangolin restart: unless-stopped volumes: @@ -158,7 +83,7 @@ services: retries: 15 gerbil: - image: docker.io/fosrl/gerbil:latest # https://github.com/fosrl/gerbil/releases + image: docker.io/fosrl/gerbil:latest container_name: gerbil restart: unless-stopped depends_on: @@ -177,35 +102,38 @@ services: - 51820:51820/udp - 21820:21820/udp - 443:443 + # - 443:443/udp # Uncomment if you enable HTTP/3 in Traefik. - 80:80 traefik: image: docker.io/traefik:v3.6 container_name: traefik restart: unless-stopped - - network_mode: service:gerbil # Ports appear on the gerbil service - + network_mode: service:gerbil 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 - - ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs + - ./config/traefik:/etc/traefik:ro + - ./config/letsencrypt:/letsencrypt + - ./config/traefik/logs:/var/log/traefik networks: default: driver: bridge name: pangolin - #enable_ipv6: true # activate if your system supports IPv6 + # enable_ipv6: true ``` -## Traefik Static Configuration + +This is the installer's default community layout with Gerbil enabled. If you want to pin releases instead of using `latest`, replace the image tags with the versions you intend to run. + + -Create `config/traefik/traefik_config.yml`: + + This file configures Traefik's providers, Badger plugin, Let's Encrypt resolver, entry points, logs, and health check endpoint. ```yaml title="config/traefik/traefik_config.yml" api: @@ -223,7 +151,7 @@ experimental: plugins: badger: moduleName: "github.com/fosrl/badger" - version: "v1.3.1" + version: "v1.4.0" # Check github.com/fosrl/badger for the latest release. log: level: "INFO" @@ -238,7 +166,7 @@ certificatesResolvers: acme: httpChallenge: entryPoint: web - email: "admin@example.com" # REPLACE WITH YOUR EMAIL + email: "admin@example.com" # REPLACE storage: "/letsencrypt/acme.json" caServer: "https://acme-v02.api.letsencrypt.org/directory" @@ -250,6 +178,9 @@ entryPoints: transport: respondingTimeouts: readTimeout: "30m" + # Uncomment to enable HTTP/3. You must also expose 443/udp in docker-compose.yml. + # http3: + # advertisedPort: 443 http: tls: certResolver: "letsencrypt" @@ -264,9 +195,13 @@ ping: entryPoint: "web" ``` -## Traefik Dynamic Configuration + + Traefik stores Let's Encrypt certificates at `/letsencrypt/acme.json` inside the container. The Compose file mounts that path from `./config/letsencrypt`, so Traefik will create `config/letsencrypt/acme.json` when it needs certificate storage. + + -Create `config/traefik/dynamic_config.yml`: + + This file defines the routers, middleware, and services that send dashboard, API, and WebSocket traffic to Pangolin. ```yaml title="config/traefik/dynamic_config.yml" http: @@ -280,9 +215,8 @@ http: scheme: https routers: - # HTTP to HTTPS redirect router main-app-router-redirect: - rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN + rule: "Host(`pangolin.example.com`)" # REPLACE service: next-service entryPoints: - web @@ -290,9 +224,8 @@ http: - redirect-to-https - badger - # Next.js router (handles everything except API and WebSocket paths) next-router: - rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN + rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE service: next-service entryPoints: - websecure @@ -301,9 +234,8 @@ http: tls: certResolver: letsencrypt - # API router (handles /api/v1 paths) api-router: - rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN + rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE service: api-service entryPoints: - websecure @@ -312,9 +244,8 @@ http: tls: certResolver: letsencrypt - # WebSocket router ws-router: - rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN + rule: "Host(`pangolin.example.com`)" # REPLACE service: api-service entryPoints: - websecure @@ -327,12 +258,12 @@ http: next-service: loadBalancer: servers: - - url: "http://pangolin:3002" # Next.js server + - url: "http://pangolin:3002" api-service: loadBalancer: servers: - - url: "http://pangolin:3000" # API/WebSocket server + - url: "http://pangolin:3000" tcp: serversTransports: @@ -343,7 +274,162 @@ tcp: proxyProtocol: version: 2 ``` + -## Pangolin Configuration + + This file contains Pangolin's application settings, dashboard domain, base domain, CORS origin, and server secret. -Create `config/config.yml` with your Pangolin settings. See the [configuration guide](/self-host/advanced/config-file) for detailed options and examples. +```yaml title="config/config.yml" +# To see all available options, please visit the docs: +# https://docs.pangolin.net/ + +gerbil: + start_port: 51820 + base_endpoint: "pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN + +app: + dashboard_url: "https://pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN + log_level: "info" + telemetry: + anonymous_usage: true + +domains: + domain1: + base_domain: "example.com" # REPLACE WITH YOUR BASE DOMAIN + +server: + secret: "replace-with-a-long-random-secret" # REPLACE WITH SECURE SECRET + cors: + origins: ["https://pangolin.example.com"] # REPLACE WITH YOUR DASHBOARD DOMAIN + methods: ["GET", "POST", "PUT", "DELETE", "PATCH"] + allowed_headers: ["X-CSRF-Token", "Content-Type"] + credentials: false + +flags: + require_email_verification: false + disable_signup_without_invite: true + disable_user_create_org: false + allow_raw_resources: true +``` + +Replace these values before starting the stack: + +- `pangolin.example.com` with your dashboard hostname +- `example.com` with your base domain +- `replace-with-a-long-random-secret` with a strong random secret +- `admin@example.com` in `traefik_config.yml` with your Let's Encrypt email + +Generate a secret with: + +```bash +openssl rand -hex 32 +``` + + +Do not reuse a weak or short `server.secret`. If you need to rotate it later, use `pangctl rotate-server-secret`. See the [container CLI tool guide](/self-host/advanced/container-cli-tool#rotate-server-secret). + + + + + +### Optional Email Configuration + +If you want Pangolin to send email, add this block to `config/config.yml` and set `flags.require_email_verification` to `true`: + +```yaml title="config/config.yml" +email: + smtp_host: "smtp.example.com" + smtp_port: 587 + smtp_user: "smtp-user" + smtp_pass: "smtp-password" + no_reply: "noreply@example.com" +``` + +### Optional Geo-blocking Configuration + +If you want geo-blocking, download the MaxMind database and add this line under `server`: + +```yaml title="config/config.yml" +server: + maxmind_db_path: "./config/GeoLite2-Country.mmdb" +``` + +See [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) for the full process. + +## Start the Stack + + + + ```bash + sudo docker compose up -d + ``` + + + + ```bash + sudo docker compose logs -f pangolin traefik gerbil + ``` + + + + ```bash + sudo docker compose ps + ``` + + `pangolin`, `traefik`, and `gerbil` should all report as running after the first startup finishes. + + + + Check the Pangolin container logs: + + ```bash + sudo docker compose logs pangolin + ``` + + Pangolin prints a setup token to stdout on first boot. Copy that token before continuing. + + + + Visit: + + ```text + https://pangolin.example.com/auth/initial-setup + ``` + + Replace the hostname with your real dashboard domain, then use the setup token from the Pangolin logs to register the first admin account. + + + +## Verify the Setup + +You should expect the following on a healthy first install: + +- `docker compose ps` shows `pangolin`, `traefik`, and `gerbil` as running. +- `docker compose logs pangolin` includes the one-time setup token for the first admin account. +- Visiting `https:///auth/initial-setup` loads the setup page. +- `config/db/db.sqlite` exists after Pangolin starts. +- `config/key` exists after Gerbil starts. + + +The first Let's Encrypt certificate request can take a short while. If the page initially shows a certificate warning, wait a minute and refresh. + + +## If Something Fails + +- If the setup page does not load, confirm your DNS record points to the server and ports `80` and `443` are reachable. +- If you cannot complete first-time signup, check `sudo docker compose logs pangolin` and copy the setup token printed by Pangolin. +- If certificates are not issued, confirm `admin@example.com` was replaced and that nothing else is already bound to ports `80` or `443`. +- If `pangolin` never becomes healthy, inspect `sudo docker compose logs -f pangolin`. +- If tunneling does not work, inspect `sudo docker compose logs -f gerbil` and confirm UDP ports `51820` and `21820` are open. +- If Traefik serves the wrong host, re-check every `pangolin.example.com` replacement in both Traefik files and `config/config.yml`. + +## Without Tunneling + +If you do not want Gerbil: + +- Remove the `gerbil` service. +- Remove `network_mode: service:gerbil` from `traefik`. +- Add ports `80:80` and `443:443` directly to `traefik`. +- Remove the `gerbil` block from `config/config.yml`. + +That mode is covered in more detail in [Without Tunneling](/self-host/advanced/without-tunneling). diff --git a/self-host/quick-install.mdx b/self-host/quick-install.mdx index 944aa86..5c8cc5a 100644 --- a/self-host/quick-install.mdx +++ b/self-host/quick-install.mdx @@ -109,9 +109,20 @@ https://pangolin.example.com/auth/initial-setup + + Check the Pangolin container logs and copy the setup token printed to stdout: + + ```bash + sudo docker compose logs pangolin + ``` + + You will need that token on the initial setup page to register the first admin account. + + Complete the initial admin user setup: + - Paste the setup token from the Pangolin logs - Enter your admin email address - Set a strong password - Verify your email (if email is configured)