The cheetah that outran the mastodon.

abuuba is a fediverse server written in Elixir and Phoenix. It speaks Mastodon's protocols: ActivityPub for federation and the complete Mastodon client API, all 289 endpoints, so existing apps work unchanged. Think of it as a drop-in replacement for Mastodon. It takes an instance over in place, keeping the domain, the accounts, the posts and the tokens on people's phones. It exists for one reason: Mastodon is too slow. I needed a fast peer to test my ultrafast business network https://vutuv.de against, and Mastodon could not keep up.

Version 1.0. Do not run this in production. It has bugs nobody has found yet, and it has never run a real community. Put it on a lab machine, break it, and tell me what broke.

68× fasterFan-out to the last of 10,000 followers
Same machine, same data, same PostgresabuubaMastodonResult
Home timeline p507.37 ms85.83 ms12×faster
Home timeline p9917.81 ms292.10 ms16×faster
Public timeline p505.33 ms76.71 ms14×faster
Public timeline p9911.45 ms204.23 ms18×faster
Fan-out to the last of 10,000 followers830 ms56,874 ms68×faster

Medians of three runs on 28 August 2026 against Mastodon v4.4.7, both servers on one bare-metal box: 2× AMD EPYC 9254, 96 threads, 1.0 TiB DDR5, Debian 13, Postgres 17.10 on both sides from the same untuned image. How it was measured.

A smaller server footprint.

What decides how many a host can carry is a different set of numbers, and they compound.

Per instanceabuubaMastodonResult
Memory, idle725 MB1,175 MB1.6× smaller
Restart, until it answers again2,695 ms4,816 ms1.8× faster
Docker containers252.5× fewer

Less JavaScript is better.

Less JavaScript means fewer potential security problems to take care of. And more speed.

453abuuba · lines of JavaScript · 0 npm dependencies
97,209Mastodon · lines of JavaScript · 1,428 lockfile entries

Different all the way down.

Speed is why abuuba exists, but it is not the only thing that differs. The licence is the one to read first: MIT lets you build on this and keep your changes to yourself. The AGPL asks anyone running a modified copy to offer its source to the people using it. Neither is the wrong answer, and which one you want is worth deciding before you pick a server rather than after.

What each one is made ofabuubaMastodon
LicenceMITAGPL-3.0
LanguageElixir 1.20, functionalRuby 3.4, object-oriented
Web frameworkPhoenix 1.8Rails 8.0
Background jobsOban 2, in PostgresSidekiq 7, in Redis
Full-text searchPostgresElasticsearch, optional

Read from each project on 29 August 2026: abuuba's .tool-versions and mix.lock, Mastodon's .ruby-version and Gemfile.lock at v4.4.7.

Everything Mastodon does. Then the speed.

abuuba is a replacement, not a companion. Nobody has to give anything up to move.

  • The whole client API. All 289 endpoints Mastodon declares under /api/v1 and /api/v2 are answered here, and a test fails the build the day one stops being answered. An app pointed at this server cannot tell which one it is talking to, lists and all.
  • The features around it. Polls, content warnings, scheduled posts, keyword filters, direct messages, two-step sign-in, CSV and archive import and export, and moving an account to another server with the aliases that make a move work. The admin area too: reports, appeals, roles, relays, domain blocks, webhooks, announcements, custom emoji, trends, an audit log.
  • The behaviour between servers. A checklist cannot prove federation, so 32 scenarios drive follows, boosts, replies, edits, deletes, polls, quotes, media, reports and account moves against real Mastodon 4.4.7 and GoToSocial 0.19.1 containers, every night.

Not for the faint of heart.

This is a 1.0. It has bugs, and nobody knows yet where they are. Mastodon has had nearly ten years of strangers finding its bugs for it; abuuba has had me. The documentation is not as good as Mastodon's either.

Do not put abuuba into production right now. Not under a community you would be sorry to lose, not under accounts somebody depends on. Run it in a lab, break it, and tell me what broke.

If you are new to running a fediverse server, run Mastodon. It is good software, it is documented by thousands of people, and when it breaks at two in the morning somebody has already written down what to do.

Try abuuba if you need the speed and an Elixir stack trace is something you can work with. Even then it is a toy for now, on a machine where losing everything costs you nothing.

Already running Mastodon? Take the server over.

Don't use abuuba right now. It is not as mature as Mastodon. Only use it in your lab and to play around with it.

Nothing has to be rebuilt. A Mastodon instance becomes an abuuba instance in place, keeping the domain it already has. That domain is the one thing a takeover cannot change: every post URL and every signature the old server published names it. Everything else moves. Accounts keep their passwords, posts keep their permalinks, followers stay followers, and the app on somebody's phone stays signed in, because the token it holds comes across too. Media, lists, filters, blocks and the whole moderation history follow.

The importer is a dry run by default. It reads the old database, checks everything that has to be true, counts what would move, and writes nothing. A run that gets interrupted continues where it stopped, and afterwards a verify pass asks the old server, while it is still up, whether every post arrived and every key still signs.

Step by step, with the Docker image

The reference deployment is two containers, abuuba and Postgres, in docker-compose.yml. Three things have to reach the container and none of them is there by default: the old database over the network, the old media directory as a mount, and the old server's keys. All three are in that file as a commented block. Uncomment it, put the values in .env, and the rest is one command with different arguments.

