I've written before about picking sides in the protocol wars. My position hasn't really shifted: I'm an AT Protocol person first, a self-hoster by compulsion, and only tangentially a fediverse participant. But "tangentially" is doing a lot of heavy lifting in that sentence, because somewhere between setting up Moonstone and getting increasingly annoyed at Mastodon's institutional anti-Bluesky agenda, I went and stood up my own ActivityPub server.
Two attempts, actually — though only one survived.
This is how these things always go.
The "again" in the title is doing real work, by the way. I was on the fediverse before Bluesky — Mastodon, a few different instances, the whole thing. I left because the community friction eventually wore me down: the instance politics, the gatekeeping, the peculiar hostility towards anything that wasn't Mastodon-shaped. Bluesky was a relief by comparison, and I settled there and largely didn't look back. Coming back to ActivityPub now, running my own server rather than depending on someone else's, feels meaningfully different — less like returning to a place I didn't enjoy and more like revisiting it on my own terms.
Why bother at all?
Let me get the obvious question out of the way: if I'm this committed to AT Protocol, why am I spending time on ActivityPub infrastructure?
Honestly? Mostly because I don't want to be entirely absent from half the decentralised social web. ActivityPub is a real, federated protocol with real people on it, and there are interesting accounts across various instances that don't have a Bluesky presence and probably won't. My objections were always more to Mastodon's dominance over the ecosystem — the community tribalism, the territorial stance towards everything that isn't itself — than to the protocol underneath it. Running my own thing sidesteps most of that. Nobody else's instance culture to absorb. Nobody else's admin decisions to inherit. Just a server, federating quietly.
There's also the fact that I run a server anyway. At a certain point, spinning up another service is just another NixOS module. The marginal cost of adding something to an existing flake configuration is low enough that "why not?" becomes a reasonable answer to most questions. I'm already managing Jellyfin, Immich, Nextcloud, Forgejo, Vaultwarden — one more service is barely worth remarking on. (He said, lying to himself.)
That said, I wasn't about to run Mastodon itself. I've seen the hosting costs (managed hosting for a small instance runs anywhere from pocket change to surprisingly not-pocket-change depending on how much of the fediverse your users follow), and more to the point I've seen the storage accumulation problem, which is chronic and largely unsolved — Mastodon federates aggressively and caches everything, and the built-in retention tools are blunt enough that the masto.host docs actively warn against using them — and more importantly, I've seen the codebase, the dependency list, and the operational surface area. Mastodon is a full Rails application with Sidekiq, Redis, Elasticsearch if you want search, and enough moving parts that keeping it healthy feels like a part-time job. No thank you.
So the question became: which of the lighter alternatives?
GoToSocial: the sensible starting point
My first attempt was GoToSocial. If you're not familiar: it's a lightweight, ActivityPub-compatible server written in Go, designed explicitly for small or single-user instances. No Elasticsearch dependency. No Sidekiq. No Redis. It just runs, which is a genuinely underrated quality in self-hosted software. The binary starts, it federates, it does what you expect. It's the kind of software that respects your time.
The NixOS module was straightforward enough. GoToSocial has a fairly simple configuration surface — SMTP for notifications, a database (SQLite works fine at this scale), and some basic federation settings. I stood it up at ap.ewancroft.uk, fronted it with Caddy, and routed traffic through my Cloudflare Tunnel setup the same way everything else is exposed.
The account-domain separation trick is worth a mention here. GoToSocial supports configuring your account domain separately from the host domain, which means my handle on the fediverse is @ewan@ewancroft.uk while the actual server lives at ap.ewancroft.uk. It's the ActivityPub equivalent of using your own domain as a Bluesky handle — a small thing that makes the whole experience feel more intentional rather than just another @user@instance.social floating around.
In theory, this also means my presence should resolve correctly whether you use @ewan@ewancroft.uk or @ewan@ap.ewancroft.uk. Both should work — and unlike a simple redirect, the WebFinger endpoint on my Vercel-hosted website is a proper proxy: it fetches the canonical WebFinger from the Sharkey instance, injects the ewancroft.uk alias into the response, and explicitly handles both handle forms as known resources. It even normalises the malformed acct:@user@host URIs that some fediverse clients send instead of the correct acct:user@host form. So the implementation is more considered than "point a DNS record somewhere and hope." The "should" is still warranted, though, because it involves two separate systems — the NixOS server and the Vercel deployment — agreeing with each other across a proxy hop, and that is exactly the kind of arrangement that quietly breaks in interesting ways when you least expect it.
The setup wasn't entirely smooth, of course. Nothing ever is.
The main headache was trusted proxies. GoToSocial needs to see the real client IP to function properly, but with Cloudflare Tunnel in front of everything, requests arrive at Caddy with the actual visitor IP buried in a CF-Connecting-IP header. The fix was a couple of header_up directives in the Caddy vhost block, injecting that header as both X-Forwarded-For and X-Real-IP so the server could trust it. Once that was sorted, federation worked correctly and the OAuth flow stopped doing anything embarrassing.
I also wrote a bulk emoji upload script — imaginatively named gts_emoji.sh — because of course I did. It uses fzf for multi-select, handles rate limiting with retry loops, has ffmpeg integration for resizing anything too large, and a --clean flag for when you've inevitably uploaded something wrong. There's probably more engineering in that script than in some production systems I've encountered. These are the things I spend my evenings on, apparently.
GoToSocial did what it said on the tin. Stable, low-maintenance, federated properly with the rest of the network. For a minimalist ActivityPub presence it's genuinely excellent, and if you want a single-user server you can basically forget about after setup, it's the obvious choice. I ran it happily enough.
Then I noticed there was no quote post support. And that was more or less that.
It's not listed as a deliberate design choice so much as simply not implemented yet — the GTS team have noted it as something they'd like to look at, and their own documentation recommends Akkoma as an alternative partly on the basis that Akkoma has quote posts and emoji reacts. Which is a gracious way of saying: we don't have those things. For a lot of people that's fine. For me, quote-posting is a basic part of how I participate in a conversation, and doing without it felt like trying to hold a discussion with one hand tied behind my back. So I went looking.
Akkoma was the obvious first candidate given the GTS docs practically hand you a referral link. I've used it before though, and the interface just doesn't sit right with me — it's functional, and the Pleroma lineage gives it a certain scrappy flexibility, but there's something about the overall feel of it that I've never been able to warm to. So Akkoma was out. Which left the Misskey forks, and eventually the decision to migrate to Sharkey rather than run both. The process was about as surgical as it sounds: I literally transplanted the account identity data from one database to another of a completely different type — GoToSocial runs on SQLite, Sharkey on PostgreSQL — hauling the RSA keypair across so the fediverse would keep seeing the same cryptographic identity rather than waking up to find a stranger where the old server used to be. (That's a direct quote, more or less, of how I described it to raichu, a good friend of mine on the same IT course — he seemed appropriately impressed, which I appreciated.) "Forget about it" stopped feeling like a virtue.
One thing worth calling out clearly if you're considering a similar migration: do not reuse the subdomain. When you decommission an ActivityPub instance, that subdomain's history doesn't disappear — other servers across the fediverse have cached its behaviour, its keys, its actor records. Standing up different server software at the same address creates federation issues that don't resolve cleanly, because the network's trust model is built on the assumption that a given domain corresponds to a consistent, continuous identity. The keypair transplant preserves your cryptographic identity, but the subdomain itself carries baggage the new server inherits whether it wants it or not. Use a fresh subdomain for the new instance.
Sharkey: unexpectedly fun
Sharkey is a fork of Misskey — which is itself one of the more interesting corners of the fediverse, a Japanese-origin platform with a feature set that puts Mastodon to shame in several respects. Sharkey is the fork that various communities have coalesced around as a maintained, actively developed alternative, with its own identity and direction rather than just being a downstream of whatever Misskey upstream decides.
The first thing you notice is that Sharkey has personality. Misskey and its forks have always felt different from Mastodon — more playful, more feature-rich, less weighed down by ideological hand-wringing about what a fediverse client should and shouldn't do. Custom emoji are a first-class feature rather than something bolted on. The reactions system actually works. There's a proper drive for media. Posts can be long or short without the character limit anxiety that Mastodon imported wholesale from Twitter as though it were a design principle rather than a technical constraint.
Then there's MFM — officially "Markup language For Misskey", though most people assume it stands for Misskey Flavoured Markdown and the source code doesn't exactly discourage that reading — which is either charming or alarming depending on your disposition. It's a superset of standard Markdown that adds things like spinning text, rainbow gradients, shaking animations, and other effects that render natively in Misskey-family clients. On one level it's completely ridiculous. On another, it's exactly the kind of thing you don't know you wanted until someone posts something with a $[shake AAAAAA] and you find yourself mildly delighted. It doesn't render on Mastodon or most other ActivityPub clients — they just see the raw syntax as plain text — so it's not a universal feature by any stretch. But within the Misskey ecosystem it adds a liveliness that's hard to find elsewhere. Honestly, I find myself hoping Bluesky or Witchsky picks something like it up at some point. Not the full carnival of spinning rainbow text necessarily, but the principle of it — rich, expressive formatting that makes a post feel like it was made by a person rather than filed. It's just fun, and fun is underrated as a design goal.
GoToSocial, for all its virtues, is intentionally minimal. It's a server that federates and gets out of the way. It doesn't ship a web client worth speaking of — you're expected to use a third-party Mastodon-compatible client, and the experience is fine but somewhat detached. Sharkey has its own full web interface, its own posting experience, and enough features that you might actually use it as a daily driver rather than just having it sit there, federated and silent.
The resource footprint is heavier — Sharkey is a full-stack Node.js application with a PostgreSQL backend, which is a meaningfully different proposition from GoToSocial's self-contained Go binary. But it's not as heavy as Mastodon, and running it alongside everything else on the server is manageable. NixOS modules, persistent data under /srv, the usual shape of things.
I should be honest about the NixOS situation specifically, because it's a bit awkward. There is a services.sharkey module in nixpkgs — so you're not entirely on your own — but it covers the basics and then stops. A fair amount of setup isn't fully declarative even when you do get it running. Initial configuration, the first admin account, certain settings that live in the database rather than in a config file — these require manual intervention through the web UI or the CLI. It's not the end of the world, but if you're used to the "rebuild switch and everything is correct" reassurance of a properly declarative NixOS service, Sharkey will disappoint you slightly. It's more "declarative enough" than "properly declarative."
What sold me on it was the posting experience and the community assumptions baked into the software itself. Misskey-family platforms feel like they were built by people who actually enjoy using social media, rather than people who feel morally obligated to provide a Correct alternative to something bad. That's a subtle distinction, but it matters when you're using something day to day. Mastodon has always had this air of reluctant necessity about it — you're not here because it's good, you're here because the alternative is worse. Sharkey doesn't carry that weight. It's just a bit of fun.
The feature I wasn't expecting to care about: the Antenna system. Sharkey lets you create timelines based on keyword or hashtag filters across the federated network, which is a genuinely useful tool for following conversations rather than just accounts. It's the kind of feature that makes you wonder why it took this long to exist, and then you remember that Mastodon has been too busy fighting culture wars with other instances to ship things people actually want.
The one thing I'll flag as a genuine limitation: the mobile app situation is not great if you're coming from a western context. The Misskey ecosystem has always been more popular in Japan, and the app landscape reflects that. I've been using Kimis, which is fine, but it has a slightly janky quality and makes certain UI assumptions that feel like they were designed for a Japanese audience first and everyone else as an afterthought. There's Miria, a Flutter app for iOS and Android, which has Sharkey support — but it's also very clearly built for a Japanese-speaking userbase. IceCubesApp, which handles Mastodon and some other servers, has had reported login issues with Sharkey specifically. The web client works well enough in a mobile browser — but it's not the same as having a polished, purpose-built native app the way Bluesky does. If you care about that, it's worth knowing going in.
Two protocols, one server, questionable life choices
The interesting part of running both is watching how they reflect fundamentally different assumptions about what social networking is for and who it belongs to.
AT Protocol is radically user-centric. My data lives on my PDS — on a self-hosted instance of the official Bluesky reference PDS, specifically — Moonstone, my own PDS implementation, exists but it's a side project rather than anything I trust with my actual data. My identity is a DID that I control and that resolves independently of any application. If Bluesky PBC disappeared tomorrow, my data would still be there, my identity would still be mine, and I could move to any other ATProto AppView without losing anything. The whole architecture is built around that assumption: that the company running the interface might not be around forever, or might do something you don't like, and you should be fine either way. I've written about this at length and my feelings haven't changed.
ActivityPub is server-centric. Federation happens between instances, and your account is, at a fundamental level, tied to the server it lives on. GoToSocial and Sharkey both support account migration — you can move to another instance and take your followers with you — but it's a migration, not seamless portability. Your posts don't come with you. Your data doesn't travel the way it does in ATProto. If the server closes, the archive closes. I've been on the wrong end of that particular reality before, watching communities scatter when instances shut down without warning, and it's not a great feeling.
Running my own server mostly solves that problem in practice. The risk of the admin going rogue or the instance dying unexpectedly is lower when the admin is me, and when the instance is running on hardware I control. But it doesn't solve the architectural problem: the data is still server-bound in a way that ATProto's isn't. The weak point has moved, but it hasn't gone away.
That said — it works. People use it. There are communities and individuals across the fediverse that aren't on Bluesky and probably won't be, not because of any particular objection, but just because they're already there and settled. Having a presence on both protocols, even a quiet one, means I'm not entirely absent from half the decentralised social web. There's something to be said for that, even if the architectural elegance isn't there.
I've also got a fallback plan in place, should maintaining my own instance become untenable. First port of call would be sk.waf.moe — WAFkey, a Sharkey sister instance to Wafrn, run by jb, a mutual of mine. Wafrn is its own interesting thing — a federated, Tumblr-inspired platform that bridges both the fediverse and Bluesky, built by gabboman, who I'm mutuals with — and I've since actually gone and made an account on app.wafrn.net directly, so that bridge is no longer hypothetical. WAFkey as a Sharkey fallback remains the more appealing long-term option if self-hosting stops being viable, though. There's something reassuring about having an exit route that goes to somewhere run by someone you actually know, rather than a managed host you're trusting on reputation alone. Self-hosting is great until it isn't, and having thought through the exit route ahead of time is just sensible practice. (Ask me about the time a NixOS rebuild failure cascaded through three services because of a single stale lockfile. Actually, don't ask me. It's a sore subject.)
A brief, uncharitable word about Nostr
I'd be remiss not to mention Nostr, the third option that occasionally gets floated in "decentralised social" conversations, because it keeps coming up and somebody has to say something.
Nostr is technically a protocol for passing signed JSON events between relays. In practice it's a social network that smells strongly of cryptocurrency adjacent thinking — not because it uses a blockchain (it doesn't), but because of the particular flavour of libertarian self-sufficiency that permeates the culture and documentation. The key model is cryptographic keypairs rather than usernames, which sounds elegant until you lose your private key and discover that your entire identity is simply gone, with no recovery path, because decentralisation means nobody can help you. The relay model means your posts exist wherever you've chosen to publish them and nowhere else, which is either empowering or just annoying depending on how much you enjoy manually managing your social media infrastructure.
The client ecosystem is a bit of a mess, the user experience varies wildly, and the loudest voices in the community seem primarily interested in escaping financial regulation rather than building something people would actually enjoy using. I could be wrong. I don't think I'm wrong.
ActivityPub has its problems. AT Protocol has its rough edges. Nostr has its devotees. I am not one of them, and I mention it mainly so you know I considered it and then closed the tab fairly quickly.
Would I recommend it?
Depends on what you're after.
If you want an ActivityPub presence with minimal fuss and near-zero ongoing maintenance, GoToSocial is the right answer. It's solid, it's small, and it doesn't demand much of you once it's running. The account-domain separation works well if you care about having a clean handle. The configuration surface is sane. It runs quietly in the background and you can more or less forget it's there.
If you want something you'll actually open and use, look at Sharkey. It's heavier operationally, but it's more alive. The feature set is genuinely impressive by fediverse standards and the community assumptions feel healthier — less of the martyred "we're doing this because it's right" energy, more of just enjoying what they've built. If you're already self-hosting and not put off by Node.js, it's worth the extra overhead.
If you're wondering whether any of this is worth the effort when you could just have an account on an existing instance, the honest answer is: probably not, unless you're already self-hosting other things and the marginal work is low. Running your own server is an infrastructure commitment, even a small one. The reward isn't really features — it's ownership and control, which has value but isn't for everyone.
The people who should be running their own servers are, broadly, the people who find themselves reading posts like this one and nodding along. Which presumably includes you. Hello.
My AT Protocol PDS is still where most of my thinking happens, where my tools are anchored, and where the ecosystem I'm most invested in is developing. The fediverse is a secondary presence — somewhere to exist, somewhere to reach people who aren't on Bluesky, somewhere to experiment. But ap.ewancroft.uk is real, Sharkey replaced GoToSocial and has been running since, and I'm finding the whole thing more enjoyable than I expected when I started down this road.
Sometimes the right answer to "why do you have a fediverse server?" is just: because it's interesting, and I had a spare afternoon.
If you're already following me on Bluesky (@ewancroft.uk) but not on the fediverse, you can find me at @ewan@ewancroft.uk. If you're on the fediverse and not on Bluesky — well, firstly, consider it, but you can also just follow me there and I'll cross-post the things worth cross-posting. No obligation either way, but I'm on both now, so the option exists.