Self-hosted portal to manage users, subscriptions, and invites for a personal Plex server.
1.8K
Self-hosted portal to manage users, subscriptions, and invites for a personal Plex server.
FastAPI + Jinja2/HTMX + SQLite, shipped as a single Docker container that sits behind your reverse proxy.
| Dashboard | Users |
|---|---|
![]() | ![]() |
| Collect | Reports |
|---|---|
![]() | ![]() |
Rame (copper) theme by default; Inchiostro and Muschio variants also available from Settings:

.po catalogs.Manages the lifecycle of a Plex server's users: who has access, what plan they're on, when it expires, who collects the payment. The idea is to stop having to track "who owes me and when" by hand.
Every regular user has a manager (an Admin or Moderator): the person who brought them in and collects their payments. This way multiple people can manage their own users under the same server, each one seeing only their own.
By default only two special plans exist:
Paid plans (with custom price and duration) are created by the SuperAdmin as needed from the Plans page — there are no predefined ones.
1. Adding a new paying user
2. Renewal (two-step)
3. Expiration reminders (automatic)
4. Reports
5. Telegram
Other automations: audit log of every action, soft-delete with orphan protection, nightly backup of the SQLite database.
The image is published on Docker Hub (public repo): pantanet96/accessflow.
cp .env.example .env # fill in the secrets, never commit the real .env
docker compose up -d
App at http://localhost:8000, behind your reverse proxy (NPM / Traefik / Caddy).
Health check: GET /healthz.
docker-compose.yml:
services:
app:
image: pantanet96/accessflow:latest
env_file: .env
ports:
- "8000:8000"
volumes:
- appdata:/data
restart: unless-stopped
volumes:
appdata:
Upgrading: docker compose pull && docker compose up -d.
The image honors
X-Forwarded-Proto(--proxy-headers), so behind an HTTPS proxy URLs come out ashttps.
Ships a Community Applications template → docs/UNRAID.md.
APP_SECRET_KEY is mandatory (random, ≥32 chars) — the app won't start without one./profile.FORWARDED_ALLOW_IPS to your reverse proxy's subnet — never * on a directly exposed app.Full details → docs/SECURITY.md.
python -m venv .venv
. .venv/Scripts/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt # prod deps + pytest (prod uses requirements.txt)
export DATABASE_PATH=./data/app.db # avoids the container's /data path
uvicorn app.main:app --reload
pytest
Everything via environment / .env — see .env.example.
Every request the app makes to Plex carries AccessFlow as product, device and
device name, so plex.tv → Settings → Authorized Devices and Tautulli's logs
name the app instead of Linux + the container hostname. The platform version
is left alone and reports the host kernel release (a container shares the host
kernel), so you end up with e.g. AccessFlow / 6.6.78-Unraid.
Running more than one instance against the same Plex account? Give each a distinct device name so you can tell their tokens apart when revoking one:
services:
app:
environment:
PLEXAPI_HEADER_DEVICE_NAME: "AccessFlow-staging"
Leave PLEXAPI_HEADER_PRODUCT as is — Plex and Tautulli group activity by
product, and changing it splits your own history in their dashboards.
Source strings are in English. Translations live in app/translations/<locale>/LC_MESSAGES/messages.po.
After changing templates/strings:
pybabel extract -F babel.cfg -o messages.pot .
pybabel update -i messages.pot -d app/translations
# edit the .po files, then:
pybabel compile -d app/translations
The Docker build compiles the catalogs automatically.
The web container also runs, in-process: a daily APScheduler job at the NOTIFY_HOUR
hour (expiration scan and reminders, auto-suspension, expired invites, manager digests),
Plex reconciliation and DB backups every few hours, and the Telegram bot in polling mode.
Dates, reminders and report months follow the TZ timezone.
They're toggled with ENABLE_SCHEDULER / ENABLE_BOT.
Content type
Image
Digest
sha256:805d7b8cd…
Size
75.7 MB
Last updated
11 days ago
docker pull pantanet96/accessflow