Skip to content

Deployment Tiers

FreeSDN uses a single docker-compose.yml base file for every environment. You choose a scale tier with an env file (--env-file .env.<tier>) and opt into optional capabilities via COMPOSE_PROFILES. There is no separate production compose file - the same base runs everything.

Axis What it controls How you set it
Tier (Lite / Pro / Max / Dev) Worker count, concurrency, resource limits --env-file .env.<tier>
Features (cameras, pooling, DR, …) Optional capabilities at any tier COMPOSE_PROFILES= in the env file

The tiers run the same code and images with different worker counts and resource limits. A larger tier is a sizing preset, not a production-readiness certification; HA, capacity, recovery and supported controller versions still need qualification. Cameras, connection pooling, and off-site DR are features you enable at any tier - a Lite homelab can run cameras; a Max enterprise deployment can skip them.

Lite Pro Max Dev
Intended use Homelab / personal SMB, dozens-hundreds of devices Enterprise / multi-site Local development
API workers (WEB_CONCURRENCY) 1 2 4 1 (uvicorn --reload)
Celery worker queues All queues on one worker default,priority (quick lane) default,priority All queues
I/O worker (io-worker profile) No Yes Yes No
Flower (monitoring profile) No Yes Yes No
PgBouncer (pooling profile) No Optional Yes No
Off-site DR (dr profile) No Optional Yes No
HA overlay (docker-compose.ha.yml) No No Yes (opt-in) No
Resource limits Small Moderate Large Minimal
Terminal window
# Lite - homelab
docker compose --env-file .env.lite up -d
# Pro - SMB
docker compose --env-file .env.pro up -d
# Max - enterprise
docker compose --env-file .env.max up -d
# Max + HA overlay (Valkey Sentinel + Postgres standby)
docker compose --env-file .env.max -f docker-compose.yml -f docker-compose.ha.yml up -d
# Dev - local development with hot-reload
docker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev up -d

The one-command installer defaults to Lite; choose another tier explicitly. These commands are for a fresh isolated installation. Existing deployments must follow Upgrading before replacing images or attaching data volumes:

Terminal window
./install.sh # Lite by default
./install.sh --tier pro --domain sdn.example.com
Container Role
postgres Primary relational DB - 19 schemas, PostgreSQL 18
logdb TimescaleDB - metrics, syslog, NetFlow, health snapshots
redis Valkey 9.1.2 - cache, session store, Celery broker (DB 0/1/2)
api FastAPI + Gunicorn + Uvicorn workers
worker Celery worker - quick/priority lane (Lite: all queues)
scheduler Celery beat - scheduled tasks
pg-backup Daily GPG-encrypted dumps of both DBs
edge (or edge-nginx) Edge proxy - TLS termination, SPA serving, API reverse proxy

Defaults from the env example files:

Resource Lite Pro Max
POSTGRES_MEM_LIMIT 1 G 2 G 4 G
LOGDB_MEM_LIMIT 1 G 2 G 4 G
REDIS_MEM_LIMIT 256M 768 M 1 G
API_MEM_LIMIT 768 M 1 G 2 G
WORKER_MEM_LIMIT 768 M 1 G 2 G
WEB_CONCURRENCY 1 2 4
CELERY_CONCURRENCY 2 4 8

Valkey’s managed-memory budget is separate from its container limit: the Lite example selects docker/redis/redis-lite.conf (96 MiB) through REDIS_CONFIG_PATH; the default Pro/Max file uses 256 MiB. The data nodes do not use swap beyond their container budget. Native buffers and persistence forks need headroom; a successful startup is not a capacity test.

Existing installations: merging a new example does not change your saved env file. Add REDIS_CONFIG_PATH=./docker/redis/redis-lite.conf for Lite, or review and size your own custom file. The previous Lite default allowed 512 MiB inside a 256 MiB container; the previous Pro budget also failed persistence pressure without swap. Do not reduce a live non-expiring queue below its current usage: stop producers, drain/reconcile work, preserve backups, and plan the restart. Existing HA installations must additionally follow docker/redis/HA-UPGRADING; rewritten persisted configuration may retain older values. Verify CONFIG GET maxmemory on both data nodes after any change.

scripts/check_valkey_memory.py exercises disposable tier configurations with TTL eviction, a full non-expiring queue, concurrent persistence and container recreation. It checks zero OOM events and zero swap use. This is bounded qualification, not a fleet-capacity or multi-host recovery guarantee.

The default .env.pro sets COMPOSE_PROFILES=io-worker,monitoring, which splits Celery into two workers:

  • worker - handles default and priority queues (heartbeats, alert evaluation, notifications, backup orchestration)
  • worker-io - handles discovery, sync, and metrics queues (vendor Adapter calls, device scans)

This prevents a slow vendor API call from starving quick tasks such as alert notifications.

If your existing deploy uses -f docker-compose.prod.yml, it still works - that file is now a thin include: shim of the base. To adopt the tier model, copy the matching example env file and switch to --env-file .env.pro (or your tier). The base file defaults to production settings: ENVIRONMENT=production, Gunicorn, read-only rootfs, non-root users, internal-only data tier.

Next steps: Configuration - fill in your env file. Compose Profiles - full list of optional feature 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.