Skip to content

Docker and Env

How to deploy Pankha Fan Control with Docker, and every environment variable the stack actually reads. Essentials first; advanced setups below.

New here? Server Installation walks through a first-time setup step by step - this page is the full reference to come back to.


Terminal window
# 1. Get the two files
curl -LO https://raw.githubusercontent.com/Anexgohan/pankha/main/compose.yml
curl -L -o .env https://raw.githubusercontent.com/Anexgohan/pankha/main/.env
# 2. Edit .env - at minimum set POSTGRES_USER, POSTGRES_PASSWORD, PANKHA_HUB_IP
# 3. Start
docker compose up -d

Open http://<server-ip>:3143 - the dashboard walks you through creating the first admin account.


Set these in the .env file next to compose.yml (or as -e flags with docker run).

VariableAccepted valuesDefaultDescription
POSTGRES_USERany stringset your ownDatabase username.
POSTGRES_PASSWORDany stringset your ownDatabase password. Never keep a published example value.
POSTGRES_DBdatabase namedb_pankhaDatabase name.
PANKHA_HUB_IPLAN IP or hostnameunsetThe address agents connect to. Baked into the install scripts the Deployment page generates, so it must be reachable from every agent machine (e.g. 192.168.1.100).
VariableAccepted valuesDefaultDescription
PANKHA_PORTport number3143Host port for the dashboard, API, and WebSocket.
TIMEZONEtz database nameUTCTimezone for dashboard times and logs.
LOG_LEVELerror, warn, info, debug, traceinfoBackend log verbosity.
PANKHA_STAGING_DIRabsolute path/app/backend/data/stagingWhere downloaded agent binaries, checksums, and reports are stored (mounted to ./docker-data/staging in compose).
PANKHA_SESSION_DURATION<n> <unit> - minute/hour/day/week/month/year, singular or plural7 daysHow long a browser login stays valid (sliding - activity renews it), e.g. 12 hours, 2 weeks. Unrecognized values fall back to 7 days.
PANKHA_MAX_PENDING_AGENTSpositive integer20Max agents awaiting approval at once; raise it for a large first-time enrollment so the whole fleet pends in one screen instead of refilling in waves.
PANKHA_TRUST_PROXYcomma-separated IPs/CIDRsunset (no proxy trusted)Addresses of your reverse proxies, e.g. 192.168.1.5 or 10.0.0.0/8, 172.16.0.1. Forwarded-for headers are only believed when they arrive from these addresses, so the login rate limiter identifies the real client instead of the shared proxy IP. Leave unset for direct connections. Invalid values are ignored with a warning. See the reverse-proxy note below.
PANKHA_AUTH_RESETtrue / falseunset (off)Account recovery: when set to true, all user accounts are reset on startup and the dashboard returns to first-run setup. Remove the variable and restart once you are back in. Any value other than true, false, or unset refuses to start.
POSTGRES_HOSThostnamepankha-postgresDatabase host. The compose service name by default; change only for an external database (see below).
POSTGRES_PORTport number5432Database port.

PostgreSQL tuning (leave as-is unless you know why)

Section titled “PostgreSQL tuning (leave as-is unless you know why)”
VariableAccepted valuesDefaultDescription
POSTGRES_MAX_WAL_SIZEsize256MBCap on transaction log size before checkpoint.
POSTGRES_MIN_WAL_SIZEsize80MBMinimum WAL kept; old files recycle down to this.
POSTGRES_CHECKPOINT_TIMEOUTduration5minTime between checkpoints.
POSTGRES_WAL_KEEP_SIZEsize64MBWAL retained for recovery purposes.

The shipped compose.yml runs two containers - the app and PostgreSQL:

services:
pankha-app:
container_name: pankha_app
image: anexgohan/pankha:latest # :latest=(stable) | :beta=(absolute latest) | :testing=(pre-release)
ports:
- "${PANKHA_PORT:-3143}:3143"
environment:
- TZ=${TIMEZONE:-UTC}
- PORT=3143 # Internal port, do not change
env_file:
- .env
depends_on:
pankha-postgres:
condition: service_healthy
volumes:
- ./docker-data/staging:${PANKHA_STAGING_DIR}
restart: unless-stopped
pankha-postgres:
container_name: pankha_postgres
image: postgres:18-alpine
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
env_file:
- .env
volumes:
- ./docker-data/backend/database/postgres_data:/var/lib/postgresql
restart: unless-stopped

