# Beispiel-Deployment für einen anderen Server: nutzt das fertige, # öffentliche Image aus der Gitea Container Registry – kein lokaler Build, # kein Quellcode auf dem Zielserver nötig. # # Verwendung auf dem Zielserver: # 1) docker login gitea.lokcal.de -u # (einmalig; danach reicht `docker compose pull` auch ohne Login, # da das Image öffentlich ist) # 2) cp .env.example .env # Pflicht: DOMAIN, APP_URL=https://, # # APP_USER, APP_PASSWORD_HASH, SESSION_SECRET, # # SETTINGS_ENCRYPTION_KEY, CRON_SECRET # 3) docker compose -f docker-compose.deploy.yml pull # 4) docker compose -f docker-compose.deploy.yml up -d # # Update auf eine neue Version: # docker compose -f docker-compose.deploy.yml pull # docker compose -f docker-compose.deploy.yml up -d # Pinne für reproduzierbare Deployments einen Digest statt :latest # (Digest steht in Gitea unter → Pakete → container/gutscheintracker). services: app: image: gitea.lokcal.de/loki/gutscheintracker:latest # Rootless: gleiche feste UID:GID wie im Image (Dockerfile USER). user: "10001:10001" # Kein ports:-Mapping: Die App bleibt im internen Compose-Netz; # nach außen geht es nur über Caddy (443/HTTPS). expose: - "3000" environment: # Interner Direktzugriff für den Erinnerungs-Scheduler # (kein Hairpin über den öffentlichen 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 healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 15s 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: {}