feat(infra): production deployment pipeline — Caddy, staging, Gitea Actions (#497) #499

Merged
marcel merged 39 commits from feat/issue-497-prod-deploy into main 2026-05-11 14:29:33 +02:00
Showing only changes of commit 56e55ff488 - Show all commits

63
infra/caddy/Caddyfile Normal file
View File

@@ -0,0 +1,63 @@
# Caddyfile for the Familienarchiv host.
#
# Caddy runs on the host (not in a container) and reverse-proxies into
# the docker compose stacks bound to 127.0.0.1.
#
# Naming convention for ports (also documented in docker-compose.prod.yml):
# production: backend 8080, frontend 3000
# staging: backend 8081, frontend 3001
# gitea: 3005
#
# Security headers and the /actuator block apply to both archive vhosts.
# X-Frame-Options is deliberately NOT set here: Spring Security configures
# frame-options SAMEORIGIN (for the in-app PDF preview iframe). Setting
# DENY in Caddy would conflict.
(security_headers) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
(block_actuator) {
# Defense in depth: even if management.endpoints.web.exposure.include grows
# in application.yaml, /actuator/* is unreachable externally. The internal
# Prometheus scrape (future) talks to the backend directly on the docker
# network, not via Caddy.
@actuator path /actuator/*
respond @actuator 404
}
archiv.raddatz.cloud {
import security_headers
import block_actuator
handle /api/* {
reverse_proxy 127.0.0.1:8080
}
handle {
reverse_proxy 127.0.0.1:3000
}
}
staging.raddatz.cloud {
import security_headers
import block_actuator
handle /api/* {
reverse_proxy 127.0.0.1:8081
}
handle {
reverse_proxy 127.0.0.1:3001
}
}
git.raddatz.cloud {
import security_headers
reverse_proxy 127.0.0.1:3005
}