Migrating from Pixelfed
There is no data migration, because there is no data to migrate. GrainFed opens the database Pixelfed was already using. What follows is a sequence of checks around one small, deliberate moment of downtime.
What does not change
- Your accounts. Same rows, same signing keys, same followers, same URLs. Nothing re-federates and nobody re-follows.
- Your media. Same files, same paths, same bucket.
- Your logins. Existing OAuth tokens keep working, so mobile clients stay signed in across the switch.
- Your schema. GrainFed never
ALTERs a Pixelfed table. Its own tables aregf_-prefixed and Pixelfed has never heard of them.
The five phases
Everything is dry-run until you pass --apply. Everything
before cutover is reversible, and cutover is deliberately the
smallest step — all the expensive work happens before it.
| Phase | What it does | Writes? |
|---|---|---|
inspect |
Runs every check and reports a verdict: whether this instance can be adopted, and what would stop it. | Nothing at all |
prepare |
Creates GrainFed's gf_* tables and records a baseline of exactly what the instance looked like beforehand. |
Its own tables only |
cutover |
Stops Pixelfed, verifies it is quiet, starts GrainFed. The point of no return. | Yes |
verify |
Checks that federation, tokens and media still work afterwards. | Nothing |
revert |
Puts it back. | Yes |
grainfed adopt --phase inspect
grainfed adopt --phase prepare --apply
grainfed adopt --phase cutover --apply
grainfed adopt --phase verify
Take a database backup before cutover.
Adoption is designed to be reversible and the baseline exists so that revert
can be exact, but a backup is the thing that is true regardless of whether
this software is correct.
Giving it back
Reverting is a supported operation and not an emergency procedure:
grainfed adopt --phase revert --apply
It stops GrainFed, restores what the baseline recorded, and starts Pixelfed again. Because no Pixelfed table was ever altered, Pixelfed finds the database it expects. Posts made while GrainFed was running are ordinary rows in the ordinary tables, so they are still there.
If you want the gf_* tables gone too,
--revert --purge drops exactly the ones adoption created, by
name, and leaves alone any that predate it.
Then make it faster
Adoption deliberately leaves the schema exactly as Pixelfed had it. That is what makes dropping GrainFed in — and taking it back out — possible. Optimisation is a separate step you run when you are ready, and can re-run later as the project learns about more:
grainfed optimize # report; changes nothing
grainfed optimize --apply # make the changes
grainfed optimize --revert # put them back exactly
It only ever changes indexes — never a column, a row or a table. An index changes how fast a query is answered and never what it answers, so an instance that never runs this is correct and merely slower, and Pixelfed keeps working against a schema that has run it. Every change is recorded before it is made, together with the statement that undoes it.
On the first instance it ran against, it found 37 indexes that could never be chosen — each one a leading prefix of another index on the same table, written on every insert for nothing — and two worth adding.
Things worth knowing first
- Pixelfed's job queue must be empty. A queued job is work Pixelfed intended to do and GrainFed will not; adoption refuses to proceed while any remain.
- Your
APP_KEYmust be intact. It decrypts sessions and several config values. An instance whose key has been rotated without re-encrypting is not adoptable, andinspectsays so. - Check your database's memory settings. Instances migrating from a stock install are frequently running MySQL's default 128 MB buffer pool against a database several times that, which makes everything feel slow regardless of which server is in front of it. Settings → Status reports it.
Be honest with yourself about the stage. GrainFed serves every route Pixelfed can serve and is verified against a real database, a real browser and real remote servers — but it runs one production instance. If you are moving a community that would be upset by a bad week, wait, or run it beside your real instance first.