The database port is intentionally not published to the host; the app reaches it over the internal Docker network. Uncomment the ports: block in the shipped file only if external database access is needed.


The same stack as plain docker run commands:

Terminal window
docker network create pankha-net
docker run -d --name pankha_postgres \
--network pankha-net \
-e POSTGRES_DB=db_pankha \
-e POSTGRES_USER=<your-user> \
-e POSTGRES_PASSWORD=<your-password> \
-v "$(pwd)/docker-data/backend/database/postgres_data:/var/lib/postgresql" \
--restart unless-stopped \
postgres:18-alpine
docker run -d --name pankha_app \
--network pankha-net \
-p 3143:3143 \
-e PORT=3143 \
-e TZ=UTC \
-e POSTGRES_HOST=pankha_postgres \
-e POSTGRES_PORT=5432 \
-e POSTGRES_DB=db_pankha \
-e POSTGRES_USER=<your-user> \
-e POSTGRES_PASSWORD=<your-password> \
-e PANKHA_HUB_IP=<your-lan-ip> \
-e PANKHA_STAGING_DIR=/app/backend/data/staging \
-v "$(pwd)/docker-data/staging:/app/backend/data/staging" \
--restart unless-stopped \
anexgohan/pankha:latest

Dashboard unreachable / port already in use - another service owns the port. Change PANKHA_PORT in .env and re-run docker compose up -d:

$ docker compose up -d
Error response from daemon: failed to bind host port 0.0.0.0:3143: address already in use

App container restarts, database connection refused - POSTGRES_HOST must match the database container’s name on the shared network (pankha-postgres in compose, pankha_postgres with plain docker run):

$ docker logs pankha_app
Error: Database connection requires either POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB or DATABASE_URL

Agents cannot connect after install - PANKHA_HUB_IP was unset or not reachable from the agent’s network when the install script was generated. Set it to the server’s LAN IP and generate a fresh install script from the Deployment page.

Locked out of the dashboard - set PANKHA_AUTH_RESET=true in .env, restart the app container, complete first-run setup again, then remove the variable and restart once more.

Times are wrong in the dashboard or logs - set TIMEZONE in .env to your tz database name (e.g. Asia/Kolkata) and restart.


To use an existing PostgreSQL server instead of the bundled container: remove (or don’t start) the pankha-postgres service, and point the app at your server in .env:

Terminal window
POSTGRES_HOST="db.example.lan"
POSTGRES_PORT="5432"
POSTGRES_DB="db_pankha"
POSTGRES_USER="<your-user>"
POSTGRES_PASSWORD="<your-password>"

The backend builds its connection string from these variables with proper URL-encoding, so special characters in passwords are safe. (A raw DATABASE_URL is accepted as a fallback only when the individual variables are absent, and you must pre-encode special characters yourself - prefer the variables above.) The schema is created automatically on first start.

Terminal window
# Upgrade to the newest image on your chosen tag
docker compose down && docker compose pull && docker compose up -d

Image tags: latest (stable), beta (absolute latest), testing (pre-releases). To pin or roll back, set an explicit version in compose.yml and recreate:

image: anexgohan/pankha:v0.6.2
Terminal window
docker compose up -d

Your data lives in ./docker-data/ on the host, outside the containers, so recreating containers does not touch it.

Pankha serves HTTP and WebSocket on the same port. If you front it with nginx, Caddy, or Traefik, the proxy must forward WebSocket upgrade headers or the dashboard’s live updates and agent connections will fail. For nginx:

location / {
proxy_pass http://127.0.0.1:3143;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Also set PANKHA_TRUST_PROXY to your proxy’s address (see the variable table) so login rate limiting sees real client addresses instead of the proxy’s.