A small VPS for a few static sites
I picked up a cheap Debian 13 VPS (2 vCPU, 2 GB RAM, 35 GB disk) to host a few static sites. The goal was something I could forget about: automatic certificates, automatic security updates, and no SSH-and-edit to change a page.
The stack
- Caddy serves every site from its own directory and gets a Let’s Encrypt certificate per hostname on its own. A shared snippet handles compression, security headers, and access logs.
- Cloudflare proxies everything, in Full (strict) mode so it verifies Caddy’s certificate.
- nftables drops everything except SSH, and only accepts web traffic from Cloudflare’s ranges. A weekly systemd timer refreshes those ranges into a named set.
- unattended-upgrades handles Debian security updates.
Nothing runs in Docker. With two or three services, native packages and systemd units are less to reason about, and Docker’s own firewall rules would bypass the input chain anyway.
Deploys
Each site is a Hugo repo on GitHub. A workflow builds on push and once a day, then rsyncs public/
to the server. The daily build lets date-driven content update itself even when nobody commits.
The deploy key is the part I like most. On the server it’s restricted in authorized_keys:
restrict,command="/usr/bin/rrsync /srv/www/example" ssh-ed25519 AAAA... github-deploy
rrsync ships with Debian’s rsync package. With that forced command the key can’t open a shell or
forward ports, and it can only write inside that one directory. If the GitHub secret ever leaked, the
worst case is a defaced page, not a compromised server.
Small things that bit me
- Running
sudo caddy validatecreated the access log as root, and the next reload failed withpermission denied. Validate as the service user instead:sudo -u caddy caddy validate .... - GitHub stopped accepting passwords for git over HTTPS in 2021. On a fresh laptop,
gh auth loginworks, but git only uses it aftergh auth setup-git. - Bringing a domain up DNS-only first, so Let’s Encrypt can reach the origin directly, leaves the origin IP in passive-DNS history. With HTTP-01 working through the proxy, it’s better to start proxied.