Deploy Watchflare Hub with Docker
Deploy the Watchflare Hub with Docker Compose. Includes the full compose file, secret generation, TimescaleDB setup, data volumes, and bind mount options.
The Hub is distributed as a single Docker image that embeds the web dashboard. It runs alongside a TimescaleDB container for persistent storage.
Tip
First time? The Quickstart gets you running in under 5 minutes.
Note
Prefer running without Docker? See Binary install (Linux) for a native systemd service setup.
Prerequisites
- Docker Engine 20.10+
- Docker Compose v2+
- Two open ports: 8080 (HTTP) and 50051 (gRPC)
1. Create the compose file
Save the following as docker-compose.yml in a dedicated directory:
services:
watchflare:
image: ghcr.io/watchflare-io/watchflare:latest
container_name: watchflare
ports:
- "${HUB_PORT:-8080}:8080"
- "${GRPC_PORT:-50051}:50051"
environment:
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_USER=${POSTGRES_USER:-watchflare}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
- POSTGRES_DB=${POSTGRES_DB:-watchflare}
- POSTGRES_SSLMODE=disable
- GRPC_PORT=${GRPC_PORT:-50051}
- JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env}
- NOTIFICATION_ENCRYPTION_KEY=${NOTIFICATION_ENCRYPTION_KEY:?Set NOTIFICATION_ENCRYPTION_KEY in .env}
- TLS_MODE=${TLS_MODE:-auto}
- TLS_PKI_DIR=/var/lib/watchflare/pki
- GRPC_TIMESTAMP_WINDOW=${GRPC_TIMESTAMP_WINDOW:-300}
- ENV=production
- COOKIE_SECURE=${COOKIE_SECURE:-}
- COOKIE_DOMAIN=${COOKIE_DOMAIN:-}
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-127.0.0.1,::1}
volumes:
- pki_data:/var/lib/watchflare/pki
depends_on:
postgres:
condition: service_healthy
networks:
- watchflare_net
restart: unless-stopped
postgres:
image: timescale/timescaledb:latest-pg16
container_name: watchflare-postgres
environment:
POSTGRES_USER: ${POSTGRES_USER:-watchflare}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
POSTGRES_DB: ${POSTGRES_DB:-watchflare}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-watchflare}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- watchflare_net
restart: unless-stopped
volumes:
pgdata:
driver: local
pki_data:
driver: local
networks:
watchflare_net:
driver: bridge 2. Create the .env file
Generate the three required secrets. NOTIFICATION_ENCRYPTION_KEY encrypts SMTP credentials and notification channel URLs at rest. Generate it now even if you don’t plan to use notifications yet, as the Compose file requires it to be set:
printf "POSTGRES_PASSWORD=%s\nJWT_SECRET=%s\nNOTIFICATION_ENCRYPTION_KEY=%s\n" \
"$(openssl rand -base64 32)" \
"$(openssl rand -base64 32)" \
"$(openssl rand -base64 32)" > .envNote
On macOS, use the Linux tab — openssl is available by default.
3. Start the stack
$ docker compose up -d [+] Running 3/3 ✔ Network watchflare_watchflare_net Created ✔ Container watchflare-postgres Started ✔ Container watchflare Started
Verify both containers are running:
docker compose ps The Hub will be available at http://your-host:8080.
Data persistence
The stack uses two named Docker volumes:
| Volume | Contents |
|---|---|
pgdata | PostgreSQL database (metrics, hosts, packages, users) |
pki_data | TLS CA and server certificate (auto-generated on first start) |
Named volumes are managed by Docker — no permission setup required. Data persists across container restarts and image upgrades.
Bind mounts
By default, both volumes are managed by Docker (pgdata and pki_data). If you want the data stored at a specific path on the host — for example to include it in a backup script, inspect files directly, or migrate to another machine — you can replace either or both with a bind mount pointing to a directory on the host filesystem.
Both volumes can be replaced independently or together.
PKI data (Hub — UID 65532)
The Hub container runs as UID 65532. The PKI directory must be owned by that user before the container starts:
mkdir -p /your/path/pki
sudo chown -R 65532:65532 /your/path/pki Then update docker-compose.yml:
# Replace:
- pki_data:/var/lib/watchflare/pki
# With:
- /your/path/pki:/var/lib/watchflare/pki Remove pki_data from the volumes: section at the bottom of the file if it is no longer used.
Database data (TimescaleDB — UID 70)
The TimescaleDB container runs PostgreSQL as UID 70. The data directory must be owned by that user:
mkdir -p /your/path/pgdata
sudo chown -R 70:70 /your/path/pgdata Then update docker-compose.yml:
# Replace:
- pgdata:/var/lib/postgresql/data
# With:
- /your/path/pgdata:/var/lib/postgresql/data Remove pgdata from the volumes: section at the bottom of the file if it is no longer used.
Warning
Never copy a PostgreSQL data directory while the container is running — the files will be in an inconsistent state. Use pg_dump for live backups, or stop the stack first with docker compose stop before copying.