Deployed 2026-07-19 on Orange Pi 5 (RK3588S, 4GB, Armbian/Debian 12). Public at https://piefed.your-domain.com/ (example)


Prerequisites

  • Orange Pi 5 (or any Linux ARM64/x86 host)
  • Docker + Docker Compose (v2)
  • Domain/subdomain pointing to a Cloudflare tunnel (or any reverse proxy)

Step 1 — Install Docker

# Add Docker's GPG key and repo (Debian/Ubuntu)
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Add user to docker group
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect

Step 2 — Clone PieFed

git clone https://codeberg.org/rimu/pyfedi.git ~/code/python/pyfedi
cd ~/code/python/pyfedi

Step 3 — Configure Environment

cp .env.docker.sample .env.docker

Edit .env.docker:

  • SECRET_KEY — Generate with openssl rand -base64 42
  • SERVER_NAME — Set to your domain: piefed.your-domain.com
  • POSTGRES_PASSWORD — Change from default

The sample DATABASE_URL points to the Docker-internal db hostname (container name). This is correct — let Docker run its own PostgreSQL, don’t try to reuse the host’s.

Step 4 — Create Required Directories

mkdir -p logs media tmp

The compose file maps these as bind mounts for media uploads, logs, and temp files.

Step 5 — Fix Permissions (ARM64 gotcha)

The stock compose.yaml has user: "1000:1000" on the db service. This breaks on ARM64 because the container’s postgres user can’t write to its own data directory with that override.

Remove the user line from the db service in compose.yaml:

sed -i '/^    user: "1000:1000"/d' compose.yaml

This lets PostgreSQL run as its default container user, which can write to /var/lib/postgresql/data.

Step 6 — Build and Start

cd ~/code/python/pyfedi
sudo docker compose up --build -d

This builds three custom images (pyfedi-web, pyfedi-celery, pyfedi-piefednotifs) and pulls postgres:17, redis:6.2, and adminer.

On first startup, the web container runs database migrations automatically (Alembic), pulls the banned-instances list, and generates community data.

Services and their ports:

Container Host Port Internal Port Purpose
piefed_app1 8030 5000 Flask web app (Gunicorn)
piefed_notifs 8040 8000 Async notifications (Uvicorn)
piefed_celery1 Background task worker
pyfedi-db-1 5432 PostgreSQL 17 (internal)
pyfedi-redis-1 6379 Redis 6.2 (internal)
pyfedi-adminer-1 127.0.0.1:8888 8080 DB admin UI

Step 7 — Create Admin Account

The web UI won’t serve pages until a site record exists. Run the interactive setup:

cd ~/code/python/pyfedi
sudo docker compose exec web bash -c "export FLASK_APP=pyfedi.py && flask init-db"

You’ll be prompted for:

  • Admin username (avoid admin)
  • Email address
  • Password

This creates the site record, admin user, and populates the banned-instances list from the No-QAnon blocklist.

Step 8 — Verify Locally

curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:8030/
# Should return 302 (redirect to front page)

Step 9 — Expose via Cloudflare Tunnel

DNS Record

The subdomain must have a DNS record pointing to Cloudflare. In Cloudflare Dashboard → DNS → Records, add a CNAME for your PieFed subdomain targeting the tunnel.

Example (if using tunnel CNAME target):

Type: CNAME
Name: piefed
Target: your-tunnel-id.cfargotunnel.com
Proxy: Proxied (orange cloud)

Alternatively, point it to another working subdomain like web.your-domain.com — Cloudflare will route it through the same tunnel.

Tunnel Ingress Rule

In Cloudflare Zero Trust → Networks → Tunnels → select your tunnel, add an ingress rule:

Hostname Origin Service
piefed.your-domain.com http://localhost:8030/

DNS propagation can take a minute or two. Once it resolves, https://piefed.your-domain.com/ works.


Management Commands

# Start/stop
sudo docker compose up -d
sudo docker compose down

# View logs
sudo docker compose logs -f         # all services
sudo docker compose logs -f web     # just the web app

# Restart a service
sudo docker compose restart web

# Update to latest version
git pull
sudo docker compose down
sudo docker compose up --build -d
# Then re-run flask init-db if schema changed

# Access adminer (DB web UI)
# Open http://localhost:8888/ in browser (Pi desktop only)
# Server: db, User: piefed, Password: piefed, Database: piefed

Architecture Notes

  • PieFed uses its own PostgreSQL and Redis containers — completely separate from any host-level services (Polaris, etc.). No port conflicts because the DB/Redis containers only listen on the internal Docker bridge network.
  • No Nginx reverse proxy needed on the Pi — Docker exposes port 8030 directly. The Cloudflare tunnel connects to that.
  • 1.7 GB image footprint — the three custom images (web, celery, notifs) share a base Python 3.13 layer, plus postgres:17 and redis:6.2.
  • Build cache consumes ~2.7 GB during initial build but can be pruned later with docker builder prune.