Besides The Beguinage, I have two professional websites respectively hosted on Codeberg Pages and Github Pages with domains I own. This is very silly and stretches me across three hosts where the differences in deployment and service are real fiction. My three big gripes:
- The two Git hosts have sorts of CI/CD. I deploy my Codeberg site with a post-receive Git hook in lieu of Woodpecker CI or Forgejo Actions, and my GitHub site still uses Eleventy
whose subject matter isn't tech at Allwith GitHub Actions. I don't love Nekoweb enough to be a Patreon supporter (sorry) and think even then its FTP and Git offerings are a little simple for my needs. - MIME types, which are only a problem for this website. Most web servers, including Cloudflare (which Nekoweb uses), assign
application/octet-streamto anything that isn't HTML, CSS, JavaScript, or an image, which is why browsers prompt you whether you want to download SVGs and non-web text file formats instead of just displaying them. This is an annoyance for me because The Beguinage has Python and shell scripts and stray CSV's hanging around, and I like naming text files with pseudo extensions likessh.keys.
And with an eye toward the future, I'm a little interested in dynamic hosting. Not sure yet since I dislike JavaScript so much, but if this is how I use Flask, sure.
The stack is:
- VPS: Hetzner CPX11, 2-core AMD + 2G RAM + 40G SSD
- Web server: Caddy
- Domain registrar: Porkbun
- Security:
- VPN: Tailscale
- SSG: kamote
My workflow is:
- Edit a local git repo on my laptop or desktop.
- Push changes to my personal git server on a clamshelled MacBook Air 2017 (current uptime: 31 weeks), which has a post-receive hook that mirrors the repo to yet another bare repo on my VPS.
- My VPS copy has another Git hook that copies the built website to
/var/www/, which Caddy exposes to my domain.
Storing websites at /var/www seems to be an established convention with no real logic on a single-user server. I could very well have put them anywhere in my user directory, but here's my /etc/caddy/Caddyfile:
www.beguine.net, beguine.net {
root * /var/www/beguine.net
try_files {path}.html {path}
encode gzip zstd
file_server {
index index.html
}
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
@textpls {
path *.sh
path *.py
path *.keys
path *.csv
}
header @textpls Content-Type "text/plain; charset=utf-8"
}
I went with Caddy over nginx on pure vibes, man. As you can see, the config is only a few lines. You just start it as a system service and restart it to load changes.
try_files {path.html} pathallows "pretty URLs" without the whole song-and-dance of everything secretly being calledindex.html.encode gzip zstdmakes these default compression methods explicit.file_serverserves files... statically.handle_errorsmakes informative HTTP error pages. These seem to be customizable via templates, but I'm leaving it boring for now.@textplsis my custom header for all my MIME type gripes.
The post-receive Git hook on my VPS for actual deployment:
#!/usr/bin/env bash
GIT_DIR=$(pwd)
UV_BIN="/home/user/.local/bin/uv"
TEMP_WORK_TREE=$(mktemp -d)
TARGET="/var/www/beguine.net"
BRANCH="main"
while read oldrev newrev ref
do
if [[ "$ref" == "refs/heads/$BRANCH" ]]; then
echo "Building..."
git --work-tree="$TEMP_WORK_TREE" checkout -f "$BRANCH"
cd "$TEMP_WORK_TREE" || exit
$UV_BIN run kamote build
if [ -d "_build" ]; then
echo "Syncing _build to $TARGET..."
rsync -avq --delete _build/ "$TARGET/"
echo "Deployment successful!"
else
echo "Error: _build directory not found after run."
exit 1
fi
rm -rf "$TEMP_WORK_TREE"
fi
done
UV_BINhas to be set because Git hooks run independent of a shell path like that set by my zsh config.- Git worktrees are awesome. This is more or less what happens in any web CI/CD: builds happen automatically on push, in a temporary directory deleted upon completion.
- Storing sites at
/var/www/is an established convention I'm following even though Caddy could totally serve anything in a user directory. However, I like keeping my_builddirectory in.gitignoreand that my bare repositories never see it.
Nekoweb has been fun and this website has been on it longer than it was on Neocities, but in the end I feel a little older than the userbase and more focused on backend and systems programming than in web dev. But generally, I'd like the indie web to be as indie as possible, which includes self-hosting and not being too closely tied to any one community's norms.
This also lets me get rid of making both an RSS and Atom feed for the sake of Nekoweb's global feed. I don't know why it only accepts RSS. If web development education is a goal of this platform, I would think migrating users toward Atom is a good move.
My other big goal in moving is to self-host stagit at git.beguine.net in lieu of the mess that is src. At least the Python projects that I want to distribute as something other than tarballs. The whole MIME type circus was in pursuit of keeping my shell scripts as they are.