Repository navigation
Docker
🌐 English · Deutsch
Official image: ghcr.io/pd-codes/mediaforge:latest (Debian-based, includes FFmpeg, mpv, Xvfb and Patchright Chromium for captchas).
Warning
NAS devices (UGREEN, Synology): Both are supported, but MediaForge's code is not optimized for these two NAS platforms and will not be — so expect recurring issues (Docker permissions, storage/DSM quirks, the captcha browser). We're happy to help with installation, but we do not optimize MediaForge for UGREEN or Synology. For the smoothest experience, use a standard Linux, Windows or macOS Docker host.
services:
mediaforge:
container_name: mediaforge
image: ghcr.io/pd-codes/mediaforge:latest
ports:
- "8080:8080"
volumes:
- ./Downloads:/app/Downloads # downloads on the host
- mediaforge-data:/home/mediaforge/.mediaforge # configuration & database
- mediaforge-tmp:/tmp/mediaforge # scratch space (see note below)
# security hardening
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp:mode=1777,size=512m
- /run:size=64m
restart: unless-stopped
volumes:
mediaforge-data:
mediaforge-tmp:Why the extra
mediaforge-tmpvolume: the/tmptmpfs lives in RAM, so it stays deliberately small (512 MB) and only holds small files — log file, sockets, the captcha browser profile fallback. Everything that can grow to gigabytes is written to/tmp/mediaforgeinstead: yt-dlp/ffmpeg download and encode intermediates, and the player's HLS segments, which for a seekable stream keep the segments of the whole title until the session ends (up to 8 concurrent sessions). Without the volume all of that lands on the tmpfs, fills your RAM and breaks logging and the captcha browser along with the download. Give the volume room for the largest file you download plus roughly the source size per concurrent viewer.
Important: Create
./Downloadsbefore the first start (mkdir -p Downloads) — otherwise Docker creates the folder asrootand you get permission errors (the container runs as the unprivileged usermediaforge).
On first start the setup token appears in the container log:
docker compose logs -f mediaforge
# → open http://<HostIP>:8080/setup?token=<token>Alternatively, create the admin account directly via environment variables (skips the setup page):
environment:
MEDIAFORGE_WEB_ADMIN_USER: "admin"
MEDIAFORGE_WEB_ADMIN_PASS: "changeme"All settings are configurable via the WebUI and stored in the database (volume mediaforge-data). Env variables are only useful for initial configuration/automation:
environment:
# SSO / OIDC (see Authentication)
MEDIAFORGE_WEB_SSO: "1"
MEDIAFORGE_WEB_FORCE_SSO: "1"
MEDIAFORGE_OIDC_ISSUER_URL: "https://keycloak.example.com/realms/myrealm"
MEDIAFORGE_OIDC_CLIENT_ID: "mediaforge"
MEDIAFORGE_OIDC_CLIENT_SECRET: "secret"
# reverse proxy
MEDIAFORGE_WEB_BASE_URL: "https://mediaforge.example.com"- The container runs
mediaforge -wP 8080 -wN -wH 0.0.0.0behind an Xvfb display (:99) — required so the captcha browser has a "real" display even headless. - Timezone/locale default to
Europe/Berlin/de_DE.UTF-8(overridable viaTZ/LANG) — realistic values improve the captcha success rate. - Built-in healthcheck: verifies every 30 s that the WebUI responds.
- Download path inside the container:
/app/Downloads(preset viaMEDIAFORGE_DOWNLOAD_PATH).
- Set
MEDIAFORGE_WEB_BASE_URLto the publichttps://URL (or in the UI under Settings) — this generates correct absolute URLs and enables secure cookies. - Register
https://<domain>/oidc/callbackas the redirect URI at your OIDC provider. - No WebSocket support needed; plain HTTP proxying is sufficient.
Some users route all of MediaForge's traffic through a Gluetun VPN container. MediaForge joins Gluetun's network namespace and inherits its kill switch — if the tunnel drops, MediaForge loses internet access too. A ready-to-use file for both variants below is in the repository: docker-compose.gluetun.yaml.
Whichever variant you use, the same two rules apply — and they are the usual stumbling blocks:
-
The port is published on the Gluetun container, not on
mediaforge. A container that joins another's network namespace may not declare its ownports:— Compose aborts with "conflicting options: port publishing and the container type network mode". Publish8080on gluetun instead. -
FIREWALL_OUTBOUND_SUBNETSmust list your LAN subnet (e.g.192.168.1.0/24). Otherwise Gluetun's firewall drops the reply packets and the WebUI stays unreachable from the local network — even though the container is running.
On startup MediaForge also logs a hint when it detects a VPN namespace (a
wg0/tun0-style interface), reminding you about the port and firewall-subnet settings — visible viadocker compose logs mediaforge. The image itself needs no changes — the built-in healthcheck (http://localhost:8080/) still works becauselocalhostrefers to the shared namespace.
The most common case: Gluetun already runs (in another stack or standalone) and you just want MediaForge to use it. Attach by container name with network_mode: "container:gluetun" — don't add a second gluetun service.
-
On your existing gluetun container, publish MediaForge's port and allow your LAN subnet, then recreate gluetun:
ports: - "8080:8080" environment: FIREWALL_OUTBOUND_SUBNETS: "192.168.1.0/24"
-
Run MediaForge on its own, joining gluetun's namespace by name (replace
gluetunwith your container's actual name).container:<name>references a container across Compose projects but has nodepends_onordering — if gluetun is recreated, restart MediaForge afterwards so it re-joins the new namespace.services: mediaforge: container_name: mediaforge image: ghcr.io/pd-codes/mediaforge:latest network_mode: "container:gluetun" volumes: - ./Downloads:/app/Downloads - mediaforge-data:/home/mediaforge/.mediaforge - mediaforge-tmp:/tmp/mediaforge cap_drop: - ALL security_opt: - no-new-privileges:true read_only: true tmpfs: - /run:size=128m - /tmp:size=256m restart: unless-stopped volumes: mediaforge-data: mediaforge-tmp:
Don't run Gluetun yet? Let Compose manage both in one file. Here MediaForge references the gluetun service with network_mode: "service:gluetun" (this only works within the same file) and can wait for it via depends_on: condition: service_healthy. Fill in the VPN_* / WIREGUARD_* values for your provider — the exact variable names differ per provider and are documented in the Gluetun wiki.
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "8080:8080" # MediaForge WebUI — published HERE
environment:
VPN_SERVICE_PROVIDER: "custom"
VPN_TYPE: "wireguard"
WIREGUARD_PRIVATE_KEY: "<your-private-key>"
WIREGUARD_ADDRESSES: "10.0.0.2/32"
FIREWALL_OUTBOUND_SUBNETS: "192.168.1.0/24" # your LAN subnet — REQUIRED
TZ: "Europe/Berlin"
restart: unless-stopped
mediaforge:
container_name: mediaforge
image: ghcr.io/pd-codes/mediaforge:latest
network_mode: "service:gluetun" # no "ports:" here!
depends_on:
gluetun:
condition: service_healthy
volumes:
- ./Downloads:/app/Downloads
- mediaforge-data:/home/mediaforge/.mediaforge
- mediaforge-tmp:/tmp/mediaforge
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /run:size=128m
- /tmp:size=256m
restart: unless-stopped
volumes:
mediaforge-data:
mediaforge-tmp:In docker-compose.gluetun.yaml the attach setup is active by default; this full stack is Option B at the bottom.
docker compose pull
docker compose up -dConfiguration and queue survive thanks to the mediaforge-data volume.
🇬🇧 English
Users
- Installation
- Getting Started
- Migration from AniWorld
- Configuration
- Web UI
- Download System
- Download History
- AutoSync
- Calendar
- Library
- Authentication
- Notifications
- Integrations
- SyncPlay
- Anime4K Upscaling
- Encoding
- Modules
- Theme Packs
- Backup
- Operations
- Audit Log
- Rules & Languages
- Docker
- Supported Sites
Developers
🇩🇪 Deutsch
Benutzer
- Installation
- Erste Schritte
- Umzug von AniWorld
- Konfiguration
- Web-UI
- Download-System
- Download-Verlauf
- AutoSync
- Kalender
- Bibliothek
- Authentifizierung
- Benachrichtigungen
- Integrationen
- SyncPlay
- Anime4K-Upscaling
- Encoding
- Module
- Theme-Pakete
- Backup
- Betrieb
- Audit-Log
- Regeln & Sprachen
- Docker
- Unterstützte Seiten
Entwickler