Skip to content

freemkv-library

freemkv-library is the freemkv web server and replaces autorip: insert a disc, and it rips the main movie automatically to MKV. A browser-based UI shows live progress, settings, and history, with nothing to type, and a Library tab manages the MKVs and ISOs you already have. This page covers deploying freemkv-library and tuning it; for the manual command-line workflow, see the CLI reference. It always rips the main movie only; for other titles use the CLI or the desktop app.

On disc insert, freemkv-library runs the full pipeline automatically: a tolerant sweep, targeted patch retries on bad ranges, decrypt, and mux to MKV. Multiple drives rip in parallel, each with independent state.

freemkv-library runs on a host with an optical drive (a home server or NAS works well) and ships as a Docker image. The configuration and API reference below apply to every platform.

The freemkv-library Library tab: every movie with its codec, track counts and the tool that made it, a live audit in progress, and filters for problems, out-of-date and queued files

The freemkv-library dashboard during an active rip: matched title and metadata up top, with live per-pass progress, ETA, and throughput below.

Published to GHCR as ghcr.io/freemkv/freemkv-library (:latest and :vX.Y.Z); the same image is also still published as ghcr.io/freemkv/autorip, so existing installs keep pulling. Drop this compose file on a host with an optical drive and bring it up:

docker-compose.yml
services:
freemkv-library:
image: ghcr.io/freemkv/freemkv-library:latest
container_name: freemkv-library
restart: unless-stopped
privileged: true # REQUIRED for optical SCSI drive access
stop_grace_period: 75s # let a rip drain on stop (default 10s is too short)
environment:
- AUTORIP_DIR=/config # where settings.json + logs live
- PORT=8080 # web bind port (set at start, not at runtime)
- AUTORIP_LOG_LEVEL=autorip=info,libfreemkv=warn,freemkv=warn # tracing filter
volumes:
- /dev:/dev # live host /dev (handles USB re-enumeration)
- ./config:/config # settings.json + logs
- /mnt/media:/output # final MKV / M2TS destination
- /tmp/autorip:/staging # intermediate ISO + mapfile
- /sys:/sys:ro # read-only sysfs
ports:
- 8080:8080
healthcheck:
test: ["CMD", "/usr/local/bin/freemkv", "server", "--healthcheck"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
Terminal window
docker compose up -d

Then open http://<host>:8080.

stop_grace_period: 75s gives a rip time to cancel and drain on a restart or redeploy; Docker’s default 10 seconds is too short for a drive mid-recovery, which can take up to about a minute.

Unraid web UI > Docker > Template Repositories: add https://github.com/freemkv/unraid-templates and Save. Then Add Container and pick freemkv-library. The template sets privileged mode, /dev and /sys, port 8080, and paths for /config, /output, /staging and the keys directory (defaults under /mnt/user/appdata/freemkv-library and /mnt/user/media); check them and Apply.

Pull the new image. freemkv-library reads autorip’s /config/settings.json unchanged and keeps the AUTORIP_* environment variable names, so an existing install can point at the same ./config directory. The image also still answers to the autorip command. From 1.8.0 there are no native binary downloads and no Homebrew formula; freemkv-library ships only as this Docker image.

Mount Purpose
/dev:/dev Live host device tree; lets freemkv-library enumerate optical drives and survive USB re-enumeration.
/config Persistent home for settings.json, per-device logs, and (by default) the keys directory.
/output Where finished MKV/M2TS files land.
/staging Intermediate ISO + mapfile during a multipass rip.
/sys:ro Read-only sysfs.

DVDs need none; Blu-ray and 4K UHD need keys. See Decryption Keys for the options (a local file or an online service). freemkv-library’s key settings live in Configuration → Keys below.

For Docker, bind-mount a host directory to /root/.config/freemkv so keys persist across restarts:

volumes:
- ./config/keys:/root/.config/freemkv

These are read at container start and cannot be changed at runtime:

Variable Default Meaning
PORT 8080 Web UI / API bind port.
AUTORIP_DIR /config Where settings.json and logs are stored.
AUTORIP_LOG_LEVEL autorip=info,libfreemkv=warn,freemkv=warn tracing filter for log verbosity.
RIP_USER autorip Unix user the container creates and runs the daemon as.
NFS_HOST (unset) If set with the two below, freemkv-library mounts an NFS share inside the container at startup.
NFS_EXPORT (unset) Export path on the NFS server (e.g. /mnt/pool/media).
NFS_MOUNTPOINT (unset) Where to mount the share inside the container; point output_dir here.
NFS_OPTS (sensible default) Optional override for NFS mount options.

Everything else is configured through the web UI and persisted to settings.json. The NFS mount is best-effort: if the server is unreachable the container still starts and logs the failure.

All settings are editable in the Settings page of the web UI and stored in settings.json under AUTORIP_DIR.

Setting Default Meaning
staging_dir /staging Where intermediate ISO + mapfile live during a rip.
output_dir /output Final MKV / M2TS destination.
movie_dir (empty) Optional subdirectory under output for movies.
tv_dir (empty) Optional subdirectory under output for TV.
output_format mkv Output container: mkv, m2ts, iso, or network (stream to network_target).
network_target (empty) Network output target host:port (for streaming output).
keep_iso false Keep the intermediate ISO in the library after muxing.
iso_dir (empty) Where a kept ISO (or a whole-disc output_format = "iso" rip) is filed. A relative path resolves under output_dir; an absolute path targets another disk. Empty files the ISO beside the muxed title.
Setting Default Meaning
on_insert scan What to do on disc insert: nothing, scan, rip, or resume (prefer resuming an existing partial rip).
auto_eject true Eject the disc automatically after a rip completes.
main_feature true Rip only the longest (main feature) title.
tv_auto true On a detected TV disc, rip every episode and file it as Show (Year)/Season NN/ with no operator step. false holds TV discs for review instead.
min_length_secs 600 Ignore titles shorter than this many seconds.
Setting Default Meaning
max_retries 1 0 = single-pass (no retries); 1-10 = multipass with that many patch passes.
on_read_error stop On a read error during the sweep: stop or skip (skip-ahead).
abort_on_lost_secs 0 Tolerated main-movie loss in seconds after retries; 0 = require a perfect rip.
capture_without_keys false Capture to ISO even when AACS keys are missing, deferring the mux.
max_rip_duration_secs 28800 (8 h) Hard ceiling on total rip time across all passes.
min_pass_budget_secs 5400 (90 m) Minimum wall-clock budget allotted to each pass.
transport_recovery_delay_secs 5 Delay after a USB transport re-enumeration before retrying the drive.
decrypt_threads 0 Decryption worker threads; 0 = auto-detect CPU cores.
Setting Default Meaning
key_source local local (use a local key database) or online (use a key service).
keydb_path (unset) Explicit path to a local key database file.
keydb_url (empty) URL to download a key database from (used by the update action and daily refresh).
keyserver_url (empty) Base URL of an external key service (when key_source = online).
keyserver_secret (empty) Optional bearer token for the key service.
Setting Default Meaning
tmdb_api_key (empty) TMDB API key for title lookup / matching.
webhook_urls (empty) Webhooks that receive a JSON POST on pipeline events. Each entry has a url and post_rip / post_mux / post_move flags (all true by default) choosing which stages it fires on.
log_retention_days 30 How long to keep per-device .log files.

freemkv-library wraps the same recovery engine as the freemkv CLI; see How recovery works for the full sweep-and-patch model. The max_retries setting selects between two modes.

  • Single-pass (max_retries = 0) streams the disc directly to the output container. No intermediate ISO, no retries. Fastest path; appropriate for healthy discs.
  • Multipass (max_retries ≥ 1) runs the full recovery pipeline:
    1. Sweep reads the whole disc to an intermediate ISO, recording good and bad ranges in a mapfile and skipping ahead over damage.
    2. Patch re-reads the bad ranges from the disc, up to max_retries times.
    3. Mux decrypts the ISO and writes the final container.

Multipass exits the retry loop early as soon as there are no unreadable bytes left.

After retries are exhausted, freemkv-library evaluates abort_on_lost_secs against main-movie loss only (menus and trailers are excluded):

  • abort_on_lost_secs = 0: perfect rip required; abort if any main-movie data is still unreadable.
  • abort_on_lost_secs = N: finish the MKV as long as no more than N seconds of main-movie video are missing.

With output_format = "iso" (a whole-disc image), abort_on_lost_secs is ignored and treated as 0: a whole-disc backup always requires a perfect rip. The tolerance applies only to muxed MKV / M2TS output.

When a rip aborts on loss, the staging directory is preserved so you can review or retry.

max_rip_duration_secs caps total wall-clock time across all passes. min_pass_budget_secs guarantees each pass a minimum amount of time. Together they keep a badly damaged disc from running indefinitely while still giving each recovery pass a fair shot.

The dashboard at http://<host>:8080 shows each drive’s current state and live progress (status, percentage, ETA, bad ranges). From it you can:

  • Scan, rip, stop, and eject per drive.
  • Edit any configuration value in Settings.
  • Review held rips (when title matching needs confirmation) and pick the correct title, optionally via a TMDB search.
  • Inspect per-device logs and the system debug log.

freemkv-library idle, showing per-drive controls and the device log

Per-drive controls (Resume, Rip, Verify, Eject) with the live device log expanded below.

State updates stream to the browser over Server-Sent Events (/events).

The Library tab lists your MKVs and ISOs side by side, shows which freemkv version muxed each MKV, and can remux an out-of-date title from its ISO. It also audits the library.

freemkv-library exposes an HTTP API on the same port as the UI. JSON errors use {"ok": false, "error": "..."}; simple successes return {"ok": true}. {device} is a drive identifier from /api/state.

Method Path Description
GET /api/state Snapshot of every drive’s rip state, plus move/verify state.
GET /api/version Running version, e.g. {"version":"1.8.0"}.
GET /api/system System info: mux/move queues and the output file list.
GET /events Server-Sent Events stream of live state pushes.
Method Path Body Description
GET /api/settings - Full configuration (secrets redacted).
POST /api/settings JSON config fields Overlay and persist settings to settings.json.
Method Path Query / Body Description
POST /api/scan/{device} - Scan the disc (no rip).
POST /api/rip/{device} ?resume=yes|no (optional) Start a rip. See Resume below.
POST /api/stop/{device} - Stop the active rip; preserves staging for resume (404 if device unknown).
POST /api/eject/{device} - Eject the disc (409 if a rip/scan is in progress).
POST /api/accept-loss/{device} - Accept the recorded main-movie loss on a rip that aborted on loss, and deliver it as-is. See Accepted loss.
Method Path Description
POST /api/mux-errors/clear?path=... Clear one failed-mux entry.
POST /api/mux-errors/clear-all Clear every failed-mux entry.
POST /api/move-errors/clear?path=... Clear one failed-move entry.
POST /api/move-errors/clear-all Clear every failed-move entry.
Method Path Description
POST /api/update-keydb Download the key database from the configured keydb_url (SSRF-guarded fetch).
Method Path Body Description
GET /api/review - List rips on hold awaiting operator review.
POST /api/review/resolve {dir, action, title, year} Resolve a held rip: proceed, retitle, or cancel.
GET /api/tmdb/search ?q=query Search TMDB for title candidates.
POST /api/title/{device} {title, year, poster_url, overview, media_type} Override the matched title for the active disc (one-shot).
Method Path Query / Body Description
GET /api/logs/{device} - Per-device log, most recent lines first (plain text).
GET /api/debug ?n=&level=&device=&q= (optional) Recent debug events (JSON, filterable).
POST /api/debug {"enabled":true|false} Toggle debug logging at runtime.

Toggle debug logging and watch the mux stage:

Terminal window
# enable debug logging
curl -s -X POST http://<host>:8080/api/debug -d '{"enabled":true}'
# stream mux-stage output from the container
docker logs freemkv-library --tail=500 -f | grep -i mux

freemkv-library’s recovery work survives interruptions.

  • Default (POST /api/rip/{device}, no param): starts a fresh sweep — the mapfile is recreated and the ISO truncated. It does not delete an existing staging directory, but it does not resume one either; a disc already marked .completed is skipped rather than re-ripped.
  • ?resume=yes: require an existing resumable staging directory. If the sweep is incomplete, it continues the partial sweep, re-reading only the not-yet-recovered ranges from the disc; if the mapfile is already fully recovered, it re-muxes the staged ISO without any disc reads. If no resumable state exists, the request still returns 200 and the rip ends in an error state (there is no synchronous 404).
  • ?resume=no: wipe staging and start completely fresh.

Stopping a rip with /api/stop/{device} preserves staging, so a stopped rip resumes on the next disc insert or container restart, with no time lost on a long UHD rip.

Finished files land in output_dir, optionally split into movie_dir / tv_dir. Set webhook_urls to have freemkv-library POST a JSON event to external systems (a media server, a chat webhook) when a rip, mux, or move completes.

When a rip finishes, freemkv-library sends a rip_complete payload:

{
"event": "rip_complete",
"title": "Dune: Part Two",
"year": 2024,
"format": "UHD",
"poster_url": "https://image.tmdb.org/...",
"duration": "2h 46m",
"codecs": "HEVC 4K HDR10 · TrueHD 7.1 · ...",
"size_gb": 78.4,
"speed_mbs": 24.1,
"elapsed_secs": 5460,
"output_path": "/output/Dune Part Two (2024)/Dune Part Two (2024).mkv",
"errors": 0,
"lost_video_secs": 0.0
}

When the MKV has been produced from the staged ISO, a mux_complete payload with the same fields as rip_complete ("event": "mux_complete").

When the file is moved to its final destination, a smaller move_complete payload:

{ "event": "move_complete", "title": "Dune: Part Two", "output_path": "/output/..." }