Skip to content

Compose Profiles

FreeSDN uses Docker Compose profiles to gate optional services. The core stack (Postgres, logdb, Valkey, API, worker, scheduler, pg-backup, Caddy edge) runs at every tier without any profile. Profiles add capabilities on top.

Set profiles in your env file as a comma-separated list:

Terminal window
# In .env.pro
COMPOSE_PROFILES=io-worker,monitoring

Or override from the command line for a one-off run:

Terminal window
docker compose --env-file .env.pro --profile cameras up -d go2rtc

Adds: worker-io

Queues handled: discovery, sync, metrics

This worker handles vendor API calls: device discovery, controller sync, and metrics collection. These tasks are slow (network I/O bound) and can block the default worker if they share a queue. The io-worker profile isolates them so adapter timeouts do not starve quick tasks like alert notifications and heartbeats.

When to use: Pro and Max tiers. If you manage more than ~20 devices or run scheduled discovery, enable this profile.

Terminal window
COMPOSE_PROFILES=io-worker,...
WORKER_IO_QUEUES=discovery,sync,metrics
CELERY_IO_CONCURRENCY=2 # raise to 4 on Max if you have many controllers
WORKER_IO_MEM_LIMIT=1G

Adds: flower

Port: Internal only (not published to the host in production)

Flower is the Celery task monitoring UI. It shows worker status, task history, queue depths, and allows manual task revocation. In production, Flower is internal-only - access it via a tunnel or the admin reverse proxy.

When to use: Pro and Max tiers, or whenever you want visibility into Celery.

Terminal window
COMPOSE_PROFILES=...,monitoring
FLOWER_BASIC_AUTH=admin:__CHANGE_ME__

Access Flower by proxying into the container:

Terminal window
# Flower is bridge-internal - no host port is published. Use an SSH tunnel:
ssh -L 5555:localhost:5555 user@host
# Or exec into the worker and inspect tasks directly:
docker compose --env-file .env.pro exec flower celery -A app.core.celery_app inspect active

Adds: go2rtc

go2rtc provides RTSP-to-HLS/WebRTC transcoding for the Video Surveillance module. Without this profile, camera live streams in the UI may not work (depending on the browser and stream format).

When to use: Any tier where you use the Video Surveillance module with live streaming.

Terminal window
COMPOSE_PROFILES=...,cameras

collector - SNMP trap / Syslog / NetFlow listener

Section titled “collector - SNMP trap / Syslog / NetFlow listener”

Adds: collector

Publishes: UDP 514 (syslog), 162 (SNMP traps), 2055 (NetFlow) on the host

The Observability module’s UDP receivers also load inside the api process, but that path cannot reliably accept external UDP: under multi-worker Gunicorn only one worker can bind a given UDP port, and the api container is non-root and read_only with no cap_add, so it cannot bind the privileged ports 514 and 162 at all. The collector profile starts one dedicated, non-root process (python -m app.modules.collector.run) that binds the receivers deterministically and publishes them to the host.

It needs neither root nor a Linux capability: the service sets the namespaced sysctl net.ipv4.ip_unprivileged_port_start=0, which lets the unprivileged user bind 514/162 inside that container’s network namespace only.

When to use: any tier where devices send SNMP traps, syslog, or NetFlow to FreeSDN. Without this profile, the Observability module still stores and searches data, but nothing external can reach the listeners.

Terminal window
COMPOSE_PROFILES=...,collector
# Optional - these set the PUBLISHED host mapping (host and container side
# alike). Compose does not pass them into the container, so moving a listener
# also means setting the matching per-org port in the collector config.
# Defaults shown.
COLLECTOR_SYSLOG_PORT=514
COLLECTOR_SNMP_PORT=162
COLLECTOR_NETFLOW_PORT=2055

Or as a one-off run:

Terminal window
docker compose --env-file .env.pro --profile collector up -d

See Observability for enabling each receiver and setting the source-IP allow-list.


Adds: pgbouncer (for the primary DB) and pgbouncer-logdb (for TimescaleDB)

PgBouncer sits between the application containers and PostgreSQL and multiplexes many application connections onto a smaller number of actual DB connections. This prevents “too many clients” errors when Gunicorn workers, Celery workers, and scheduled tasks all hold connection pools simultaneously.

