Files
monlet/deploy/docker-compose.yml

123 lines
3.7 KiB
YAML

services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: monlet
POSTGRES_PASSWORD: monlet
POSTGRES_DB: monlet
POSTGRES_INITDB_ARGS: "--auth-local=scram-sha-256 --auth-host=scram-sha-256"
TZ: UTC
healthcheck:
test: ["CMD-SHELL", "pg_isready -U monlet -d monlet"]
interval: 5s
timeout: 3s
retries: 20
volumes:
- monlet_pg:/var/lib/postgresql/data
ports:
- "${MONLET_POSTGRES_HOST_PORT:-5432}:5432"
server:
build:
context: ../server
depends_on:
postgres:
condition: service_healthy
environment:
MONLET_AUTH_TOKEN: ${MONLET_AUTH_TOKEN:?MONLET_AUTH_TOKEN is required}
MONLET_DATABASE_URL: postgresql+asyncpg://monlet:monlet@postgres:5432/monlet
MONLET_LOG_LEVEL: ${MONLET_LOG_LEVEL:-INFO}
MONLET_ENABLE_DETECTOR: "true"
MONLET_ENABLE_NOTIFIER_WORKER: "true"
MONLET_NOTIFICATION_TIME_ZONE: ${MONLET_NOTIFICATION_TIME_ZONE:-UTC}
MONLET_NOTIFIER_DEBUG_ENABLED: "true"
TZ: UTC
command:
- sh
- -c
- "alembic upgrade head && uvicorn monlet_server.main:app --host 0.0.0.0 --port 8000"
ports:
- "${MONLET_SERVER_HOST_PORT:-8000}:8000"
healthcheck:
test: ["CMD-SHELL", "python -c 'import urllib.request,sys;sys.exit(0) if urllib.request.urlopen(\"http://localhost:8000/api/v1/ready\").status==200 else sys.exit(1)'"]
interval: 5s
timeout: 3s
retries: 30
# PH-015: production hardening for runtime containers.
read_only: true
tmpfs:
- /tmp:size=64M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
stop_grace_period: 15s
web:
build:
context: ../web
depends_on:
server:
condition: service_healthy
environment:
MONLET_API_BASE_URL: http://server:8000
MONLET_API_TOKEN: ${MONLET_AUTH_TOKEN:?MONLET_AUTH_TOKEN is required}
MONLET_WEB_TIME_ZONE: ${MONLET_WEB_TIME_ZONE:-UTC}
MONLET_WEB_AUTH_USERNAME: ${MONLET_WEB_AUTH_USERNAME:-}
MONLET_WEB_AUTH_PASSWORD: ${MONLET_WEB_AUTH_PASSWORD:-}
MONLET_WEB_SESSION_SECRET: ${MONLET_WEB_SESSION_SECRET:-}
MONLET_WEB_SESSION_TTL_SEC: ${MONLET_WEB_SESSION_TTL_SEC:-604800}
MONLET_WEB_TRUST_PROXY_AUTH: ${MONLET_WEB_TRUST_PROXY_AUTH:-false}
TZ: ${MONLET_WEB_TIME_ZONE:-UTC}
ports:
- "${MONLET_WEB_HOST_PORT:-3000}:3000"
# PH-015: production hardening for the Next.js runtime. Next writes a small
# cache under .next/cache so we keep that path writable via tmpfs.
read_only: true
tmpfs:
- /tmp:size=64M
- /app/.next/cache:size=64M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
healthcheck:
test: ["CMD-SHELL", "wget -q -O- http://localhost:3000/api/health >/dev/null 2>&1 || exit 1"]
interval: 10s
timeout: 10s
retries: 10
start_period: 30s
stop_grace_period: 15s
prometheus:
image: prom/prometheus:v2.55.0
profiles: ["observability"]
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
ports:
- "9090:9090"
depends_on:
- server
alertmanager:
image: prom/alertmanager:v0.27.0
profiles: ["observability"]
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
ports:
- "9093:9093"
grafana:
image: grafana/grafana:11.3.0
profiles: ["observability"]
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GF_ADMIN_PASSWORD:-admin}
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
ports:
- "3001:3000"
volumes:
monlet_pg: