Deploy with Docker
Deploy the Watchflare Hub using Docker Compose.
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.
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}
- SMTP_ENCRYPTION_KEY=${SMTP_ENCRYPTION_KEY:?Set SMTP_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. SMTP_ENCRYPTION_KEY encrypts SMTP credentials at rest — generate it now even if you don’t plan to use email notifications, as the Compose file requires it to be set:
printf "POSTGRES_PASSWORD=%s\nJWT_SECRET=%s\nSMTP_ENCRYPTION_KEY=%s\n" \
"$(openssl rand -hex 32)" \
"$(openssl rand -hex 32)" \
"$(openssl rand -hex 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.