cove.town, atproto self-hosted self-hosting
cove.town consists of a PDS, a knot, a spindle, and a hold, and is fully built on itself. The source code is hosted on the knot, updates trigger workflows running on the spindle that build container images hosted on the hold, and the accounts that own the services live on the PDS.
while ago I shared cove.town as an experiment with atproto self-hosting. It’s driven by a bunch of little docker composes and contains a PDS, a knot, a spindle and a hold. The PDS owns the accounts, the knot owns the repo itself, the spindle builds the images it runs on, and the hold is the docker registry the composes point at. It’s a tiny little almost self-sufficient atproto ecosystem.
I’ve now shared write ups for how to get all these components set up, step by step, so I thought I’d do one final post tying it all together.
Building the images
The composes run on my Dokploy server, and each one points at a Docker image built on the spindle. I would love to use the newer microvm engine, but like most hosting providers, Hetzner (referral link for €20 off) does not provide access to /dev/kvm, which seems like a precondition for using microvm if you don’t want your workflows to take forever using qemu emulation. Instead I’m using the original nixery engine and I’m cheekily giving the workflow access to the docker socket I'm using kaniko to build the images without a docker socket! Anyway, we also have access controls for the spindle and the fact that non-members creating a PR does not run any workflows.
So let’s take a look at one. They’re all very similar so it doesn’t matter much which one. Here’s the PDS:
[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
So working my way through it, the when section specifies pushes to main only, and filters on the file changing. This means I only build new images when I change the workflow, or, more importantly, when I bump the version in the file, to match a new tranquil-pds release.
It defines a few dependencies that the nixery server provides for us. I'm self-hosting the server because of reliability issues with the Tangled one. Nixery is this magic tool that builds container images on demand, so that's how we get the environment set up.
Next, we're using kaniko to build the docker image in user space, no socket required, while sidestepping the problem with not having /dev/kvm and not being able to use microvm. The version of Kaniko we want is only distributed as a container image, so I use crane to unpack it. Note that Kaniko wipes the local disk when it runs, but you can protect directories you want to keep around with --ignore-path.
Once built, we have to log into atcr.io to push, passing an app password from the repo secrets. The account that pushes the images lives on the PDS. I override the default distroless image because my server is an ARM machine.
Finally, a webhook hitting my Dokploy instance to get the compose to pull the latest image. And that’s it, the compose owned by Dokploy, everything else on atproto. Take a look at the rest of the repo and the workflows. Most of the setups are more complex than the guides I’ve written, so feel free to copy and adjust.
Thank you for reading
And that’s it! That’s the end of the series. This has been an incredibly fun and educational project, and it’s just made me want to dig deeper into the infrastructure that underpins the ATmosphere and all these incredible services built on top of it.
ps I guess the only thing I’m missing now is a self-hosted component that runs composes for me, backed by on-protocol “deployments” or “stacks”. Would be cool if deploying was just writing to my repo…
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.