Docker
These steps run the container image with Docker Compose. Check that the Compose plugin is installed with docker compose version.
Quick start
Save this file as compose.yaml in an empty directory:
name: drfed
services:
drfed:
image: ghcr.io/fedify-dev/drfed:0.2.0
environment:
# The origin browsers and other servers reach DrFed at. Instances are
# its subdomains, such as http://alice.drfed.localhost:8888.
DRFED_ROOT_ORIGIN: http://drfed.localhost:8888
# Print login links to the container log instead of sending mail.
DRFED_MAIL_LOG_CONTENT: "true"
ports:
- "127.0.0.1:8888:8888"
volumes:
- drfed-data:/var/lib/drfed
# Longer than DrFed's own 10-second shutdown deadline.
stop_grace_period: 20s
restart: unless-stopped
volumes:
drfed-data:Create your account, then start DrFed:
docker compose run --rm drfed drfed-account create \
--email alice@example.com --name "Alice"
docker compose up -d drfedReplace the email address and display name with your own. Open http://drfed.localhost:8888 and sign in with your email address. This local example prints the login link to the container log instead of sending mail:
docker compose logs -f drfedFollow the open http://drfed.localhost:8888/login/… link in the log to finish signing in.
docker compose run --rm drfed drfed-migrate --check runs drfed-migrate instead of the server; see how the image runs commands. run --rm reuses the drfed service definition, so management commands see the same environment, volumes, and database as the server. Keep database settings in that service.
PGlite maintenance
Only one process can open a PGlite data directory; see PGlite: one process at a time. Create the first account before starting the server, as in the quick start. For later maintenance, stop the server first:
docker compose stop drfed
docker compose run --rm drfed drfed-account create \
--email bob@example.com --name "Bob"
docker compose up -d drfedPostgreSQL
For long-running or shared deployments, use PostgreSQL. It lets you create accounts and check migrations while DrFed is running. This example keeps the database in its own named volume:
name: drfed
services:
postgres:
image: postgres:18
environment:
POSTGRES_DB: drfed
POSTGRES_USER: drfed
# Replace this password here and in DRFED_POSTGRES_URL below.
POSTGRES_PASSWORD: change-me
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD", "pg_isready", "--username=drfed", "--dbname=drfed"]
interval: 5s
timeout: 5s
retries: 12
restart: unless-stopped
drfed:
image: ghcr.io/fedify-dev/drfed:0.2.0
depends_on:
postgres:
condition: service_healthy
environment:
DRFED_ROOT_ORIGIN: https://drfed.example.com
DRFED_POSTGRES_URL: postgres://drfed:change-me@postgres/drfed
# Configure a mail transport for login mail, for example:
# DRFED_SMTP_URL: smtps://user:password@smtp.example.com
ports:
- "127.0.0.1:8888:8888"
# Longer than DrFed's own 10-second shutdown deadline.
stop_grace_period: 20s
restart: unless-stopped
volumes:
postgres-data:Replace the password in both places and configure a mail transport for login mail; see Configuration. Start both services, then create an account without stopping DrFed:
docker compose up -d
docker compose run --rm drfed drfed-account create \
--email alice@example.com --name "Alice"Run a single DrFed server per database; multiple server replicas are not supported.
Migrations
The server applies database migrations when it starts. To inspect or apply them without starting the server, run drfed-migrate through the same service:
docker compose run --rm drfed drfed-migrate --check
docker compose run --rm drfed drfed-migrate--check exits with 0 only when the database matches the image's migrations, and with 1 when it is uninitialized or has pending migrations. With PGlite, stop the server first.
A separate frontend
drfed-server serves the frontend by default. To run the frontend in its own container from the same image, layer this file over your compose.yaml as compose.web.yaml:
services:
drfed:
command: ["drfed-server", "--no-web"]
environment:
# The frontend's public origin, where login links point.
DRFED_LOGIN_ORIGINS: '["http://localhost:3000"]'
web:
image: ghcr.io/fedify-dev/drfed:0.2.0
command: ["drfed-web"]
depends_on:
- drfed
environment:
DRFED_PUBLIC_ORIGIN: http://localhost:3000
DRFED_BACKEND_URL: http://drfed:8888
# The backend is plain HTTP on the Compose network, not loopback.
DRFED_ALLOW_INSECURE_BACKEND: "true"
ports:
- "127.0.0.1:3000:3000"
stop_grace_period: 20s
restart: unless-stoppeddocker compose -f compose.yaml -f compose.web.yaml up -dThe frontend reaches the backend at http://drfed:8888 on the Compose network. Plain HTTP to a backend other than loopback needs DRFED_ALLOW_INSECURE_BACKEND, which is safe only on a trusted internal network. Set DRFED_LOGIN_ORIGINS and DRFED_PUBLIC_ORIGIN to the frontend's public origin.
Backups and upgrades
Data in the volume survives container replacement, including docker compose up -d after an image change. docker compose down --volumes deletes the volume and every account and instance in it.
To back up a PGlite volume to drfed-data.tgz, run this script next to your compose.yaml. It stops the server while it copies the volume and starts it again only if it was running. An earlier drfed-data.tgz is replaced only when the new backup is complete:
# Start the server again afterwards only if it is running now, and replace an
# earlier drfed-data.tgz only with a complete backup.
running=$(docker compose ps --status running --quiet drfed)
finish() {
if [ -n "$running" ]; then
docker compose up -d drfed
fi
rm -f drfed-data.tgz.partial
}
trap finish EXIT
trap 'exit 1' HUP INT TERM
docker compose stop drfed
docker compose run --rm -T drfed \
tar czf - -C /var/lib/drfed . > drfed-data.tgz.partial
mv drfed-data.tgz.partial drfed-data.tgzTo restore it into a new, empty volume:
docker compose run --rm -T drfed \
tar xzf - -C /var/lib/drfed < drfed-data.tgz
docker compose up -d drfedFor PostgreSQL, back up and restore the database with pg_dump and pg_restore.
To upgrade, back up your data, change the image tag in compose.yaml, then pull the new image and recreate the container. The server applies new migrations when it starts:
docker compose pull drfed
docker compose up -d drfedWith PostgreSQL, check the result with docker compose run --rm drfed drfed-migrate --check; with PGlite, stop the server first. To go back to an older version, restore the backup you made before upgrading.