Corposphere escape velocity: CI/CD on my very specific indie tech stack

Sep 18, 2025, 6:07 AM
Δ
Sep 19, 2025, 7:42 AM
SiteV2 Decentralization Anticorporatism IndieWeb Ownership Blog

As of this last week, I finally managed to get everything for my site moved out of GitHub and onto an independent, federated git host. Many people have taken up Codeberg, but I figured I'd put my decentralization money where my decentralized mouth is and not crowd the "main" instance, so I've landed on the Forgejo instance hosted by Disroot.

I also managed to migrate hosting from Vercel to NearlyFreeSpeech.NET (NFSN). This came suggested from the most consistently reliable technology recommenders on the entire internet: the #random channel of the Julia Zulip.

It's a relief to be out of the corposphere in these little ways, but it does forgo a few creature comforts. One of the things Vercel/GitHub gets you is an automated CI/CD process that is so slick that I literally do not remember setting it up. I, like, signed up for Vercel with my GitHub account and they just automatically hooked up GitHub Actions to my repo or something? Who even knows.

Disroot and NFS.NET are not getting that sweet sweet investor money to fund LLMs automatically generating 4K-airbrushed-thumbs-up-emojis and misreads-of-my-issue-description-rehashed-as-emoji-haikus every time I comment in a repo, or whatever we're using GitHub Actions for these days. But it's still nice to have the website automatically update when I push something instead of having to manually SFTP the files myself (which is also a thing I just learned how to do this week).

So here's how I set that up!

1. Get into your internet computer

NFSN lets you SSH directly into the infrastructure running your site, which is probably pretty normal I guess.

I have pretty minimal systems-anything experience, so it's still very magical to me to have a real life account on a real life system out there that I can log into and just...do stuff. And then it also can end up on the internet! Like, imagine owning a electronic billboard out on a highway on some long-forsaken Mississippi backroad or something. Would you not log in to it sometimes just to doodle something? To know full well those photons will never glance a human eye, but to bounce them into the night all the same, past the addled fireflies? To witness to whatever's out there in the moment that you, too, are out there in the moment? To feel the world alive and at your fingertips—experiencing and being experienced? echo "hello!"

Anyways, Forgejo Actions require setting up a runner on some remote system, and I'm already paying for this remote system, so once we're logged in we just need to install Forgejo's runner program and hook it up to the repo.

Anything you plop into the /home/public/ directory get served on your site itself, so the end goal is to automate the process of dumping new stuff into that directory when something gets committed to the repo.

There are actually two sides to this: one is installing the runner daemon itself (this was my first time actually having to understand what that term meant), and the other is setting up NFSN site to automatically keep it running by registering the daemon with their system.

2. Follow the instructions (mostly)

The Forgejo Runner installation guide is actually pretty up-to-date and comprehensible. There's also a guide in Codeberg's documentation that is a bit outdated, but gives some helpful perspective on how you might diverge from some of the defaults if you want.

The fact that NFSN's systems don't have Docker means we have to do a little bit of off-roading—here are all the little hurdles I had to figure out.

Binary installation

The given instructions for downloading the binary worked great! However, there are a couple security restrictions to be aware of:

Setting up the runner user

Don't.

(Actually, you just can't; NFSN won't let you willy-nilly create new users. I'm sure there are access or safety things to worry about if you can't set up a separate user—and I'm also sure that I don't know what they are! Everything worked fine for me without setting up a user, so...*shrug*)

Setting up the container environment

Don't.

(Again, you actually just can't, because there's no Docker on the NFSN system. We're just gonna be rawdogging commands from Forgejo actions. What you might want to do, though, is setup up a new directory somewhere to run the runner from so you don't accidentally rm -rf your whole site while playing around with actions. I created /home/protected/runnerspace/ as a set-aside place for that.)

Registering the runner

Definitely use the prompted registration wizard as demoed in the docs (i.e. forgejo-runner register instead of manually passing in values), because I tried doing it via command line arguments and ended up with less-helpful defaults.

As mentioned in the docs, you can get your runner registration token from your repo settings.

