- Unknown hostnames and direct IP access no longer land on the first
vhost (Gitea): port 80 closes the connection, except for ACME
challenges, and port 443 refuses the handshake.
- Mozilla intermediate ciphers (forward secrecy only, no CBC), session
cache, no session tickets, and HSTS with a short max-age for now.
- Set X-Forwarded-For to the client address instead of appending to
whatever the client sent.
RabbitMQ stores its data under rabbit@<hostname>, and the hostname
changed with every recreated container, so an image update or an .env
change came up with an empty broker. Pin it to `rabbitmq` and document
how to carry the definitions over on an existing deployment.
Every project shares infra-net, where a generic service name like `api`
resolves to every container that has it, round-robin, across projects.
The edge now proxies to container names (musify-api, infra-zitadel, ...).
Zitadel v4 redirects every sign-in to /ui/v2/login, which is served by a
separate container (zitadel-login) that this stack didn't run, so logging
in on a fresh instance ended in a 404.
- Add zitadel-login, with its own service account (login-client) whose
PAT Zitadel writes to a volume only that container mounts.
- Configure the Login V2 base URL (ZITADEL_LOGIN_BASE_URL): its own port
in dev, same hostname as Zitadel behind the edge in prod.
- Pin zitadel and zitadel-login to v4.19.1 and add healthchecks.
- Run Zitadel as root, as Zitadel's own compose does, so it can write the
PATs into root-owned mounts (the Linux bind mount and the new volume).
- Route /ui/v2/login on the auth vhost to the login container and drop
the no-op grpc_set_header.
- Document the extra steps for an instance created before this.
One nginx owns host ports 80/443 for every project (Gitea, Musify) and
proxies by container name over infra-net and gitea_gitea; a certbot
service renews the Let's Encrypt certificates.