W Watchflare docs

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:

docker-compose.yml yaml
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)" > .env

Note

On macOS, use the Linux tab — openssl is available by default.


3. Start the stack

bash
$ docker compose up -d
[+] Running 3/3
✔ Network watchflare_watchflare_net  Created
✔ Container watchflare-postgres      Started
✔ Container watchflare               Started

Verify both containers are running:

bash
docker compose ps

The Hub will be available at http://your-host:8080.


Data persistence

The stack uses two named Docker volumes:

VolumeContents
pgdataPostgreSQL database (metrics, hosts, packages, users)
pki_dataTLS 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:

bash
mkdir -p /your/path/pki
sudo chown -R 65532:65532 /your/path/pki

Then update docker-compose.yml:

yaml
# 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:

bash
mkdir -p /your/path/pgdata
sudo chown -R 70:70 /your/path/pgdata

Then update docker-compose.yml:

yaml
# 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.


Next steps