The labels part is very important! Since we aren't running things in a container, we need to specify the environment is host, so your label should look something like freebsd:host. If you don't specify a *:host label at some point, the runner will default to trying to run Docker, which won't work (because it's not installed).

Registering your runner will generate a .runner file in the directory you ran the command from. If you're not already there, you need to make sure to move that .runner file to the directory you want your daemon running in, because any time you run forgejo-runner it checks for that file to know what it's been registered as.

Once your runner is registered, an associated entry should pop up under the Actions/Runners dashboard in your Forgejo repo's Settings page.

Customizing the configuration

I decided to tweak the default config a little. To do this you can run the provided command:

forgejo-runner generate-config > config.yml

and then edit it with vim config.yml or whatever editor you want to use.

The things I tweaked (aside from fixing my labels info, which I had messed up) were shrinking all the timeouts to 900s at the largest, just to be safe—11ty should only take a few seconds anyways.

I moved my config file to /home/conf/forgejo-runner.yaml because I noticed it was there, and it was empty, and it seemed like that's where configs should maybe go. Feel free to put it wherever, I guess, but just know that you'll want to pass the path in as a command line argument when starting up the daemon.

Starting the runner

With registration and maybe a config complete, you can actually start the runner manually and run an action!

For my setup, I ran this (from the /home/protected/ directory):

bin/forgejo-runner daemon --config /home/conf/forgejo-runner.yaml

Once the daemon is running, you should be able to check out the Actions/Runners dashboard in your Forgejo repo's settings and see that the Status field for your runner is lit up green and says Idle. That means you're good to run an action! The Forgejo Action Quick Start Guide has a nice little demo action config file. If you add that to your repo under .forgejo/workflows/demo.yaml, commit it, and push that commit to your Forgejo instance, you should kick off your first (successful?!) Forgejo Action run that's hosted directly on your NFSN site! Cool stuff.

3. Exercise the daemon

We don't want to have to manually kick off the daemon all the time (and NFSN won't guarantee it stays running forever once you start it, or maybe you want to get back to doing other stuff with the shell you've SSH'd into). The proper setup is to instead register the daemon, which NFSN's web portal has a handy interface for.

The instructions in the interface and in the FAQ should were fairly informative, and you basically just need two things: a run script, and a quick setup step in the UI.

Write a script to run the daemon

This can be really simple, you just need a little wrapper to pass the needed command line arguments into the daemon start command. Mine looks like this:

#!/bin/sh
exec /home/protected/bin/forgejo-runner daemon --config /home/conf/forgejo-runner.yaml

and I put that in /home/protected/bin/forgejo-runner.sh

If you read the docs carefully, they should remind you to make that script executable by running chmod a+x /home/bin/forgejo-runner.sh

Set up the daemon in the UI

There should be an Add Daemon button under your site's dashboard. That'll take you to a screen where you need to enter

4. Get into action

That's it! The daemon should automatically run, which should automatically get requests from your Forgejo repo, which should trigger runs according to whatever action config files you have set up.

My action clones the repo, installs the npm dependencies, and then runs 11ty, outputting the results to /home/public/. Here's the action in its entirety (my label for the runner was nfsnet:host, yours might be different):

on: 
push:
branches:
- main
jobs:
deploy:
runs-on: nfsnet
steps:
- uses: actions/checkout@v5
- run: |
npm install && npx @11ty/eleventy --output=/home/public

This post is the first one automatically published using it—all I had to do was commit and push :)

Addendum: NearlyFreeSpeech.NET rocks

If you want get your feet wet with any kind of hosting/serving stuff, and you've never done anything like it before, I whole-heartedly recommend NearlyFreeSpeech.NET.

The name raised a red flag at first ("free speech" often seems most loudly championed by people who hate it the most), but their about page and their policies and all of the online interactions I've seen from them end up speaking for themselves. They do great work.

They have tons of documentation and explanations ranging from the most basic to insanely obscure. Their self-styled "change your own oil" analogy is incredibly apt, and they might just be the most helpful and down-to-earth "auto shop" on the internet. And, if you have the time and resources, I think it's well worth learning how to change your web site's oil.