GrainFed

Install

One binary, one database, one reverse proxy. If you already run Pixelfed, read Migrate instead — this page is for running the binary itself.

What you need

Get the binary

curl -LO https://dl.grainfed.waxquixotic.com/grainfed-0.1.0-linux-amd64.tar.gz
curl -LO https://dl.grainfed.waxquixotic.com/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS

tar xzf grainfed-0.1.0-linux-amd64.tar.gz
sudo install -m755 grainfed /usr/local/bin/grainfed
grainfed version

Replace amd64 with arm64 on ARM hardware. Both builds are on the download page.

Point it at an instance

GrainFed reads Pixelfed's .env for its database credentials, app key, storage paths and mail settings. Run it from the Pixelfed root, or pass -env.

Start by asking it what it thinks of the instance. This writes nothing — the database session is put into read-only mode at the server, and that is verified rather than trusted:

grainfed doctor

Then run it:

grainfed serve -env /path/to/pixelfed/.env -read-only=false -addr 127.0.0.1:8080

It defaults to a read-only database session. Pointing GrainFed at an instance you are only evaluating cannot write to it. You have to pass -read-only=false deliberately, and until you do, the server will refuse every write and say so.

As a service

[Unit]
Description=GrainFed
After=network.target mariadb.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/pixelfed
ExecStart=/usr/local/bin/grainfed serve -read-only=false -addr 127.0.0.1:8080
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Proxy to it, passing the real host through so federation URLs are built correctly:

location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host              $host;
    proxy_set_header   X-Real-IP         $remote_addr;
    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
    client_max_body_size 64m;
}

Settings of its own

Everything GrainFed adds is GRAINFED_-prefixed, so an adopted .env can never switch one on by accident. Every one of them is off, or set to Pixelfed's behaviour, unless you say otherwise.

VariableWhat it does
GRAINFED_LOG_LEVELdebug, info (default), warn, error. The federation queue logs every delivery attempt and its backoff at debug.
GRAINFED_STORAGEThe Laravel storage/ directory, when it is not beside the .env.
GRAINFED_MAX_EDGELong edge, in pixels, that uploads are resized to. Unset keeps Pixelfed's 1920×1080, which for an ordinary 4:3 photo means 1440×1080.
GRAINFED_SLOW_QUERY_MSLog any query slower than this. Off unless set. Start at 30.
GRAINFED_ALLOW_PRIVATE_DELIVERYPermits federation to private and loopback addresses. For a LAN lab — off by default, because the SSRF guard is what stops a peer turning delivery into a port scanner.
GRAINFED_ALLOW_HTTP_DELIVERYPermits plaintext http:// peers. A separate switch from the one above, because they are separate risks: a signed delivery over http leaks the whole activity — the signature authenticates it, it does not hide it.

Checking it rather than running it

grainfed parity     # how much of Pixelfed's surface is served
grainfed probe      # call every route; report the ones that cannot work
grainfed resolve @someone@example.social
grainfed optimize   # index changes that would make this instance faster

probe exists because registered is not the same as reachable, and this project has shipped that difference more than once. resolve exercises the whole outbound federation chain — WebFinger, the SSRF guard, the fetch budget, the actor validator — against your own configuration, which is otherwise only testable by waiting for a peer to send you something.

After it is running

Two screens in the admin panel are worth opening on day one. Settings → Status checks the database, object storage, Redis, the queue and mail by actually doing each thing, and reports where the database's memory is going. Mail log records every message the instance has tried to send and what the mail server answered.