Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Jellyfin

apps.jellyfin completes the startup wizard unattended, then reconciles the server name, libraries and accounts. Transcoding, storage and everything else stays yours to set on services.jellyfin.

selfhost.apps.jellyfin = {
  enable = true;
  libraries = [
    { name = "Movies"; collectionType = "movies"; locations = [ "/mnt/media/movies" ]; }
  ];
};
selfhost.users.alice.services.jellyfin.enable = true;

Jellyfin refuses to start with under 2 GiB free in its data directory, and aborts with a core dump rather than a readable error. Worth knowing before putting dataDir on a small partition.

Everything is merged, never replaced

branding, encoding, trickplay and a user’s policy are all merged onto whatever Jellyfin currently holds, then written back only if something changed. That matters most for branding: LoginDisclaimer is where an SSO plugin puts its login button, and replacing the whole object would wipe it on every run.

The same applies to library locations, which are reconciled rather than set once. Moving a directory in your config moves the library.

A library’s name is its reconcile identity, so renaming one creates a second library.

Encoding takes exactly one writer

Two mechanisms can own encoding.xml, and mixing them is the trap.

apps.jellyfin.encoding merges onto what Jellyfin currently holds, so fields you do not name keep their value. services.jellyfin.hardwareAcceleration and services.jellyfin.transcoding instead write the file from a template, and on a server that already has one they do nothing until you also set forceEncodingConfig. That template carries 18 elements while a populated encoding.xml runs to over 40, so everything outside it resets to Jellyfin’s default, including TonemappingAlgorithm, PreferSystemNativeHwDecoder and EnableDecodingColorDepth10Vp9, none of which nixpkgs models.

Set both and the file permanently differs from the template, so every restart leaves another encoding.xml.backup-<timestamp> behind. Prefer the nixpkgs options on a fresh install where their defaults are acceptable, and this one on a server whose settings are worth keeping.

Jellyfin ignores properties it does not recognise, so a misspelled field is dropped without complaint. EnableHwEncoding and EnableHwDecoding are the trap worth naming: both are real TrickplayOptions fields, neither exists on EncodingOptions, where the equivalents are EnableHardwareEncoding and HardwareDecodingCodecs.

Forward-auth is off

Jellyfin authenticates its own clients, and native apps cannot pass a forward-auth gateway, so forwardAuth.enable defaults to false here rather than following the gateway. Turn it on only if every client you use is a browser.

Accounts

passwordFile is a bootstrap credential: applied when the account is created and never reconciled, so a password changed in the app survives. Accounts without one get a random password. policy is a freeform passthrough merged into Jellyfin’s UserPolicy, so anything you do not name keeps its current value.

The admin account is separate. The reconciler authenticates as it to drive the API.

Nothing is deleted

The reconcile creates and updates only. Dropping a library or user from your config leaves the Jellyfin side untouched, since removing either is destructive. Removals are a deliberate manual step.

Keeping up with Jellyfin

api-contract.json beside the module declares the request payloads configure.nu sends, and the vm-jellyfin test asserts them against the server’s own /api-docs/openapi.json. Jellyfin deserialises with System.Text.Json, which ignores unknown keys rather than rejecting them, so a renamed field would otherwise stop being applied with nothing to show for it.

POST /Library/VirtualFolders takes its name, collection type and paths as query parameters, so library creation is the one call the contract cannot cover. It fails loudly with a 4xx instead.