When to use: Max tier, or Pro tier once you exceed ~50 managed devices or start seeing TooManyConnectionsError in API logs.

Terminal window
COMPOSE_PROFILES=...,pooling
# After enabling, point the application at PgBouncer instead of Postgres:
DB_HOST=pgbouncer
DB_PORT=6432
LOGDB_HOST=pgbouncer-logdb
LOGDB_PORT=6432

Adds: pg-backup-offsite (rclone-based sync sidecar)

The dr profile runs an rclone process that copies the locally-encrypted GPG dumps from the pg_backups volume to your configured off-site destination (S3, Backblaze B2, Azure Blob, GCS, SFTP, WebDAV, and 60+ other providers). Off-site retention defaults to 30 days and runs independently of local 7-day retention.

When to use: Max tier. Strongly recommended for any production deployment where data loss would have business impact.

Terminal window
COMPOSE_PROFILES=...,dr
BACKUP_OFFSITE_REMOTE=backupremote # name of the remote in rclone.conf
BACKUP_OFFSITE_PATH=freesdn-backups # path/bucket on the remote

One-time setup:

Terminal window
# Generate rclone config on any machine with rclone installed:
rclone config
# Save the resulting config as ./secrets/rclone.conf in the repo

See Backups and Restore for the full backup strategy, GPG key setup, and restore procedure.


Adds: prometheus, redis-exporter, postgres-exporter, logdb-exporter

This profile starts a Prometheus instance configured to scrape the FreeSDN API’s /metrics endpoint (auth token required) and the three infrastructure exporters. The Prometheus scrape config is loaded from docker/prometheus/prometheus.yml; alert rules are loaded from observability/prometheus/alerts.yml.

When to use: Pro and Max tiers when you want integrated Prometheus metrics. For large deployments with an existing Prometheus infrastructure, scrape the /metrics endpoint directly instead.

Terminal window
COMPOSE_PROFILES=...,metrics
METRICS_AUTH_TOKEN=<openssl rand -hex 32> # required; see Configuration

See Monitoring for the key custom metrics and recommended Grafana dashboards.


Adds: edge-nginx as an alternative edge proxy

Use this instead of the default Caddy edge when your environment standardizes on nginx, or when TLS is terminated at an upstream hardware/cloud load balancer. See Networking and TLS for the detailed usage pattern.

Terminal window
# Use nginx edge, suppress Caddy
COMPOSE_PROFILES=nginx,...
# Then start with --scale edge=0 to prevent Caddy from starting
docker compose --env-file .env.pro up -d --scale edge=0

Adds: vpn, a root container with cap_add: [NET_ADMIN] and /dev/net/tun

FreeSDN runs capless by default: no privileged container, and the api is an ordinary bridge service. The vpn profile starts a single sidecar that concentrates all the elevated network privilege (root, NET_ADMIN, /dev/net/tun, net.ipv4.ip_forward=1) in one place and runs the tunnel daemons there. It reuses the backend image with a VPN-only entrypoint.

The profile alone is not enough. It starts the sidecar; the docker-compose.vpn.yml overlay is what rewires the rest, setting network_mode: service:vpn and VPN_MODE=sidecar on the api, adding the shared control volumes, and repointing the Caddy edge at vpn:8000. Enable both together:

Terminal window
COMPOSE_PROFILES=vpn \
docker compose --env-file .env.pro \
-f docker-compose.yml -f docker-compose.vpn.yml up -d

When to use: only when FreeSDN itself must route into remote-site overlay tunnels. Leave it off otherwise - the default capless topology is the intended one.

Tier Typical COMPOSE_PROFILES value
Lite (empty - core services only)
Pro io-worker,monitoring
Pro + cameras io-worker,monitoring,cameras
Pro + pooling io-worker,monitoring,pooling
Pro + observability ingest io-worker,monitoring,collector
Max io-worker,monitoring,pooling,dr
Max + metrics io-worker,monitoring,pooling,dr,metrics
Max + cameras io-worker,monitoring,pooling,dr,cameras

The vpn profile is deliberately absent from these combinations: it also requires the docker-compose.vpn.yml overlay on the command line, so it is not a drop-in addition to COMPOSE_PROFILES.

All product names, logos, and brands are property of their respective owners. FreeSDN is an independent project and is not affiliated with or endorsed by the vendors it integrates with. See Trademarks.