How My Site Is Deployed
tl;dr#
- Changes are pushed to a self-hosted forgejo instances
- Pushes to
maintrigger an action that builds the site withhugoand copies it with rsync to another server. - Caddy serves the static files at
/srv/calebsharp.dev
Hugo#
This is pretty self-explanatory, but this site is built with Hugo. This is actually my first time trying it, and I’m still getting used to it. For that reason, I decided to start with a theme and ended up choosing this neat terminal theme by panr. I’ve started to rip it apart and customize it to my liking and I expect to eventually change most everything. At this point, I’m still trying to decide if it makes more sense to treat my custom stuff as a “theme”, or just inline everything into the main project. I get the feeling it only matters if I want to re-use the theme on another site, and/or share it. Guess we’ll see!
Other than that, I’ve been appreciating the authorship experience of Hugo. I feel like I can focus on writing when I want to write, and focus on everything but the posts when I want to tinker. It’s a nice separation of concerns that I think stops me from getting too distracted.
Forgejo#
I host my own Forgejo instance on an old PC I have at home. I use Tailscale to connect to it (and holy crap what a useful tool that is). I have a cheap Hetzner instance somewhere in Germany that I use as an actions runner (I’ll have to write about that soon, it’s a bit finicky to set up, especially in my case where I wanted to run the runner in podman and run workflows in “nested” containers). Other than that, it’s pretty stock.
To have a nice domain/SSL, I don’t use Tailscale’s MagicDNS feature (I think the
domains are ugly!). Instead, I just have a public DNS record that resolves to
the internal Tailscale IP of the machine (dig forgejo.ts.calebsharp.dev). If
you’re not on the tailnet, you can’t connect. I proxy Forgejo (+ all the other
things I run) through Caddy for automatic HTTPS.
When I push new changes to the upstream repo, the runner kicks off a build job
that basically just installs some tools (TODO for myself: add tools to the
runner image) and runs hugo build --minify. I’m using
mise to manage the versions of the tools I need, both
for local dev and in CI/CD (although I can’t get it to install hugo; there seems
to be some problem with the URL it tries to access for macOS builds). It then
copies the built static files with rsync to my Hetzner VPS that’s actually
hosting the site. Since both the runner and server are both in my tailnet, it’s
trivial to let them connect via SSH without having to worry about exposing SSH
to the world. That’s it for deployment! It’s a little refreshing to not have a
stupidly complex build step with containers and linters and all that crap.
Caddy#
The static files deployed to the server are ultimately served by Caddy. Pretty basic config, along with some automatic HTTPS and basic cache-control headers:
calebsharp.dev {
root /srv/calebsharp.dev
encode zstd gzip
file_server
header /css/* {
Cache-Control "public, max-age=3600, must-revalidate"
}
header /fonts/* {
Cache-Control "public, max-age=604800, must-revalidate"
}
# API credentials for ACME DNS challenges
import porkbun
}
And that’s it! I mentioned this in my first post, but it’s nice to have something architecturally simple to let myself focus on writing when I want to. On the other hand, I feel like I have quite a bit of flexibility to do whatever cool stuff I want to try out. I think it strikes a nice balance.
Thanks for reading! If you’re trying to do something similar and want to chat about it, feel free to send me an email (info on the about page).