super-productivity/packages/super-sync-server/docker-compose.monitoring.yml
johannesjo 25d637e917 chore(supersync): harden compose for 4GB VPS production
Right-size container limits to fit a 4GB / 2-vCore VPS host without
OOM risk, and apply minimal best-practice hardening informed by
multi-agent review.

Container memory budget (sum 2816 MB, leaves ~1.2 GB host headroom):
- postgres: 1g → 1.5g (was at 74% under 199 WSS connections)
- supersync: 1g → 768m (uses 143 MiB steady state)
- dozzle: add 64m cap (was unbounded)
- uptime-kuma: add 192m cap (was unbounded)
- caddy: unchanged at 256m

Postgres tuning (1.5g cap, shm_size 256m to allow shared_buffers > 64m):
- shared_buffers=384MB, effective_cache_size=1GB
- work_mem=4MB, maintenance_work_mem=128MB
- max_connections=40 (Prisma pool is single-digit; 40 leaves headroom
  for migrations + psql; 100 was an OOM landmine vs 1.5g cap)
- random_page_cost=1.1 (SSD)
- idle_in_transaction_session_timeout=300000 (5 min — catches leaked
  Prisma idle-in-tx but stays well above migrate-deploy.sh
  STEP_TIMEOUT=1800 for CONCURRENTLY index recovery)
- wal_compression=on, huge_pages=off

Other hardening:
- Standardize restart policy to unless-stopped across all services
- Add caddy healthcheck via admin API at localhost:2019; Caddyfile
  comment warns future edits not to disable admin
- Add ulimits.nofile=65535 to supersync (Alpine soft default 1024 is
  tight for 200+ WSS + reconnect bursts)
- Pin networks.internal.name=super-sync-server_internal so the
  monitoring overlay can attach via external: true regardless of
  COMPOSE_PROJECT_NAME
- Attach dozzle + uptime-kuma to internal network so Kuma can probe
  http://supersync:1900/health by service name

Notes for future maintenance:
- oom_score_adj was tried and removed: Compose v2 silently ignores it
  (verified on host via /proc/<pid>/oom_score_adj returning 0).
  Would need a systemd post-up hack to apply — not worth it.
- The reconnect storm that motivated this work is fixed separately
  on master in b404bf8a3.
2026-05-16 12:34:13 +02:00

57 lines
1.6 KiB
YAML

# Monitoring stack for SuperSync Server
#
# Attaches to the external `super-sync-server_internal` network created by
# the base docker-compose.yml — the base stack must be brought up first
# (or merged into the same `up` invocation as below). Running this file
# standalone will fail with "network not found".
#
# Usage:
# docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d
#
# Access via SSH tunnel (from local machine):
# ssh -L 8080:localhost:8080 -L 3001:localhost:3001 yourserver
#
# Then open:
# - Dozzle (logs): http://localhost:8080
# - Uptime Kuma (uptime): http://localhost:3001
services:
# Real-time log viewer (~40MB RAM)
# Read-only Docker socket access for viewing container logs
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: unless-stopped
ports:
- '127.0.0.1:8080:8080'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- DOZZLE_NO_ANALYTICS=true
networks:
- internal
mem_limit: 64m
# Uptime monitoring + alerts (~120MB RAM)
# Provides health check monitoring and notifications
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- '127.0.0.1:3001:3001'
volumes:
- uptime-kuma-data:/app/data
networks:
- internal
mem_limit: 192m
# Attach to the internal network defined in docker-compose.yml so Kuma can
# probe http://supersync:1900/health by service name (not just public URL).
networks:
internal:
external: true
name: super-sync-server_internal
volumes:
uptime-kuma-data: