super-productivity/packages/super-sync-server/docker-compose.yml
felix bear d82754faf2
feat(supersync): configure server bind host #7301 (#8108)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 12:12:49 +02:00

186 lines
5.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Production docker-compose for super-sync-server
#
# Usage:
# 1. Copy env.example to .env and configure your settings
# 2. Run: ./scripts/deploy.sh
# 3. View logs: docker-compose logs -f
#
# For local builds (development):
# ./scripts/deploy.sh --build
x-logging: &default-logging
driver: 'json-file'
options:
max-size: '10m'
max-file: '3'
services:
supersync:
image: ${SUPERSYNC_IMAGE:-ghcr.io/super-productivity/supersync:latest}
container_name: supersync-server
restart: unless-stopped
environment:
- NODE_ENV=${NODE_ENV:-production}
- PORT=1900
- HOST=${HOST:-0.0.0.0}
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-supersync}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-supersync}}
- RUN_MIGRATIONS_ON_STARTUP=${RUN_MIGRATIONS_ON_STARTUP:-false}
- JWT_SECRET=${JWT_SECRET}
- PUBLIC_URL=${PUBLIC_URL:-http://localhost:1900}
- CORS_ORIGINS=${CORS_ORIGINS:-https://app.super-productivity.com}
- SMTP_HOST=${SMTP_HOST:-}
- SMTP_PORT=${SMTP_PORT:-587}
- SMTP_SECURE=${SMTP_SECURE:-false}
- SMTP_USER=${SMTP_USER:-}
- SMTP_PASS=${SMTP_PASS:-}
- SMTP_FROM=${SMTP_FROM:-}
- WEBAUTHN_RP_ID=${WEBAUTHN_RP_ID:-localhost}
- WEBAUTHN_RP_NAME=${WEBAUTHN_RP_NAME:-Super Productivity Sync}
- WEBAUTHN_ORIGIN=${WEBAUTHN_ORIGIN:-http://localhost:1900}
- OLD_OPS_CLEANUP_DELETE_BATCH_SIZE=${OLD_OPS_CLEANUP_DELETE_BATCH_SIZE:-5000}
- OLD_OPS_CLEANUP_MAX_DELETED_PER_RUN=${OLD_OPS_CLEANUP_MAX_DELETED_PER_RUN:-25000}
- SUPERSYNC_BATCH_UPLOAD=${SUPERSYNC_BATCH_UPLOAD:-false}
- SUPERSYNC_PAYLOAD_BYTES_BACKFILL_COMPLETE=${SUPERSYNC_PAYLOAD_BYTES_BACKFILL_COMPLETE:-false}
ports:
- '127.0.0.1:1900:1900'
volumes:
- supersync-data:/app/data
depends_on:
postgres:
condition: service_healthy
healthcheck:
test:
[
'CMD',
'wget',
'--no-verbose',
'--tries=1',
'--spider',
'http://localhost:1900/health',
]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
# 200+ WSS clients each consume an fd; Alpine default soft limit (1024)
# would be tight under reconnect bursts.
ulimits:
nofile:
soft: 65535
hard: 65535
mem_limit: 768m
cpus: '1.0'
networks:
- internal
logging: *default-logging
postgres:
image: postgres:16-alpine
container_name: supersync-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER:-supersync}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB:-supersync}
# Tuned for the 1.5g mem_limit. shared_buffers needs shm_size > default 64m.
# 120 × 4MB work_mem = 480MB worst-case sort budget + 384MB shared_buffers,
# well inside the 1.5g cap. Prisma's DEFAULT pool is ~(host_cpu_count × 2),
# read from host cores NOT the container cpu limit — it is NOT single-digit
# on this VPS and will silently consume the entire cap. The app MUST bound
# it via DATABASE_URL ?connection_limit=N (currently 60). max_connections
# stays well above N to reserve slots for migrations, psql, the old-ops
# cleanup job, and the reconnect stampede after an in-place crash recovery.
command:
- postgres
- -c
- shared_buffers=384MB
- -c
- effective_cache_size=1GB
- -c
- work_mem=4MB
- -c
- maintenance_work_mem=128MB
- -c
- max_connections=120
- -c
- random_page_cost=1.1
- -c
# 5 min — kill leaked Prisma idle-in-tx, but well above the
# migrate-deploy.sh 30-min STEP_TIMEOUT for CONCURRENTLY index builds.
- idle_in_transaction_session_timeout=300000
- -c
- wal_compression=on
- -c
- huge_pages=off
shm_size: 256m
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- >
pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" &&
psql -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -c "SELECT 1" > /dev/null 2>&1
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
security_opt:
- no-new-privileges:true
mem_limit: 1536m
networks:
internal:
aliases:
# Keep existing .env files with DATABASE_URL=...@db:5432/... working.
- db
logging: *default-logging
caddy:
image: caddy:2.11-alpine
container_name: supersync-caddy
restart: unless-stopped
ports:
- '80:80'
- '443:443'
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
environment:
- DOMAIN=${DOMAIN}
healthcheck:
# Use 127.0.0.1, not localhost: Caddy binds the admin API to IPv4
# 127.0.0.1:2019, but busybox wget resolves `localhost` to ::1 first
# and gets "connection refused", marking a healthy Caddy unhealthy.
test: ['CMD', 'wget', '-q', '--spider', 'http://127.0.0.1:2019/config/']
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- internal
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
mem_limit: 256m
depends_on:
- supersync
logging: *default-logging
volumes:
supersync-data:
postgres-data:
caddy-data:
caddy-config:
networks:
internal:
# Pinned name so docker-compose.monitoring.yml can attach via
# `external: true` regardless of the COMPOSE_PROJECT_NAME used.
name: super-sync-server_internal
driver: bridge