No description
  • Shell 42.6%
  • JavaScript 38.6%
  • Dockerfile 11.5%
  • HCL 7.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-03 23:08:05 +01:00
caddy fix(caddy): the Caddyfile keyword is test_dir (test_ca made caddy-docker-proxy drop the whole global block, email included) 2026-11-03 19:01:30 +01:00
dns feat(dns): sharp.fuckingtech.ch (reviewed plan: 2 to add) 2026-11-03 23:07:27 +01:00
doco-cd feat(doco-cd): one deployment at a time (parallel Vite builds exhausted the 3.8 GB host; a build was cancelled) 2026-11-03 20:52:27 +01:00
forgejo fix: RustFS init and Forgejo hydrate as post_start hooks (doco-cd treats an exited one-shot service as a failed deployment) 2026-11-03 20:36:59 +01:00
hypercomics feat: host age key and swap in bootstrap; storytellurx repo; Hypercomics static sites behind Caddy 2026-11-03 16:03:33 +01:00
rustfs fix(rustfs): processed/ public too (autokit-media writes its renditions there) 2026-11-03 22:22:52 +01:00
sharp feat(sharp): image transformer on the VPS, replacing sharp.elias.systems 2026-11-03 23:08:05 +01:00
.doco-cd.yml feat(sharp): image transformer on the VPS, replacing sharp.elias.systems 2026-11-03 23:08:05 +01:00
bootstrap.sh feat: host age key and swap in bootstrap; storytellurx repo; Hypercomics static sites behind Caddy 2026-11-03 16:03:33 +01:00
README.md feat(sharp): image transformer on the VPS, replacing sharp.elias.systems 2026-11-03 23:08:05 +01:00
site.env feat(sharp): image transformer on the VPS, replacing sharp.elias.systems 2026-11-03 23:08:05 +01:00

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 Watches the bare repos in /srv/git, runs docker compose up (with build:) on every push, updates itself
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 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

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:

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).

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.