Documentation
Getting started
From nothing to a federating instance. This page covers the happy path and the two places people usually get it wrong: the domain names, and the reverse proxy.
These are the real commands, but you cannot clone the repository today. Read this as a description of the interface rather than a recipe you can run now. The documentation index says what changes when the code lands.
Build it
Go 1.26 or newer is required to build from source. Nothing else is.
make build
./littlefedi init # choose "instance"
./littlefedi --config littlefedi.toml
The result is one static executable with CGO disabled. It has no shared library dependencies, no interpreter, and no external database. You can build it on one machine and copy it to another of the same architecture.
Run the wizard
littlefedi init asks the questions that are painful to answer later:
single-user or multi-user, the domains, the database, where media lives, whether
to join littleMesh, and the address of the reverse
proxy it should trust. It writes a complete littlefedi.toml, creates
the first administrator, and comments out everything you declined so the file
still documents it.
Then open http://localhost:8080 and sign in with that
administrator.
init already asked for the web_domain other
instances will use. Federation needs that domain served over public HTTPS, not a
direct connection to port 8080. Nothing federates until a proxy is in front.
Two domains, one server
Existing single-domain configurations may keep using domain. New
installations should use both:
local_domain = "example.com"
web_domain = "social.example.com"
local_domain is the domain in account handles, the thing people
type. web_domain serves the web interface, the API, media, and the
ActivityPub objects. When the two differ, the local domain must redirect
WebFinger requests to the web domain, or nobody can find your accounts.
Pick these before you federate. Changing the actor domain of a live instance means every remote server holds a cached identity that no longer resolves, and there is no clean way back.
Put a proxy in front
littleFedi listens on loopback and does not terminate TLS itself. Configuration
examples for nginx and Caddy ship in the packaging/ directory,
along with the littleMesh gateway files.
- Point A and AAAA records at the proxy and test both address families. If IPv6 is not routed and firewalled properly, do not publish an AAAA record.
- Set
http.proxy_urito the exact public HTTPS origin. - Set
http.trusted_proxiesto the proxy's address. In the usual single-host topology that is loopback and nothing else. Never put broad internet ranges in there. - Permit inbound TCP 80 and 443 only. Keep port 8080, the metrics endpoint, the profiler, the database and SSH off the public network.
- Use an automated ACME client and alert 21 days before expiry.
Then check the three endpoints that tell you it worked:
curl --fail http://127.0.0.1:8080/health
curl --fail http://127.0.0.1:8080/readyz
curl --fail https://social.example.com/.well-known/nodeinfo
Fill in the instance profile
init offers to write the one-line and paragraph descriptions, the
moderation contact and the server rules. That is what /about and
every Mastodon client show about your server, and an empty one looks abandoned
before anyone has read a post.
You can also do it later from the admin console, without a restart. The config file is the seed and the console is the override; see the instance profile for how the two interact, including the one field where they do not.
Email and SMTP TLS
SMTP defaults to STARTTLS on port 587. Use implicit_tls = true for
SMTPS, normally on port 465. Certificate-chain and hostname verification stay
enabled in both modes, and there is an administrator SMTP test in the console
that only ever sends to your own address.
A private relay with a self-signed certificate can opt out with
insecure_skip_verify = true or
LITTLEFEDI_SMTP_INSECURE_SKIP_VERIFY=true. This accepts
any certificate on that connection, not only the one you had in mind.
Use it on a trusted network, log the fact, and replace it with a valid
certificate. A startup warning is logged for as long as it is on.
Small machines
On a Raspberry Pi Zero W or anything of that size, start here:
low_power = true
[media]
cache_remote = "off"
The preset selects one database connection and one queue worker, a bounded memory cache, one image decoder, a 13 million pixel bound on decoded images (enough for a 4032 by 3024 phone photo), a 1600 pixel cap on stored uploads, cheaper thumbnail resampling, and no dynamic gzip.
Measure on the actual device before making capacity claims. The preset makes the server fit; it does not make the hardware faster.
Other build variants
make build-postgres
make build-s3
make build-postgres-s3
make build-blog
Build tags keep optional dependencies out of the default binary:
postgres adds PostgreSQL storage and cross-process streaming,
s3 adds S3-compatible media storage and migration, and
blog adds static blog generation. If you
do not ask for them, they are not linked in.
Your first post
printf 'Hello from a new instance.\n' | \
./littlefedi --config littlefedi.toml post
./littlefedi --config littlefedi.toml post --username alice \
--markdown --visibility private post.md
Then follow someone remote from the web interface, wait for them to appear in the home timeline, and check the queue drained. That round trip is the real smoke test: everything before it only proves the process starts.
Before you invite anyone
- Keep registrations closed until somebody is willing to moderate.
- Enroll TOTP for the administrator, store the recovery codes offline, then
turn on
require_admin_mfaand confirm you can still get in. - Publish rules, a moderation contact and terms. The policy checklist lists what they should cover.
- Take a backup and restore it somewhere else. A backup you have never restored is a belief, not a backup.
- Read the deployment guide for the service account, the file modes, the descriptor limit and the alerts.