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.
Two independent axes
Section titled “Two independent axes”| 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.
Tier summary
Section titled “Tier summary”| 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 |
Up commands
Section titled “Up commands”# Lite - homelabdocker compose --env-file .env.lite up -d
# Pro - SMBdocker compose --env-file .env.pro up -d
# Max - enterprisedocker 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-reloaddocker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev up -dThe 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:
./install.sh # Lite by default./install.sh --tier pro --domain sdn.example.comCore always-on services
Section titled “Core always-on services”| 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 |
Tier resource sizing
Section titled “Tier resource sizing”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.
Pro tier: split worker queues
Section titled “Pro tier: split worker queues”The default .env.pro sets COMPOSE_PROFILES=io-worker,monitoring, which splits Celery into two workers:
- worker - handles
defaultandpriorityqueues (heartbeats, alert evaluation, notifications, backup orchestration) - worker-io - handles
discovery,sync, andmetricsqueues (vendor Adapter calls, device scans)
This prevents a slow vendor API call from starving quick tasks such as alert notifications.
Migrating from docker-compose.prod.yml
Section titled “Migrating from docker-compose.prod.yml”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.