My blog deployment flow
February 3, 2026
My Hugo blog is coming together swimmingly. I’m comfortable in Markdown and Hugo makes it quick to build; big fan.
What I’m sometimes not a big fan of: is my idiosyncratic desire to be technical when it doesn’t need to be.
Let me explain:
I like VPSes
It’s your little computer-away-from-computer. It’s accessible, cheap (if you know what you’re getting), and you have sudo superpowers. I can have other things running while my site’s being served
This blog is hosted on a VPS (currently Vultr). Their machines are affordable and suit my use cases. Really I picked them because their color theme is 🔵 blue 🔵. There are many others in the market, though; shop around. Neither this post nor I is sponsored by Vultr; I’m just a happy customer.
VPS means self-hosting fu, managing which versions of software are installed, regular restarts for the latest security patches — you know the drill. But my brain cannot fathom any other way. I’ve tried selling myself on static S3 hosting or some other managed service: didn’t bite. I love the control… and the command line.
I like web servers
Web apps are my thing. Monitoring, databases, whatever; if they’re web-accessible, I’m sold. A solid web server is the gateway to this wonderland.
On my last site, I used to endure the pains of Nginx and Certbot for a while, longer than one cares to admit. This choice meant revisiting docs, Googling for answers, and going “How do I do this again? Is the command still in the bash history?”
Well, no more. To serve this site, I employ Caddy: the self-proclaimed “Ultimate Server” (and I kinda agree). Caddy gives me TLS on my sites for free, no extra plumbing. It’s one of those “it just works” things. Very nice!
What’s better: is that I’m not obliged to apt install it — great for my obsessive need to have a pure, unadulterated host environment — I just need Docker. Well, docker compose but it’s only got the one service:
services:
caddy:
image: caddy:2.10.2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- /path/to/hugo/blog/public:/srv
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
And the Caddyfile:
syaffers.xyz {
root * /srv
file_server
encode gzip
}
Want a subdomain? 5 more lines in the Caddyfile. Isn’t that great?
I like private git repos
I’m ashamed of my deluge of ramblings. No one should see my blog’s secret git diffs. So I chuck ’em into a dark corner of a VPS, along with 5 other projects I’ve been meaning to release (if I can muster the courage).
Private repos give me a safe space to experiment while not closing the door on future public contributions. Here’s how I set it up:
Make a bare repository somewhere in your VPS:
cd /path/to/repos
git init --bare blog.git
Then in your local:
git remote add origin ssh://user@domain.com/path/to/repos/blog.git
There you go, your private repo is set up. Everything is done via SSH. You might want to copy your SSH keys ahead of time (if you haven’t already).
But whoops, logistics. Having this setup means: after every blog post, I must
git pushto the private repo,sshinto my VPS,cdto/path/to/hugo/blog,git pull,- Run
make buildthat runshugo --minifyin a container (because host purity).
To streamline that, I have a post-receive hook in my private repository to do all that every time I push:
#!/usr/bin/env bash
set -e
echo "Updating content..."
git --work-tree="/path/to/hugo/blog" \
--git-dir="/path/to/repos/blog.git" \
checkout -f
cd "/path/to/hugo/blog"
make build
echo "Updated."
If you want to make the project public while still keeping this private repo, just add another remote:
git remote add github ssh://github.com/<user name>/<repo name>.git
EOF
That’s about it for my blog setup. Feel free to steal and make your own.