A Pixelfed-compatible server that is one binary
GrainFed reads and writes Pixelfed's own database, speaks the same APIs, and federates under the same identity. Remote servers do not notice the change. Mobile apps do not log out. It is free software under the AGPL-3.0.
nginx + php-fpm + horizon + schedule:work
+ mysql + redis
./grainfed serve + mysql
(redis optional)
What it is
Pixelfed is a photo-sharing server for the Fediverse, and its data model is good. GrainFed keeps that model — the same tables, the same wire format, the same account identities — and replaces the runtime around it with a single static Go binary.
That means it is not "inspired by" Pixelfed and not a clone that imports your data. It is a different program driving the same database. You can stop it and start Pixelfed again, and Pixelfed carries on.
Where the project actually is. GrainFed serves all 913 of Pixelfed's servable routes, including the 280 that are a hard external contract — the Mastodon API, the Pixelfed-native API its mobile apps require, federation in both directions, the admin panel and the public pages.
It runs one production instance. That is one more than it ran a month ago and far fewer than Pixelfed runs. If you are considering it for a community you care about, read how the migration works first — particularly the part about giving the instance back.
What is different
One process
No PHP, no Composer, no Horizon, no supervisor, no cron entry. The job
queue runs in-process; grainfed worker moves it to its own
machine when it earns one.
Redis is optional
Every piece of user-visible state is reconstructible from the database
alone. FLUSHALL is a performance event, not a correctness
one.
Works without JavaScript
Every screen the web interface renders is also rendered by the server. No build step, no bundle, no framework.
iPhone photos work
HEIC, AVIF and WebP are decoded in-process and transcoded, so an upload straight from a phone becomes a post instead of a broken image.
Delivery is recorded
Every federation attempt keeps the peer's response — status, timing and the reason it failed. Pixelfed discards them, which is why "it just stopped appearing on Mastodon" is so hard to diagnose there.
Email is written down
Every message the instance tries to send is logged with what the mail server answered, and a button sends a test to yourself. "It says it was sent" becomes checkable.
A status page that measures
The database is queried, object storage has a file written and read back, the queue's oldest job is timed. Nothing on it is inferred from configuration.
Reversible tuning
grainfed optimize reports redundant indexes and adds ones
your queries want, records every change with the statement that undoes
it, and --revert puts the schema back exactly.
What is the same, on purpose
Compatibility is the whole point, so a few rules are treated as non-negotiable:
- Never
ALTERa Pixelfed table. GrainFed's own tables are allgf_-prefixed. This is what makes rolling back real, and a real rollback is what makes trying GrainFed reasonable. - The same identity. Your accounts keep their keys, their followers and their URLs. Nothing re-federates and nobody has to re-follow.
- The same tokens. Existing OAuth tokens keep working, so mobile clients stay logged in across the switch.
- Bug-for-bug where it counts. Where Pixelfed's behaviour is observable to a client or a peer, GrainFed reproduces it — including some things that are plainly bugs — because a difference on the wire is a difference somebody has already built against.
Open source
GrainFed is licensed under the GNU Affero General Public License v3.0, the same licence Pixelfed uses. If you run a modified copy as a network service, the AGPL requires you to offer its users the source of your modifications.
Pixelfed is the work of Daniel Supernault and its contributors, and GrainFed exists because of it: the schema, the API surface and the federation behaviour are all theirs. GrainFed is an independent project and is not endorsed by or affiliated with them.