Skip to content

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.

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

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

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

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

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