Docker Service Management
Caroline runs 37 containers organized across 5 compose stacks. Every container uses restart: unless-stopped. CPU and memory limits are enforced via docker-compose.pi.yml overrides on every container.
Stack Layout
Section titled “Stack Layout”| Compose file | Containers | Purpose |
|---|---|---|
docker-compose.yml + docker-compose.pi.yml | postgres, n8n, caddy, authelia, redis, cloudflared, waha, mcp-auth-, mcp-proxy- | Core foundation |
docker-compose.dashboard.yml | dashboard-api, dashboard-nginx, docker-socket-proxy | Web dashboard |
grafana/docker-compose.grafana.yml | grafana, loki, prometheus, tempo, alloy, node-exporter, cadvisor, postgres-exporter, unpoller, blackbox-exporter, otel-collector, docker-socket-proxy-grafana | Observability |
pi/docker-compose.ha.yml | homeassistant, wyoming-whisper, wyoming-piper, wyoming-openwakeword, esphome | Home automation and voice |
docker-compose.discord.yml | discord-bot | Discord bot |
The Pi compose file (docker-compose.pi.yml) is an overlay that adds memory and CPU limits for the ARM environment. It is always passed with the core stack: docker compose -f docker-compose.yml -f docker-compose.pi.yml.
The Makefile abstracts these compose file combinations. Use Make targets rather than raw compose commands to avoid applying the wrong file set.
Resource Limits
Section titled “Resource Limits”All containers on Caroline have explicit limits. Representative examples:
| Service | Memory limit | CPU limit |
|---|---|---|
| postgres | 6 GB | 2.0 |
| n8n | 2560 MB | 1.0 |
| homeassistant | 1536 MB | 2.0 |
| mcp-proxy-memory | 1 GB | 0.5 |
| grafana | 1 GB | 1.0 |
| prometheus | 2 GB | 1.0 |
| dashboard-api | 512 MB | 0.5 |
| caddy | 512 MB | 0.5 |
| redis | 256 MB | 0.25 |
| blackbox-exporter | 64 MB | 0.1 |
Limits are not optional. Caroline has 16 GB RAM total. Without limits, a single runaway container can OOM the entire host.
Common Operations
Section titled “Common Operations”Check Status
Section titled “Check Status”make pi-status # All containers on Carolinemake status # Local Mac containersView Logs
Section titled “View Logs”make pi-logs # All containers (tail -f)make logs # Local Macmake logs-n8n # n8n only (local)make logs-postgres # Postgres only (local)make logs-caddy # Caddy only (local)make logs-mcp # MCP proxy + auth + tunnel (local)For a specific container on Caroline:
make pi-shelldocker logs chris-os-n8n --tail 100 -fRestart Services
Section titled “Restart Services”make pi-restart # All services on Carolinemake ha-restart # Home Assistant onlymake ha-up / ha-down # HA start/stopmake voice-up / voice-down # Voice pipelinemake dashboard-pi-up / dashboard-pi-downmake grafana-up / grafana-downRebuild a Service (Dashboard)
Section titled “Rebuild a Service (Dashboard)”The dashboard is built from source, not pulled from a registry. Use the 5-step build:
make dashboard-pi-buildThis: generates commit-cache.json from git log, rsyncs dashboard/ to Caroline, rsyncs ha-config/ to Caroline, runs docker build --no-cache, runs docker compose up -d, and verifies the version after deploy.
Check Container Health
Section titled “Check Container Health”make pi-shelldocker ps --format "table {{.Names}}\t{{.Status}}\t{{.RunningFor}}"A container in Up X minutes (healthy) is passing its healthcheck. Up X minutes (unhealthy) means the healthcheck is failing. Check logs immediately.
Reload Caddy Config
Section titled “Reload Caddy Config”make caddy-reload # Reload Caddyfile without restart (local)make pi-shelldocker exec chris-os-caddy caddy reload --config /etc/caddy/CaddyfileNetwork Topology
Section titled “Network Topology”Five isolated bridge networks separate service tiers:
| Network | What’s on it |
|---|---|
| net-data | postgres, redis, n8n, authelia, mcp-proxy-{postgres,memory}, grafana, dashboard-api |
| net-app | n8n, authelia, waha, caddy, mcp-proxy-n8n, discord-bot |
| net-mcp | caddy, cloudflared, all mcp-proxy and mcp-auth containers |
| net-frontend | caddy, dashboard-{api,nginx}, grafana, prometheus |
| net-monitoring | grafana, loki, prometheus, tempo, alloy, all exporters |
Home Assistant and ESPHome use network_mode: host for mDNS and Bluetooth. The voice containers (wyoming-*) use the pi_default bridge network with published ports to loopback, reachable by the host-networked HA via localhost.
Image Policy
Section titled “Image Policy”Pinned versions on all images — no :latest tags except where arm64 compatibility requires it. When updating an image version:
- Update the version in the compose file.
- Test locally if possible.
- Push to main; the deploy pipeline pulls and recreates the container.
GitOps convergence also detects image drift for pulled-image services and auto-recreates containers running stale versions.
Pulled-image services (auto-recreated by GitOps on drift): postgres, redis, authelia, cloudflared, waha, n8n, caddy, grafana, loki, prometheus.
Build-from-source services (reported but not auto-recreated): mcp-auth-, mcp-proxy-, dashboard-api, dashboard-nginx.