# ============================================================================= # Betreuungs-Cockpit - Produktivbetrieb # ============================================================================= # # docker compose up -d --build # # Kein Reverse Proxy in dieser Datei: der ist bereits vorhanden. Die # Anwendung beendet selbst kein TLS und horcht standardmaessig nur auf # 127.0.0.1:3000. # # Liegt der Proxy auf einem anderen Server, setze APP_BIND in der .env auf # die Adresse, ueber die er herankommt - siehe docs/INSTALLATION.md, # Schritt 7. # # Die Datenbank hat bewusst KEINE Portfreigabe. Sie ist nur ueber das # interne Docker-Netz erreichbar. Wer mit psql heran muss, geht ueber # docker compose exec db psql -U cockpit betreuungs_cockpit # ============================================================================= name: betreuungs-cockpit services: db: image: postgres:17-alpine restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-cockpit} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt in der .env} POSTGRES_DB: ${POSTGRES_DB:-betreuungs_cockpit} # Deutsche Sortierung und Zeichensatz - wichtig fuer Namenslisten # und fuer die Volltextsuche mit der Konfiguration "german". POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8' LANG: de_DE.UTF-8 volumes: - postgres-daten:/var/lib/postgresql/data - ./backups:/backups healthcheck: test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-cockpit} -d ${POSTGRES_DB:-betreuungs_cockpit}'] interval: 10s timeout: 5s retries: 10 start_period: 30s networks: [intern] # Kein "ports:" - die Datenbank ist von aussen nicht erreichbar. minio: # S3-kompatibler Objektspeicher fuer die Dokumente. Wer einen externen # Speicher in der EU nutzt (z.B. Hetzner Object Storage), kann diesen # Dienst weglassen und in der .env S3_ENDPOINT darauf zeigen lassen. # Auch dann gilt: AV-Vertrag nach Art. 28 DSGVO abschliessen. image: minio/minio:RELEASE.2025-01-20T14-49-07Z restart: unless-stopped command: server /daten --console-address ":9001" environment: MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID:?S3_ACCESS_KEY_ID fehlt in der .env} MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY:?S3_SECRET_ACCESS_KEY fehlt in der .env} MINIO_BROWSER_REDIRECT_URL: ${MINIO_KONSOLE_URL:-http://127.0.0.1:9001} volumes: - minio-daten:/daten healthcheck: test: ['CMD', 'mc', 'ready', 'local'] interval: 15s timeout: 5s retries: 10 start_period: 20s ports: # Nur an die Loopback-Adresse gebunden. Die Konsole erreichst du # ueber einen SSH-Tunnel oder ueber deinen vorhandenen Proxy. - '127.0.0.1:9001:9001' networks: [intern] app: build: context: . dockerfile: Dockerfile args: # In der Oberflaeche und unter /api/gesundheit sichtbar. Setzen mit: # GIT_COMMIT=$(git rev-parse --short HEAD) docker compose up -d --build GIT_COMMIT: ${GIT_COMMIT:-} BUILD_ZEIT: ${BUILD_ZEIT:-} restart: unless-stopped depends_on: db: condition: service_healthy minio: condition: service_healthy environment: NODE_ENV: production DATABASE_URL: postgresql://${POSTGRES_USER:-cockpit}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-betreuungs_cockpit}?schema=public&connection_limit=10 AUTH_SECRET: ${AUTH_SECRET:?AUTH_SECRET fehlt in der .env} AUTH_URL: ${AUTH_URL:?AUTH_URL fehlt in der .env} AUTH_TRUST_HOST: 'true' ENC_KEY_V1: ${ENC_KEY_V1:?ENC_KEY_V1 fehlt in der .env - ohne ihn sind die verschluesselten Felder nicht lesbar} ENC_KEY_V2: ${ENC_KEY_V2:-} ENC_KEY_AKTIV: ${ENC_KEY_AKTIV:-1} INDEX_KEY: ${INDEX_KEY:?INDEX_KEY fehlt in der .env} SESSION_MAX_AGE: ${SESSION_MAX_AGE:-1800} REAUTH_MAX_AGE: ${REAUTH_MAX_AGE:-300} LOGIN_MAX_VERSUCHE: ${LOGIN_MAX_VERSUCHE:-5} LOGIN_FENSTER_SEKUNDEN: ${LOGIN_FENSTER_SEKUNDEN:-900} LOGIN_SPERRE_SEKUNDEN: ${LOGIN_SPERRE_SEKUNDEN:-900} SPEICHER_TREIBER: s3 S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000} S3_REGION: ${S3_REGION:-eu-central} S3_BUCKET: ${S3_BUCKET:-betreuungs-cockpit} S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} UPLOAD_MAX_BYTES: ${UPLOAD_MAX_BYTES:-26214400} OCR_AKTIV: ${OCR_AKTIV:-true} SMTP_HOST: ${SMTP_HOST:-} SMTP_PORT: ${SMTP_PORT:-587} SMTP_USER: ${SMTP_USER:-} SMTP_PASSWORD: ${SMTP_PASSWORD:-} SMTP_FROM: ${SMTP_FROM:-} TZ: Europe/Berlin ports: # Standard ist die Loopback-Adresse: dann erreicht die Anwendung nur # ein Proxy, der auf demselben Rechner laeuft. # # Liegt der Reverse Proxy auf einem ANDEREN Server, muss hier die # Adresse stehen, ueber die er herankommt - am besten die eines # privaten Netzes oder eines WireGuard-Tunnels: # # APP_BIND=10.0.0.5 # # 0.0.0.0 nur setzen, wenn eine Firewall den Port ausschliesslich # fuer die IP des Proxy-Servers oeffnet. Die Anwendung beendet kein # TLS und gehoert nie unverschluesselt ins offene Netz. - '${APP_BIND:-127.0.0.1}:${APP_PORT:-3000}:3000' networks: [intern] healthcheck: test: ['CMD', 'node', '-e', "fetch('http://127.0.0.1:3000/api/gesundheit').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] interval: 30s timeout: 5s retries: 5 start_period: 40s sicherung: # Taeglich um 03:15 Uhr ein verschluesseltes Backup nach ./backups. # Legt zusaetzlich die Pruefsumme ab und raeumt alte Staende auf. image: postgres:17-alpine restart: unless-stopped depends_on: db: condition: service_healthy environment: PGHOST: db PGUSER: ${POSTGRES_USER:-cockpit} PGPASSWORD: ${POSTGRES_PASSWORD} PGDATABASE: ${POSTGRES_DB:-betreuungs_cockpit} SICHERUNG_PASSWORT: ${SICHERUNG_PASSWORT:?SICHERUNG_PASSWORT fehlt in der .env} SICHERUNG_AUFBEWAHRUNG_TAGE: ${SICHERUNG_AUFBEWAHRUNG_TAGE:-30} TZ: Europe/Berlin volumes: - ./backups:/backups - ./scripts/sicherung.sh:/sicherung.sh:ro - ./scripts/wiederherstellung.sh:/wiederherstellung.sh:ro entrypoint: ['/bin/sh', '-c'] command: - | apk add --no-cache openssl coreutils >/dev/null echo "15 3 * * * /bin/sh /sicherung.sh >> /backups/sicherung.log 2>&1" > /etc/crontabs/root echo "Sicherungsdienst bereit. Naechster Lauf 03:15 Uhr." crond -f -l 8 networks: [intern] volumes: postgres-daten: minio-daten: networks: intern: driver: bridge