Skip to content

Latest commit

 

History

110 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Flowarr

Flowarr

Self-hosted media library file transformation automation

Features • Quickstart • Architecture • Contributing

Tests Docker License

Flowarr automates media file transformations for private media servers running Jellyfin, Plex or Emby. It watches filesystem directories, runs configurable jobs (transcoding, subtitle extraction and conversion), and holds all processing while someone is streaming, or outside a time window you choose, so jobs never compete with playback for the GPU.

Built for the Steam Deck media server problem: transcode h.264 → HEVC for ~60% space savings, extract ASS/SSA subtitles to SRT sidecars for direct-play compatibility.

Features

  • GPU-Accelerated Transcoding: HEVC encoding via NVENC or VAAPI (AMD/Intel) with automatic detection and libx265 fallback; HDR sources are tonemapped to SDR; all audio, subtitle and attachment tracks are kept
  • Subtitle Extraction: Extract text-based subtitles (SRT, ASS, SSA, WebVTT, mov_text) to <name>.<lang>.srt sidecars, optionally stripping them from the video
  • Subtitle Conversion: Convert ASS/SSA/WebVTT subtitle files to SRT without ever overwriting an existing SRT
  • Playback-Aware Pausing: Jellyfin, Plex and Emby webhooks suspend running jobs (SIGSTOP on the whole ffmpeg process tree) and resume them when playback ends
  • Processing Window: Only process during chosen hours (e.g. 22:00–06:00); jobs are suspended outside the window
  • Live Execution Control: Progress, logs and failure reasons for every file; pause, resume, stop and retry individual or bulk executions
  • Smart Rescans: Files are fingerprinted (size + mtime), so unchanged files are never reprocessed and changed files are picked up again
  • Worker Pools: Per-job-type concurrency (1–10 processes) scaled live via supervisord, with crash recovery for lost executions
  • Notifications: Webhook notifications on completed/failed executions (Discord, Slack and generic JSON)
  • REST API: Token-authenticated /api/v1 for status, libraries, executions and pause/resume, handy for Homepage dashboards and scripts
  • Web UI: Dashboard, libraries, executions and workers via Inertia + React
  • Authentication: Registration, login, passkeys (WebAuthn), email verification, 2FA/TOTP

Tech Stack

Layer Technology
Backend PHP 8.5, Laravel 13, Fortify
Frontend React 19, Inertia v3, TypeScript, Tailwind v4, shadcn/ui
Database PostgreSQL 18
Queue Database driver
Cache Redis
Containers Docker (single Debian-based image: nginx + PHP-FPM + supervisord queue workers)

Quickstart

Self-Hosting (Docker)

Pull the prebuilt image from GHCR, configure environment, and start with Docker Compose.

Prerequisites
  • Docker Engine 24+ with Compose plugin
  • An APP_KEY (generate with openssl rand -base64 32)
  • A PostgreSQL 18-compatible password
  • (Optional) A running Traefik reverse proxy for TLS / homelab routing

PostgreSQL 18 note: The volume mounts at /var/lib/postgresql (not /var/lib/postgresql/data). PG 18+ requires a mount at the parent directory so it can create a version-specific subdirectory, which is needed for clean pg_upgrade later. See docker-library/postgres#1259.

Setup

# 1. Pull the compose file and env template
curl -O https://raw.githubusercontent.com/fais649/flowarr/main/docker-compose.prod.yml
curl -O https://raw.githubusercontent.com/fais649/flowarr/main/.env.prod.example

# 2. Configure
cp .env.prod.example .env.prod
#   Edit .env.prod — at minimum set:
#     APP_KEY=$(openssl rand -base64 32)
#     DB_PASSWORD=<your-password>
#     DOMAIN=<your-domain>          # only needed with Traefik
#     APP_URL=https://<your-domain> # only needed with Traefik

# 3. Start
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d

.env.prod serves double duty: it feeds Compose variable substitution via --env-file, and it's bind-mounted into the container (./.env.prod:/var/www/html/.env.prod) as the app's own config.

