Docker Deployment
Images
slozy-web (Multi-Stage)
docker build -t slozy-web:latest .- Builder stage:
golang:1.25.0-alpine, downloads modules, builds./cmd/slozy-webwithCGO_ENABLED=0 - Runtime stage:
alpine:3.19, non-rootslozyuser (UID 1000), exposes:8080,HEALTHCHECKat/health - Labels:
maintainer,version,org.opencontainers.image.source
slozy-ingestor
docker build -f Dockerfile.ingestor -t slozy-ingestor:latest .Same multi-stage pattern, builds ./cmd/slozy-ingestor. No HTTP server — runs the Prometheus metric ingestion loop.
Frontend
cd frontend && docker build -t slozy-frontend:latest .Builds with node:20-alpine, serves static files via nginx:alpine on port 80.
Docker Compose Files
There are two compose files for different environments:
docker-compose.yml (Development)
All-in-one stack for local development. Starts everything you need with one command:
| Service | Purpose | Port |
|---|---|---|
postgres | PostgreSQL 16, credentials from .env, /tmp locked with noexec | 127.0.0.1:5432 |
redis | Redis 7 with password auth from .env | 127.0.0.1:6379 |
prometheus | Metrics storage, 30d retention | 127.0.0.1:9090 |
grafana | Dashboards, password from .env | 127.0.0.1:3001 |
slozy-web | Main Go application, hot-reload via air | 127.0.0.1:8080 |
slozy-frontend | Vite + React dev server | 127.0.0.1:3000 |
Use case: local development, testing, demo. PostgreSQL and Redis run as containers — no external dependencies.
docker compose up -ddocker-compose.prod.yml (Production)
Minimal production stack. Does not include PostgreSQL or Redis — it assumes managed external services (e.g. RDS, ElastiCache) connected via .env.
| Service | Purpose | Port |
|---|---|---|
slozy-web | Go application, resource limits (2 CPU / 4 GB) | 127.0.0.1:8080,9090 |
slozy-ingestor | Metric ingestion worker, resource limits (1 CPU / 2 GB) | none |
nginx | Reverse proxy, SSL termination on 80/443 | 80, 443 |
Use case: production deployments where PostgreSQL and Redis are managed externally for reliability and backup.
docker compose -f docker-compose.prod.yml up -dWhy two files?
One docker-compose.yml is not enough because:
- Dev needs everything local — PostgreSQL, Redis, Prometheus, Grafana, frontend as containers for zero-setup development
- Prod needs external DBs — managed PostgreSQL (RDS, CloudSQL) and Redis (ElastiCache) provide backups, failover, and maintenance that containerized databases cannot
- Different security posture — prod binds all ports to
127.0.0.1, adds resource limits, uses separate ingestor worker, and a reverse proxy for SSL - Different build — dev builds from source with hot-reload, prod uses pre-built images with
restart: unless-stopped
Security hardening (both files)
All compose files apply these protections:
security_opt: no-new-privileges:true— prevents privilege escalationtmpfs: /tmp:noexec,nosuid,size=100M— prevents executing malware from/tmp- Ports bound to
127.0.0.1— not exposed to the network - Credentials from
.env— never hardcoded in compose files
.env configuration
All secrets are injected via .env file. See .env.example for the full list.
cp .env.example .env
# Then edit .env with your valuesAlso see deploy/production/docker-compose.prod.yml and deploy/production/cloud-init.yaml for cloud provisioning.