head-vps/README.md

66 lines
3.3 KiB
Markdown

# head-vps
The HEAD VPS (`185.143.103.191`, Ubuntu 24.04, 4 vCPU, 3.8 GB) as code. Everything it runs is in
this repository; a `git push` to the bare repo on the VPS deploys it. No CI, no external forge.
| Piece | Role |
|---|---|
| [doco-cd](https://doco.cd) | Watches the bare repos in `/srv/git`, runs `docker compose up` (with `build:`) on every push, updates itself |
| [caddy-docker-proxy](https://github.com/lucaslorentz/caddy-docker-proxy) | Reverse proxy with automatic HTTPS, configured by `caddy.*` labels on containers |
| RustFS (`rustfs/`) | The S3 of every app: API at `s3.fuckingtech.ch`, public media at `assets.fuckingtech.ch` |
| sharp (`sharp/`) | Image transformer at `sharp.fuckingtech.ch` (from `nestarz/remove-background`, without background removal) |
| [Forgejo](https://forgejo.org) | The forge at git.fuckingtech.ch: account and repos declared in `site.env` + `forgejo/`, hydrated on every deploy |
## Layers
1. **`bootstrap.sh`**: only to rebuild the host from scratch (`ssh fuckingtech.ch 'sudo bash -s -- <app repos>' < bootstrap.sh`,
push, run again) or to create a new app's bare repo; doco-cd manages everything else, itself included.
2. **`.doco-cd.yml` + `*/compose.yaml`**: the base stacks (doco-cd, caddy, gitore). doco-cd applies them
on every push to `main`.
3. **Apps**: each app repository carries its own `.doco-cd.yml` and compose file, and is listed in
`doco-cd/compose.yaml`'s `POLL_CONFIG`.
## Use
```sh
git remote add forge ssh://git@git.fuckingtech.ch:2222/headpoolnumerique/head-vps.git
git push forge main # deploys; follow with: ssh fuckingtech.ch 'docker logs -f doco-cd-doco-cd-2'
```
Repositories belong to the organisation `headpoolnumerique` (public to read); Forgejo stores them in
`/srv/git/headpoolnumerique/<name>.git`, where doco-cd reads them.
A site is two labels on a container that joins the external `caddy` network:
```yaml
labels:
caddy: storytellurx.fuckingtech.ch
caddy.reverse_proxy: "{{upstreams 8000}}"
```
## Certificates
Caddy keeps its certificates and ACME account in the `caddy_data` volume: restarts and redeploys reuse
them and never count against Let's Encrypt, and renewal is automatic. Let's Encrypt allows 5 failed
validations per name per hour, so **a new name goes in this order: DNS record first, Caddy label after**.
A label published while the name points elsewhere fails on every retry and locks the name out.
## DNS
`dns/` (OpenTofu, official `Infomaniak/infomaniak` provider) declares only the records that point at
the VPS: A and AAAA for `storytellurx`, `v1.storytellurx`, `git` in `fuckingtech.ch`.
Each `infomaniak_record` is a single record, so nothing else in a zone is read or changed.
It is a workflow run **by the VPS**: the `dns` stack (in `.doco-cd.yml`) builds `dns/Dockerfile`
(OpenTofu + fnox + the provider) and runs `apply.sh` once whenever `dns/` changes. The token
(scopes `dns:read dns:write`) and the state passphrase are in `dns/fnox.toml` (recipients: Elias, the
VPS); the state lives in the VPS's `dns_state` volume, encrypted (OpenTofu state encryption, AES-GCM).
```sh
ssh fuckingtech.ch 'docker logs dns-apply-1' # the last run's plan and apply
```
## Mirror
`github.com/bureaudouble/head-vps` (private) is a copy, pushed alongside: `git push forge main && git push github main`.
Deployments only ever come from Forgejo's repositories on the VPS.