# 1. Fill in .env and uncomment the takeover block in docker-compose.yml.

# 2. Create abuuba's own database, the one the import writes into.
docker compose run --rm abuuba bin/abuuba eval 'Abuuba.Release.migrate()'

# 3. The dry run. It writes nothing. Read what it says before going on.
docker compose run --rm abuuba bin/abuuba eval 'Abuuba.Release.import_mastodon()'

# 4. Back up both databases. Step 5 cannot be undone.

# 5. The import. Hours on a large instance, and safe to run again if it stops.
docker compose run --rm abuuba bin/abuuba eval 'Abuuba.Release.import_mastodon(execute: true)'

# 6. While the old server is still reachable.
docker compose run --rm abuuba bin/abuuba eval 'Abuuba.Release.import_mastodon(verify: true)'

# 7. Point the domain here and start serving.
docker compose up -d

run --rm and not exec, because the import is a job of its own and has to work before this instance has ever answered a request. The container is thrown away afterwards; what lets an interrupted import carry on is rows in the database, not anything in that container, so step 5 can be repeated as often as it takes.

Two things only bite inside a container. localhost in MASTODON_DATABASE_URL means the container, not the host, so the old Postgres needs an address both can see: the host's address on the network they share, or Mastodon's service name if it runs in Docker too. And MASTODON_MEDIA_ROOT is the path inside the container, the right-hand side of the mount, never the host path.

Back up before step 5. It is not reversible: it writes into abuuba's database, and the way back from an import that went wrong is restoring that database from before the run. The old instance is only ever read, so keep its backup until the verify pass comes back clean.

Running from source instead of the image? The same three steps are mix abuuba.import, --execute and --verify.

What moves, what is deliberately left behind, and the two things not carried yet: Taking over a Mastodon instance.

Why abuuba exists.

I reinvented a perfectly good wheel. Here is why.

I am the founder of vutuv, a business network: an open source LinkedIn alternative that talks to the fediverse as well. It is fast on purpose, and testing it means driving federation through a real ActivityPub peer every day. A peer that cannot keep up drags the whole loop down to its own speed, and for a long time that peer was Mastodon. Seeding it with a thousand followers and two hundred posts took fifteen minutes. abuuba does the same in thirty-nine seconds.

That is not a complaint about Mastodon, and not one about Ruby on Rails. I love Rails. It is a pleasure to write and it has earned every bit of its reputation, and I wrote books about it. But Phoenix and Elixir are faster, and not by a little. The BEAM was built to run one system across several machines: nodes find each other and pass messages as ordinary work, with no Redis in the middle and no separate job runner to keep in step. Rails reaches the same place by adding those pieces and then keeping them alive.

So I wrote abuuba. The wheel was perfectly good. It was just too heavy for the cart I was building.

Stefan Wintermeyer

Written with agents.

One person built this, and that sentence is only honest with the next one: it was written with AI coding agents, every day, at a volume I could not have typed.

The speed is the least interesting part of it. What changed is that the tedious correct thing now happens every time, including on the days I would have talked myself out of it.

  • 4,387 tests, written before the code rather than after it. "Test first" is a rule an agent keeps and a tired human negotiates with.
  • 32 federation scenarios driven against real Mastodon and GoToSocial containers every night. Nobody enjoys writing the twenty-ninth one.
  • Ten user-guide pages and ten German translations of them. A page changes in the same commit as the behaviour it describes, or the commit does not land.

What it still needs me for: deciding what to build, what to refuse, and which of two working designs is the one to keep. An agent will do the wrong thing thoroughly and cheerfully. Every architectural call here is mine, and so is every mistake.

Not here to kill Mastodon. To push it.

This is the nginx and Apache story. Apache was good software and still is; nginx turned up because a different shape of load arrived and an architecture built for the older one could not bend that far. The part worth remembering is what happened next: nginx did not kill Apache, it made Apache faster and better.

That is what I hope abuuba does for Mastodon. If a server twelve times quicker on the same hardware pushes Mastodon to close that gap, the fediverse wins twice, and I will count that as the best thing this project ever did.

Stefan Wintermeyer

Why the name.

During the time mastodons lived, roughly 5 million to 10,000 years ago, one of the fastest mammals in North America was Miracinonyx, the so-called American cheetah. So the project was almost called Miracinonyx. But who can remember that?

A stack of registered but unused domain names later, it was abuuba. The cheetah stayed in the logo.

Specimen

Written in
Elixir, Phoenix, LiveView
Runs on
the BEAM, one application and one Postgres
Speaks
ActivityPub, the complete Mastodon client API (289 endpoints)
Verified against
Mastodon 4.4.7 and GoToSocial 0.19.1, 32 scenarios
Licence
MIT
Version
1.0, and not ready for production

abuuba and Postgres. Nothing else.

No Redis, no worker process, no cron, no Node. Scheduled work runs inside the server that answers requests.

# development
mix setup
mix phx.server

# production
cp .env.example .env
docker compose run --rm abuuba bin/abuuba eval 'Abuuba.Release.migrate()'
docker compose up -d

Run it yourself. Or ask for help.

News lands on the fediverse.