Building and releasing this site.
I build this bilingual site with Next.js and release it with a script that checks every step and restores the previous version if a check fails.
There is no application server: Caddy serves static files. Every check described here runs on each release.
The setup
The site is a Next.js static export in English and Spanish. The production image builds it with Node.js and serves the files with Caddy, so no Node.js process runs in production.
It runs as a single container on a small Hetzner server behind Cloudflare, which handles TLS for visitors and caches pages at the edge.
What I built
The build, the release process, and the checks around them.
Checked builds
The image build runs linting, type checks, and the static export, then fails if an expected page is missing or a retired address comes back.
Releases from main only
The release script deploys only a clean main commit that matches the remote. It uploads the source with a checksum, builds the image on the server, and tags it with the commit.
Checks and automatic rollback
Before switching, it validates the web server configuration. After switching, it checks the new container and the origin, purges the public pages from Cloudflare's cache, and runs public smoke tests. If a check fails, it restores the previous image.
Hardened hosting
The container runs read-only, with every Linux capability dropped except the one needed to open the web ports. Responses carry a strict Content Security Policy and other security headers. Server administration uses SSH over a private Tailscale network.
Tests in both languages
Playwright tests cover both languages, every service page, navigation at mobile and desktop widths, and reduced motion, with automated WCAG A and AA checks using axe.
Components
The site, its container image, and the release script live in one repository.
- Site
- Next.js · TypeScript · CSS Modules
- Web server
- Caddy · security headers
- Hosting
- Docker Compose · Hetzner Cloud
- Edge
- Cloudflare cache · Web Analytics
- Release
- Bash · Cloudflare API · SSH over Tailscale
- Tests
- Playwright · axe
Want releases you can repeat?
Tell me how your application is deployed today, how often it changes, and what tends to break.
View the service