Ansible deployment for CariConnect Bremen (Django container on gierz-services, Caddy route on gierz-gateway). Slots into the prototypes/* drop-in hook in main infra.
Find a file
Paul Gierz 27a348344f
All checks were successful
Deploy CariConnect Bremen / deploy (push) Successful in 48s
Fix 400 after subdomain promotion: align Django config with new vhost
Caddy was moved to serve cariconnect-bremen.gierz.xyz (main infra),
but the app env still had the path-mount era settings, so Django
rejected the new Host header with a stock 400.

- ALLOWED_HOSTS: gierz.xyz -> cariconnect-bremen.gierz.xyz (+localhost
  /127.0.0.1 so the loopback container healthcheck keeps passing)
- FORCE_SCRIPT_NAME emptied: no path prefix is stripped anymore; the
  healthcheck probe path follows it automatically
- CSRF_TRUSTED_ORIGINS / public_url moved to the subdomain
- Gateway task now removes the stale /prototypes/ path-mount snippet
  instead of dropping it (vhost is owned by main infra now)

Co-Authored-By: LLM <noreply@invalid>
2026-06-11 19:26:54 +02:00
.forgejo/workflows Add CI-specific inventory so deploy doesn't SSH-loopback to itself 2026-05-11 14:57:59 +02:00
inventory Track vault file 2026-05-11 15:23:00 +02:00
playbooks Initial cariconnect/infra scaffold 2026-05-11 14:57:59 +02:00
roles/cariconnect_web Fix 400 after subdomain promotion: align Django config with new vhost 2026-06-11 19:26:54 +02:00
.gitignore Use .vault_pass.sh pattern from main infra 2026-05-11 14:57:59 +02:00
.vault_pass.sh Use .vault_pass.sh pattern from main infra 2026-05-11 14:57:59 +02:00
ansible.cfg Use .vault_pass.sh pattern from main infra 2026-05-11 14:57:59 +02:00
README.md Use .vault_pass.sh pattern from main infra 2026-05-11 14:57:59 +02:00
requirements.yml Initial cariconnect/infra scaffold 2026-05-11 14:57:59 +02:00

CariConnect Bremen — deployment

Ansible repo for deploying the CariConnect Bremen Django site (cariconnect/web) onto the gierz homelab at https://gierz.xyz/prototypes/cariconnect-bremen.

What this repo owns

  • The cariconnect_web Ansible role — builds the container image with podman on gierz-services from code.gierz.xyz/cariconnect/web, renders a Quadlet container unit, brings up the service.
  • The Caddy snippet drop-in for gierz-gateway that registers the /prototypes/cariconnect-bremen/* route — written via delegate_to: gierz-gateway against /etc/caddy/conf.d/prototypes/cariconnect-bremen.caddy.
  • The CI workflow that re-runs the playbook on push to main, executed by an act_runner on gierz-services.

What main infra/ owns

  • The generic prototype mount mechanism (import conf.d/prototypes/*.caddy inside the http://gierz.xyz site block on gateway).
  • The caddy role that creates /etc/caddy/conf.d/prototypes/.
  • The act_runner role (TODO — separate issue).

Main infra has no knowledge of CariConnect specifically; this repo slots into the generic prototype mechanism.

First-time run (manual, before CI is set up)

git clone https://code.gierz.xyz/cariconnect/infra.git
cd infra
ansible-galaxy collection install -r requirements.yml

# One-time: stash the vault password in `pass` so .vault_pass.sh finds it.
# (Same shape as main infra's ansible/vault-password entry, namespaced
# under cariconnect-infra to keep blast radius small.)
pass insert ansible/cariconnect-infra/vault-password

ansible-playbook -K playbooks/deploy.yml

In CI, .vault_pass.sh reads $ANSIBLE_VAULT_PASSWORD instead of touching pass; the Forgejo Actions workflow sets that env var from the ANSIBLE_VAULT_PASSWORD repo secret.

Vault entries needed

inventory/group_vars/all/vault.yml (encrypted with ansible-vault):

vault_cariconnect_web_secret_key: "..."         # Django SECRET_KEY (50+ chars)
vault_cariconnect_web_deploy_token: "..."       # Forgejo PAT, read:repository scope only

Generate the secret key with python -c "import secrets; print(secrets.token_urlsafe(50))".

URL layout, recap

browser  →  edge.gierz.xyz (TLS, gierz.xyz catch-all)
         →  gateway 10.10.0.2 (Caddy imports conf.d/prototypes/*.caddy;
                               this repo writes cariconnect-bremen.caddy
                               with `handle_path /prototypes/cariconnect-bremen/*`
                               that strips the prefix)
         →  services 192.168.122.30:8000 (Quadlet cariconnect-bremen)
         →  gunicorn → Django (FORCE_SCRIPT_NAME re-adds the prefix to
                               rendered HTML)