Visit http://localhost:8080/register (direct access) or https://<your-domain>/register (with Traefik) to create the first user.

Build from source instead of pulling: :latest only exists after a release tag is published — pushes to main publish :edge. Until then, build locally:

git clone https://github.com/Fais649/flowarr && cd flowarr && docker build -t ghcr.io/fais649/flowarr:latest .
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d

Compose reference

The stack runs three services: PostgreSQL 18, Redis 7, and the Flowarr app container.

services:
  flowarr-postgres:
    image: postgres:18-alpine
    volumes:
      - postgres-data:/var/lib/postgresql   # PG 18+ needs mount at /var/lib/postgresql, not /data
    healthcheck:
      test: ["CMD", "pg_isready", "-q", "-d", "flowarr", "-U", "flowarr"]
      start_period: 60s   # grace for first-start initdb
      retries: 5

  flowarr-redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]

  flowarr:
    image: ghcr.io/fais649/flowarr:latest
    ports:
      - "8080:8080"   # remove for Traefik-only access
    environment:
      APP_KEY:        # required — generate with `openssl rand -base64 32`
      APP_URL:        # https://<your-domain>
      DB_PASSWORD:    # required — matches POSTGRES_PASSWORD above
      RUN_MIGRATIONS: "true"
    volumes:
      - /path/to/media:/media          # writable — Flowarr writes transcoded files & sidecars here
      - flowarr-storage:/var/www/html/storage

The production compose file is at docker-compose.prod.yml in the repo root, and the Laravel Sail development compose file is at compose.yaml. Environment variables are documented in .env.prod.example.

Container details

The Docker image (ghcr.io/fais649/flowarr:latest) is a single Debian-based container where supervisord manages nginx, PHP-FPM, and the queue worker processes.

  • Port: 8080 (internal), FastCGI proxy to 127.0.0.1:9000
  • PHP: 8.5, extensions: pgsql, pdo_pgsql, bcmath, zip, intl, pcntl, redis
  • System tools: jellyfin-ffmpeg 7 (NVENC, VAAPI, zscale tonemapping), mkvtoolnix, bash, curl, postgresql-client
  • Build: 3-stage (composer → assets → runtime), ~465 MB compressed

First boot

On first start, PostgreSQL initializes its data directory (can take 30-60s). The healthcheck has start_period: 60s to allow for this — Docker won't mark it unhealthy during init.

Once Postgres is healthy, the Flowarr entrypoint runs in order:

  1. Requires .env.prod to be mounted at /var/www/html/.env.prod — if it's absent, the container exits with an error message (no .env is auto-created)
  2. Copies .env.prod to .env
  3. Clears the Laravel configuration, route, event, and view caches
  4. Runs database migrations if RUN_MIGRATIONS=true (default)
  5. Rebuilds the configuration, route, event, and view caches
  6. Starts supervisord, which runs PHP-FPM, nginx, the orchestration/notification queue worker, the library scanner worker, the task scheduler (schedule:work, which drives interval library scans and crash recovery), and a one-shot queue:orchestrate startup job

The transcode, subtitle-extraction, and subtitle-conversion worker pools (up to 10 processes each) are defined with autostart=false and are scaled by the orchestrator to each worker's configured concurrency. Default workers for all three job types are created on first migration. No manual artisan commands required.

APP_KEY persistence

APP_KEY is required — it is never auto-generated. If it's missing or empty, the app cannot encrypt sessions, cookies, or other encrypted data. Generate one and set it in your .env.prod:

openssl rand -base64 32
# → SaN+R05iCUuA64GtMh579p/MdA5giQLJw1q8wYQ3oB8=
# Set APP_KEY=base64:SaN+R05iCUuA64GtMh579p/MdA5giQLJw1q8wYQ3oB8= in .env.prod

Traefik reverse proxy

