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
- Linux, x86-64 or ARM64, on glibc (Debian, Ubuntu, Fedora,
Arch and the like; not Alpine). The binary is one file with nothing to
install. For images it uses the system's
libwebp,libheifandlibavifwhen they are there, and falls back to copies built into it when they are not. - MySQL 8 or MariaDB 10.4+, holding a Pixelfed schema. GrainFed does not create that schema itself: it adopts one.
- A reverse proxy terminating TLS — nginx, Caddy, or whatever you already run. GrainFed listens on plain HTTP on localhost.
- Redis is optional and never authoritative.
Get the binary
curl -LO https://dl.grainfed.waxquixotic.com/grainfed-0.2.0-linux-amd64.tar.gz
curl -LO https://dl.grainfed.waxquixotic.com/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS
tar xzf grainfed-0.2.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. The published binary
is 0.2.0, which has everything on
what's new.
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.
| Variable | What it does |
|---|---|
GRAINFED_LOG_LEVEL | debug, info (default), warn, error. The federation queue logs every delivery attempt and its backoff at debug. |
GRAINFED_STORAGE | The Laravel storage/ directory, when it is not beside the .env. |
GRAINFED_MAX_EDGE | Long 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_MS | Log any query slower than this. Off unless set. Start at 30. |
GRAINFED_ALLOW_PRIVATE_DELIVERY | Permits 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_TRUSTED_PROXIES | Which peers are your own reverse proxies, so each person’s real address, not the proxy’s, is recorded at sign-up and used for lockouts and limits. Default loopback,cloudflare: nginx on the same host, behind Cloudflare. Takes addresses, CIDRs, loopback, private, linklocal, cloudflare, or none. |
GRAINFED_PUBLISH_DOMAIN_BLOCKS | Whether /api/v1/instance/domain_blocks answers the public. Off unless set, as in Pixelfed. |
GRAINFED_ALLOW_HTTP_DELIVERY | Permits 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. |
Settings in the admin panel
GrainFed's own settings that are not environment variables live in
System settings and are stored in its own gf_*
tables, never in Pixelfed's configuration cache. The first is
Performance: how long the instance's counts (people, posts,
monthly-active) are kept before they are counted again. Two hours is
recommended, and it is the default. Pixelfed itself keeps them for twelve
hours. A busy public instance that wants fresher figures can lower it; a tiny
one on a slow disk can raise it, up to a day.
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 @[email protected]
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.