services: app: # Ohne Build-Argumente: alles, was die Anwendung braucht — DATABASE_URL, # AUTH_* — liest sie zur Laufzeit aus .env. Das Abbild ist damit für jede # Umgebung dasselbe. build: context: . restart: unless-stopped # Nur auf der Loopback-Adresse, nicht auf allen Schnittstellen. Erreichbar # ist die App damit ausschliesslich über den Reverse Proxy, der TLS # beendet — sonst stünde daneben derselbe Dienst unverschlüsselt offen, # und ein Fehler in der Firewall genügte. ports: - "127.0.0.1:3000:3000" env_file: - .env # Replaces the Vercel Cron job from vercel.json (not available outside # Vercel): calls the same endpoint on the same daily schedule using the # same bearer-secret auth the route already expects. cron: image: alpine:3.20 restart: unless-stopped depends_on: app: condition: service_healthy env_file: - .env entrypoint: ["/bin/sh", "-c"] command: - | echo "0 3 * * * /bin/sh -c 'wget -q -O- --header=\"Authorization: Bearer \$$CRON_SECRET\" http://app:3000/api/cron/apply-pending-changes >> /var/log/cron.log 2>&1'" > /etc/crontabs/root crond -f -d 8