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 for installation instructions. You must also have root access to the server.
Prerequisites
Checkout the quick install guide 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 fileconfig/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
Create configuration directory
mkdir -p config/traefik config/db config/letsencrypt config/logs
Create configuration files
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
Update domain and email
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
Start the services
sudo docker compose up -d
Monitor startup
sudo docker compose logs -f
Verify services
All services should show “Up” status after a few minutes.
Access the dashboard
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:
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:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
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
:
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
:
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 for detailed options and examples.