services: app: build: . # Rootless: Prozess läuft als UID:GID 10001:10001 (siehe Dockerfile USER). # Entspricht der festen Identität im Image – auch beim Deployment mit # fertigem Registry-Image setzen (siehe docker-compose.deploy.yml). user: "10001:10001" # Kein ports:-Mapping: Die App ist nur im internen Compose-Netz erreichbar. # Nach außen geht es ausschließlich über den Caddy-Reverse-Proxy (443/HTTPS). expose: - "3000" environment: # Interner Direktzugriff für den Erinnerungs-Scheduler (kein Hairpin über den Proxy nötig). - APP_INTERNAL_URL=http://app:3000 volumes: - gutschein-data:/data env_file: [.env] restart: unless-stopped security_opt: - no-new-privileges:true caddy: image: caddy:2-alpine restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" # HTTP/3 (optional, schadet nicht) volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config environment: DOMAIN: ${DOMAIN:?DOMAIN muss in .env gesetzt sein (z. B. gutscheine.example.de)} depends_on: - app security_opt: - no-new-privileges:true volumes: gutschein-data: {} caddy-data: {} caddy-config: {}