Colophon
One machine, and how I run it.
The footer says this site is served by Caddy from a server I administer. This page is that sentence written out: what runs on the host, how a change reaches production, and how the whole thing fails.
This site
Astro, built to static files. No runtime, no database behind it, no framework shipped to the browser, no tracking and no analytics. Every colour, size and duration resolves from one token file, and seventeen render gates — contrast, keyboard operation, target size, reduced motion, RTL mirroring, silent overflow — run over every page in both themes before anything ships.
The edge
Caddy terminates TLS with certificates it obtains and renews on its own, serves over HTTP/2, redirects www to the apex and strips trailing slashes so each page has one URL, and returns the Spanish 404 to Spanish paths. Content is read from disk per request, so publishing restarts nothing.
Every response carries
- Strict-Transport-Security
- max-age=31536000; includeSubDomains
- X-Content-Type-Options
- nosniff
- Referrer-Policy
- strict-origin-when-cross-origin
- Permissions-Policy
- geolocation=(), camera=(), microphone=(), payment=(), usb=()
- Content-Security-Policy
- default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; font-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'
No third-party origin is permitted at all. Fonts, styles and scripts are served from this host, so nothing on the page can call anywhere else.
Independent scanners, so this is checkable rather than claimed:
Getting a change out
- Gate and pushseventeen gates, then origin
- Pullon the host, via a deploy key
- Build besideinto a directory nobody is serving
- Swaptwo renames
The swap is the part that matters. Astro clears its output directory when a build starts, so building straight into the directory Caddy serves would empty the site for the length of every build, and leave it empty if that build failed. Building beside it and renaming makes the change effectively instant and leaves the previous build in place, so a rollback is the same two renames in the other order — no reload, nothing to reconfigure.
The host
One bare-metal Debian machine that I install, patch and answer for. It runs this site alongside a number of other services, described here by what they do rather than by where they are.
- Reverse proxy and TLS
- Caddy, in front of every site and service on the box.
- Relational database
- PostgreSQL, on the fastest disk and deliberately off the media pools.
- Object storage
- An S3-compatible store for application media.
- Containers
- Docker, with persistent volumes pinned to a dedicated disk so a full root partition cannot take the daemon down with it.
- Source control and delivery
- A self-hosted Git forge with push-to-deploy pipelines. Each repository gets its own key, so a compromised pipeline reaches one project rather than the host.
- Local model inference
- For work where the data cannot leave the machine it is already on.
- Monitoring and hardening
- Per-host metrics with alerting rules, SMART disk monitoring, and fail2ban on the exposed surface.
How it fails
It is one machine on a residential connection with a dynamic address, which is worth saying plainly on a page about reliability. A dynamic DNS updater watches that address and republishes it when it changes, so an address change costs the poll interval plus the record TTL. There is no second machine, so a kernel update takes everything down together — deliberately, at a time I pick. That is the right trade for a personal host and the wrong one for a company, which is why the production systems in the case studies are built differently.