Configuration
Drift-prevention: This Starlight page is the canonical docs-site version. The legacy
docs/*.mdpath now points here instead of carrying a second copy.
This guide explains how Ferrex is configured for local development and self‑hosting. It complements the quickstart in the README and the reference .env.example.
Where Configuration Lives
Section titled “Where Configuration Lives”- Generated environment file:
.envin the project root (created byjust startorjust config). - Example reference:
.env.example(kept in repo). - Derived assets and caches:
cache/ - Optional demo seed data:
demo/(when using demo mode)
Back up .env if you keep long‑lived credentials. The generator creates strong Postgres/Redis passwords.
Core Environment Variables
Section titled “Core Environment Variables”These are the most commonly used variables. See .env.example for the authoritative list.
TMDB_API_KEY– Required for metadata lookups.SERVER_HOST/SERVER_PORT– Bind address and port (defaults:0.0.0.0/3000).FERREX_SERVER_URL– The URL clients use to reach the server (e.g.,http://localhost:3000).DATABASE_URL– Postgres connection URL (host/local use) plusDATABASE_URL_CONTAINERfor in-container commands.REDIS_URL– Redis connection URL (plusREDIS_URL_CONTAINERfor in-container access).RUST_LOG– Server logging filter, e.g.sqlx=trace,ferrex=debug.FERREX_MPV_PATH– Optional override for mpv path on Windows if auto‑detection fails.- TLS options – Paths can be provided via env (if you terminate TLS at the app). If you use a reverse proxy, terminate TLS there instead.
- Player URL – Run the player against a custom server with
FERREX_SERVER_URL=https://host:port.
Intelligence Runtime Variables
Section titled “Intelligence Runtime Variables”The Phase 2 intelligence runtime is configured with FERREX_INTELLIGENCE_* variables. The runtime is disabled by default; when enabled it calls the configured local/trusted OpenAI-compatible provider.
FERREX_INTELLIGENCE_ENABLED– Enables grounded runtime execution when set totrue.FERREX_INTELLIGENCE_BASE_URL– OpenAI-compatible provider base URL. Local development default:http://localhost:8081/v1.FERREX_INTELLIGENCE_API_KEY– Optional provider API key placeholder; leave blank for local providers that do not require a key.FERREX_INTELLIGENCE_MODEL– Optional model override. Local development default:gemma-4-12b.FERREX_INTELLIGENCE_MODEL_TIMEOUT_MS,FERREX_INTELLIGENCE_TOOL_TIMEOUT_MS,FERREX_INTELLIGENCE_TOTAL_TIMEOUT_MS– Model, tool, and whole-run timeout budgets in milliseconds.FERREX_INTELLIGENCE_MAX_STEPS,FERREX_INTELLIGENCE_MAX_TOOL_CALLS– Upper bounds for runtime loops.FERREX_INTELLIGENCE_MAX_OUTPUT_BYTES,FERREX_INTELLIGENCE_MAX_TOOL_RESULT_BYTES– Byte caps for generated output and persisted tool results.FERREX_INTELLIGENCE_MAX_RETRIES– Retry count for transient provider/runtime failures.FERREX_INTELLIGENCE_PER_USER_CONCURRENCY– Per-user concurrent run limit; additional active run starts for the same user receive aconcurrency_limitresponse until a slot is free.
Generating Configuration
Section titled “Generating Configuration”From the repo root:
# Generate/refresh config without starting servicesjust config
# Start the full stack (DB, Redis, ferrex-server)just start# (same as: ferrexctl stack up)# Bring the stack down:# ferrexctl stack down
# Run the desktop player (release profile)just run-player-releaseCompose Files / Overlays
Section titled “Compose Files / Overlays”docker-compose.ymlis the default self-host stack and pulls the published server image.docker-compose.dev.ymladds a local build offerrex-server(used byjustviaFERREX_COMPOSE_FILES).docker-compose.perf.ymlenables the Postgres performance preset (huge pagestry, io_uring, larger buffers).
Unraid: see Unraid.
Nix (NixOS)
Section titled “Nix (NixOS)”This repo includes a flake for local development and for running the player with
a pinned Linux GStreamer build. nix develop is the canonical development shell
and includes the player-capable GStreamer/GPU environment. The historical
ferrex-player shell remains as an alias for existing scripts.
# canonical player-capable dev shellnix develop
# backward-compatible aliasnix develop .#ferrex-player
# lean server/core shell without player-only GStreamer/GPU inputsnix develop .#server
# run player (NixOS-friendly)nix run .#ferrex-playerProfiles and Performance
Section titled “Profiles and Performance”Ferrex defines useful build profiles for faster iteration and improved runtime performance:
- Development:
just start --profile dev(faster compile times) - Priority:
just start --profile priority(optimize workspace crates; recommended for the player) - Release:
just run-player-releaseorjust run-server-release
The ferrex-player benefits noticeably from optimization.
Tailscale Sidecar (single env file)
Section titled “Tailscale Sidecar (single env file)”Run the stack with the Tailscale sidecar; no extra .env is required:
just start --mode tailscalejust start --mode tailscale automatically overrides the container endpoints to 127.0.0.1 for Postgres and Redis inside the shared Tailnet namespace while keeping your base .env intact.
Scanner / Incremental Scans
Section titled “Scanner / Incremental Scans”Scanner settings can be supplied in scanner.toml, scanner.json, config/scanner.toml, config/scanner.json, SCANNER_CONFIG_PATH, or inline JSON via SCANNER_CONFIG_JSON. New libraries default to bounded automatic maintenance every 15 minutes, filesystem watching is enabled per library, and the watcher uses auto strategy. Watch events remain the primary low-latency path; the shorter maintenance interval is the fallback for missed notifications and newly added top-level media folders. Existing libraries keep their persisted interval until an operator updates them. On Linux, auto selects polling immediately for CIFS/SMB3 and NFS mounts; other filesystems use native notifications with polling fallback.
Library create/update API payloads can override per-library policy:
{ "name": "Movies", "library_type": "Movies", "paths": ["/media/movies"], "scan_interval_minutes": 15, "auto_scan": true, "watch_for_changes": true}Local filesystem example (low latency native watching):
video_extensions = ["mkv", "mp4", "avi", "mov", "webm", "m4v"]ignored_extensions = ["tmp", "part"]
[orchestrator.watch]strategy = "auto" # auto | native | polldebounce_window_ms = 250max_batch_events = 8192poll_interval_ms = 30000
[orchestrator.maintenance]enabled = truetick_interval_ms = 60000max_jobs_per_library = 128max_root_entries_per_library = 512
[orchestrator.lease]dispatch_timeout_ms = 1800000 # 30 minutes per job execution attemptWhen a root has more eligible folders than a maintenance pass can enqueue, Ferrex rotates deterministic partitions on successive scan intervals. This rotation does not depend on the prior partition completing successfully, so folders beyond the configured bound are still revisited.
Job execution attempts are bounded to 30 minutes by default. When an actor or external dependency never returns, Ferrex stops renewing that attempt, releases its lease and workload capacity, and routes the job through the normal retry policy. Operators can lower orchestrator.lease.dispatch_timeout_ms for faster recovery, but it should remain above the longest expected scan, analysis, metadata, indexing, or image operation.
Network/container mount example (prefer bounded polling over unreliable native events):
Explicit poll remains useful for container mounts whose host filesystem type is hidden from the container. If native is forced on a detected CIFS/NFS mount, startup logs an operator warning because notifications may be unreliable.
video_extensions = ["mkv", "mp4", "mpeg", "ts"]ignored_extensions = ["tmp", "part", "download"]ignored_path_patterns = ["**/.staging/**"]
[orchestrator.watch]strategy = "poll"poll_interval_ms = 120000debounce_window_ms = 1000max_batch_events = 2048poll_backoff_max_ms = 600000
[orchestrator.maintenance]enabled = truetick_interval_ms = 300000max_jobs_per_library = 64max_root_entries_per_library = 256Optional transcript indexing
Section titled “Optional transcript indexing”Transcript indexing is off by default. When enabled, Ferrex stores only redacted, bounded subtitle segments from supported text sources: sidecar SubRip/WebVTT (.srt, .vtt, .webvtt) files and text-convertible embedded subtitle streams reported by ffprobe/ffmpeg. Bitmap subtitle formats such as PGS/DVD subtitles are skipped. Redaction runs before persistence and search indexing; built-in patterns cover email addresses, phone-like numbers, URL query secrets, bearer/token assignments, and deployment-specific custom_regexes.
[orchestrator.transcript_indexing]enabled = trueembedded_enabled = truesidecar_enabled = trueallowed_languages = ["en", "es"] # empty means all detected languagesmax_subtitle_bytes = 4194304max_segments_per_media = 20000max_chars_per_segment = 4000max_chars_per_snippet = 320extraction_timeout_ms = 15000concurrency_budget = 1
[orchestrator.transcript_indexing.redaction]enabled = trueredact_emails = trueredact_phone_numbers = trueredact_url_secrets = trueredact_bearer_tokens = truecustom_regexes = ["(?i)internal-case-[0-9]+"]Operational controls:
- Retry extraction for a playable item:
POST /api/v1/media/{movie|episode}/{id}/refresh-transcripts. - Purge stored transcript sources/segments without deleting media files or non-transcript intelligence artifacts:
POST /api/v1/libraries/{library_id}/media/{movie|episode}/{id}/transcripts:purgewith{"reason":"operator request"}. - Purge and request a rebuild when the media file is still available:
POST /api/v1/libraries/{library_id}/media/{movie|episode}/{id}/transcripts:rebuild. - Search snippets through
POST /api/v1/intelligence/timed-text:search; snippet length is clamped by both request caps andmax_chars_per_snippet.
Validation commands for transcript changes:
cargo fmt --all --checknix develop .#ferrex-player --command env cargo check --workspace --all-targetsnix develop .#ferrex-player --command env cargo test -p ferrex-core --libDATABASE_URL=postgres://... cargo test -p ferrex-core --test transcript_repositoryDATABASE_URL=postgres://... cargo test -p ferrex-server --test intelligence_routes transcript_purge_and_rebuild_routes_remove_searchable_segmentsInvalid scanner config fails during startup with the field path in the error (for example, scanner.orchestrator.watch.poll_interval_ms must be greater than 0). Operators can inspect the effective policy and health counters via the scan config/metrics/status endpoints; these report watch strategy, poll/debounce/batch settings, the job dispatch deadline, transcript indexing controls, maintenance sweep policy, media/ignore filters, watcher registrations, replay lag, stale cursor counts, overflow events, and root-discovery truncation/deferred-entry counters.
Logging
Section titled “Logging”Control server verbosity via --rust-log:
just start --rust-log 'sqlx=trace,ferrex=debug'Alternatively, set RUST_LOG directly in .env.
Demo Mode (Optional)
Section titled “Demo Mode (Optional)”Ferrex includes a feature‑gated demo mode that seeds disposable libraries for exploration and testing. See Demo mode for full details and environment variables.
Postgres Performance Configuration
Section titled “Postgres Performance Configuration”Ferrex supports configurable Postgres performance presets for different hardware configurations:
Presets
Section titled “Presets”Use FERREX_POSTGRES_PRESET to select a predefined configuration:
small(4-8GB RAM): shared_buffers=512MB, effective_cache_size=2GB, work_mem=16MB, max_connections=50medium(16-32GB RAM): shared_buffers=4GB, effective_cache_size=12GB, work_mem=64MB, max_connections=100large(64GB+ RAM): shared_buffers=16GB, effective_cache_size=48GB, work_mem=256MB, max_connections=200custom: Use individual environment variables (see below)
# During initial setupferrexctl init --postgres-preset=medium
# Or set manually in .envFERREX_POSTGRES_PRESET=mediumIndividual Overrides
Section titled “Individual Overrides”You can override specific parameters regardless of preset:
FERREX_POSTGRES_SHARED_BUFFERS- Shared memory for Postgres (e.g., “4GB”)FERREX_POSTGRES_EFFECTIVE_CACHE_SIZE- OS cache estimate (e.g., “12GB”)FERREX_POSTGRES_WORK_MEM- Per-operation memory (e.g., “64MB”)FERREX_POSTGRES_MAX_CONNECTIONS- Max concurrent connections (e.g., “100”)FERREX_POSTGRES_SHM_SIZE- Docker shm_size (e.g., “8g”)FERREX_POSTGRES_MAINTENANCE_WORK_MEM- Maintenance operations memoryFERREX_POSTGRES_WAL_BUFFERS- Write-ahead log buffersFERREX_POSTGRES_HUGE_PAGES- Huge pages support (“on” or “off”)FERREX_POSTGRES_MIN_WAL_SIZE- Minimum WAL sizeFERREX_POSTGRES_MAX_WAL_SIZE- Maximum WAL size
Example with overrides:
FERREX_POSTGRES_PRESET=mediumFERREX_POSTGRES_SHARED_BUFFERS=8GB # Override preset valueTLS / HTTPS
Section titled “TLS / HTTPS”Ferrex can terminate TLS directly. If you prefer a reverse proxy (nginx, Caddy, Traefik), terminate TLS there and run Ferrex over HTTP on localhost.
To enable HTTPS directly in Ferrex, set certificate and key paths:
TLS_CERT_PATH=/path/to/cert.pemTLS_KEY_PATH=/path/to/key.pemAdvanced (optional):
TLS_MIN_VERSION– Minimum TLS version to allow. Defaults to1.3.1.3(recommended) or1.2.
TLS_CIPHER_SUITES– Comma‑separated allow‑list of TLS 1.3 cipher suites.- Example:
TLS13_AES_256_GCM_SHA384,TLS13_CHACHA20_POLY1305_SHA256
- Example:
Notes:
- Default behavior is TLS 1.3 (Ferrex Player is the primary client).
- If you set
TLS_MIN_VERSION=1.3, very old clients that only support TLS 1.2 will fail to connect — this is expected and desired for hardening. - Certificate hot‑reload is supported: when
cert.pem/key.pemcontents change, the server reloads them (checked every ~5 minutes).
Security Notes
Section titled “Security Notes”- Ferrex is under active development; avoid exposing the server directly to the public Internet.
- Prefer running on an internal network, behind a reverse proxy, or via the Tailscale sidecar.
- See the project security policy for the vulnerability disclosure policy.
- See Authentication security model for password login, remember-device, PIN login, auto-login, profile listing privacy, lockout, revoke, and recovery semantics.