The compose file includes Traefik v2/v3 discovery labels. To use them, run the stack with the docker-compose.traefik.yml override, which attaches the flowarr container to the external traefik network:

  1. Make sure Traefik is running with an traefik Docker network: docker network create traefik (if absent)
  2. Set DOMAIN in .env.prod — this becomes the Host() rule
  3. Set APP_URL to https://<your-domain>
  4. Optionally remove the ports: mapping (Traefik routes internally)
  5. Start the stack:
    docker compose --env-file .env.prod -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d

Labels are pre-configured for TLS on the websecure entrypoint. The base compose file keeps the flowarr container on the flowarr internal network; the override additionally attaches it to the external traefik network.

Directory layout (container)

/var/www/html/
├── public/          ← Laravel public dir (index.php, build/ assets)
├── storage/         ← persistent (mount as volume)
├── vendor/          ← Composer deps
├── app/ config/ etc ← Laravel app

Mount your media library at /media (writable — Flowarr writes transcoded files and subtitle sidecars here):

volumes:
  - /path/to/your/media:/media

Development (Laravel Sail)

git clone https://github.com/fais649/flowarr
cd flowarr
cp .env.example .env
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate
./vendor/bin/sail composer setup
./vendor/bin/sail artisan migrate
./vendor/bin/sail artisan db:seed

The app will be available at http://localhost.

./vendor/bin/sail composer run dev    # Starts application at localhost:80
./vendor/bin/sail artisan test   # Run tests

Media Server Integration

Flowarr holds processing while a media server reports active playback. The endpoint URLs are also listed under Config → Processing.

Server Setup
Jellyfin Install the Webhook plugin, add a Generic destination pointed at http://your-flowarr-host/webhooks/jellyfin, enable Playback Start, Playback Stop (and optionally Playback Progress) and tick Send All Properties.
Plex Settings → Webhooks → add http://your-flowarr-host/webhooks/plex (requires Plex Pass).
Emby Settings → Webhooks → add http://your-flowarr-host/webhooks/emby with the playback events.

Set MEDIA_SERVER_WEBHOOK_TOKEN to require a shared secret, sent as the X-Flowarr-Token header or as a ?token= query parameter (Plex cannot send custom headers). JELLYFIN_WEBHOOK_TOKEN is still honoured.

When a stream starts, running ffmpeg processes are suspended (SIGSTOP) and queued jobs wait; they resume (SIGCONT) when the last stream ends. Sessions without a stop event expire after the configurable stream timeout, and the Workers page can clear them manually.

Processing Window & Notifications

Under Config → Processing you can:

  • restrict processing to a daily window (in APP_TIMEZONE, may wrap around midnight)
  • send notifications for completed and/or failed executions to a webhook URL (Discord, Slack or any JSON endpoint)
  • generate a REST API token

REST API

Authenticate with Authorization: Bearer <token> (or X-Api-Key: <token>):

curl -H "Authorization: Bearer $FLOWARR_TOKEN" http://your-flowarr-host/api/v1/status
curl -X POST -H "Authorization: Bearer $FLOWARR_TOKEN" http://your-flowarr-host/api/v1/processing/pause
curl -H "Authorization: Bearer $FLOWARR_TOKEN" "http://your-flowarr-host/api/v1/executions?status=failed"

See ARCHITECTURE.md for all endpoints.

GPU Acceleration

TRANSCODE_HW_MODE=auto (default) uses NVENC when an NVIDIA GPU is visible, VAAPI when /dev/dri/renderD128 exists, and libx265 otherwise. The Workers page shows which encoder is detected.

  • AMD / Intel (VAAPI): keep the devices: - /dev/dri:/dev/dri mapping in docker-compose.prod.yml.
  • NVIDIA: install the NVIDIA Container Toolkit on the host and use the commented deploy.resources block instead.
  • CPU only: remove the device mapping or set ENABLE_GPU_TRANSCODING=false.

Architecture

See ARCHITECTURE.md for the full entity schema, data flow, and implementation plan.

Contributing

See CONTRIBUTING.md for setup instructions, test commands, and PR workflow.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages