Jellyfin Service

Table of Contents

Features
Usage
Debug
Options Reference

Defined in /modules/services/jellyfin.nix.

This NixOS module is a service that sets up a Jellyfin instance.

Compared to the stock module from nixpkgs, this one sets up, in a fully declarative manner:

Features

Usage

The following snippet assumes a few blocks have been setup already:

shb.jellyfin = {
  enable = true;
  subdomain = "jellyfin";
  domain = "example.com";

  admin = {
    username = "admin";
    password.result = config.shb.sops.secret."jellyfin/adminPassword".result;
  };

  ldap = {
    enable = true;
    host = "127.0.0.1";
    port = config.shb.lldap.ldapPort;
    dcdomain = config.shb.lldap.dcdomain;
    adminPassword.result = config.shb.sops.secrets."jellyfin/ldap/adminPassword".result
  };

  sso = {
    enable = true;
    endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
  
    secretFile = config.shb.sops.secret."jellyfin/sso_secret".result;
    secretFileForAuthelia = config.shb.sops.secret."jellyfin/authelia/sso_secret".result;
  };
};

shb.sops.secret."jellyfin/adminPassword".request = config.shb.jellyfin.admin.password.request;

shb.sops.secrets."jellyfin/ldap/adminPassword".request = config.shb.jellyfin.ldap.adminPassword.request;

shb.sops.secret."jellyfin/sso_secret".request = config.shb.jellyfin.sso.sharedSecret.request;
shb.sops.secret."jellyfin/authelia/sso_secret" = {
  request = config.shb.jellyfin.sso.sharedSecretForAuthelia.request;
  settings.key = "jellyfin/sso_secret";
};

Secrets can be randomly generated with nix run nixpkgs#openssl -- rand -hex 64.

The user and admin LDAP groups are created automatically.

The shb.jellyfin.sso.secretFile and shb.jellyfin.sso.secretFileForAuthelia options must have the same content. The former is a file that must be owned by the jellyfin user while the latter must be owned by the authelia user. I want to avoid needing to define the same secret twice with a future secrets SHB block.

Certificates

For Let’s Encrypt certificates, add:

{
  shb.certs.certs.letsencrypt.${domain}.extraDomains = [
    "${config.shb.jellyfin.subdomain}.${config.shb.jellyfin.domain}"
  ];
}

Backup

Backing up Jellyfin using the Restic block is done like so:

shb.restic.instances."jellyfin" = {
  request = config.shb.jellyfin.backup;
  settings = {
    enable = true;
  };
};

The name "jellyfin" in the instances can be anything. The config.shb.jellyfin.backup option provides what directories to backup. You can define any number of Restic instances to backup Jellyfin multiple times.

You will then need to configure more options like the repository, as explained in the restic documentation.

Impermanence

To save the data folder in an impermanence setup, add:

{
  shb.zfs.datasets."safe/jellyfin".path = config.shb.jellyfin.impermanence;
}

Declarative LDAP

To add a user USERNAME to the user and admin groups for jellyfin, add:

shb.lldap.ensureUsers.USERNAME.groups = [
  config.shb.jellyfin.ldap.userGroup
  config.shb.jellyfin.ldap.adminGroup
];

Debug

In case of an issue, check the logs for systemd service jellyfin.service.

Enable verbose logging by setting the shb.jellyfin.debug boolean to true.

Options Reference

shb.jellyfin.enable

Whether to enable shb jellyfin.

Type: boolean

Default: false

Example: true

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin

Default admin user info. Only needed if LDAP or SSO is not configured.

Type: null or (submodule)

Default: null

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password

Password of the default admin user.

Type: submodule

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.request

Request part of the secret contract.

Options set by the requester module enforcing some properties the secret should have.

Type: submodule

Default: ""

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.request.group

Linux group owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.request.mode

Mode of the secret file.

Type: string

Default: "0440"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.request.owner

Linux user owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.request.restartUnits

Systemd units to restart after the secret is updated.

Type: list of string

Default:

[
  "jellyfin.service"
]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.result

Result part of the secret contract.

Options set by the provider module that indicates where the secret can be found.

Type: submodule

Default:

{
  path = "/run/secrets/secret";
}

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.password.result.path

Path to the file containing the secret generated out of band.

This path will exist after deploying to a target host, it is not available through the nix store.

Type: absolute path

Default: "/run/secrets/secret"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.admin.username

Username of the default admin user.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup

Backup configuration.

Type: submodule

Default: { }

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request

Request part of the backup contract.

Options set by the requester module enforcing how to backup files.

Type: submodule

Default: { user = jellyfin; sourceDirectories = [ “services.jellyfin.dataDir” ] ; excludePatterns = [ ]; hooks.beforeBackup = [ ]; hooks.afterBackup = [ ]; };

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.excludePatterns

File patterns to exclude.

Type: list of string

Default: [ ]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.hooks

Hooks to run around the backup.

Type: submodule

Default: { }

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.hooks.afterBackup

Hooks to run after backup.

Type: list of string

Default: [ ]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.hooks.beforeBackup

Hooks to run before backup.

Type: list of string

Default: [ ]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.sourceDirectories

Directories to backup.

Type: non-empty (list of string)

Default: [ “services.jellyfin.dataDir” ]

Example: "/var/lib/vaultwarden"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.request.user

Unix user doing the backups.

Type: string

Default: "jellyfin"

Example: "vaultwarden"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.result

Result part of the backup contract.

Options set by the provider module that indicates the name of the backup and restore scripts.

Type: submodule

Default: ""

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.result.backupService

Name of service backing up the database.

This script can be ran manually to backup the database:

$ systemctl start backup.service

Type: string

Default: "backup.service"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.backup.result.restoreScript

Name of script that can restore the database. One can then list snapshots with:

$ restore snapshots

And restore the database with:

$ restore restore latest

Type: string

Default: "restore"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.debug

Enable debug logging

Type: boolean

Default: false

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.domain

Domain to serve sites under.

Type: string

Example: "domain.com"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap

LDAP configuration.

Type: submodule

Default: { }

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.enable

Whether to enable LDAP.

Type: boolean

Default: false

Example: true

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminGroup

LDAP admin group

Type: string

Default: "jellyfin_admin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword

LDAP admin password.

Type: submodule

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.request

Request part of the secret contract.

Options set by the requester module enforcing some properties the secret should have.

Type: submodule

Default: ""

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.request.group

Linux group owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.request.mode

Mode of the secret file.

Type: string

Default: "0440"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.request.owner

Linux user owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.request.restartUnits

Systemd units to restart after the secret is updated.

Type: list of string

Default:

[
  "jellyfin.service"
]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.result

Result part of the secret contract.

Options set by the provider module that indicates where the secret can be found.

Type: submodule

Default:

{
  path = "/run/secrets/secret";
}

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.adminPassword.result.path

Path to the file containing the secret generated out of band.

This path will exist after deploying to a target host, it is not available through the nix store.

Type: absolute path

Default: "/run/secrets/secret"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.dcdomain

DC domain for LDAP.

Type: string

Example: "dc=mydomain,dc=com"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.host

Host serving the LDAP server.

Type: string

Example: "127.0.0.1"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.plugin

Pluging used for LDAP authentication.

Type: package

Default: <derivation jellyfin-plugin-ldapauth-20>

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.port

Port where the LDAP server is listening.

Type: signed integer

Example: 389

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ldap.userGroup

LDAP user group

Type: string

Default: "jellyfin_user"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.plugins

Install plugins declaratively.

The LDAP and SSO plugins will be added if their respective shb.jellyfin.ldap.enable and shb.jellyfin.sso.enable options are set to true.

The interface for plugin creation is WIP. Feel free to add yours following the examples from the LDAP and SSO plugins but know that they may require some tweaks later on. Notably, configuration is not yet handled by this option so that will be added in the future.

Each plugin’s meta.json must be writeable because Jellyfin appends some information upon installing the plugin, like its active or disabled status. SHB automatically enables the plugin and deletes any plugin with the same prefix but other versions. Note that SHB does not attempt to find which version is latest. If twice the same plugin is added, the last one in the “plugins” list wins.

Type: list of package

Default: [ ]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.port

Listen on port.

Type: 16 bit unsigned integer; between 0 and 65535 (both inclusive)

Default: 8096

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ssl

Path to SSL files

Type: null or (open submodule of anything)

Default: null

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ssl.paths

Paths where the files for the certificate will be located.

This option is the contract output of the shb.certs.certs SSL block.

Type: open submodule of anything

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ssl.paths.cert

Path to the cert file.

Type: absolute path

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ssl.paths.key

Path to the key file.

Type: absolute path

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.ssl.systemdService

Systemd oneshot service used to generate the certificate. Ends with the .service suffix.

Use this if downstream services must wait for the certificates to be generated before starting.

Type: string

Example: "cert-generator.service"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso

SSO configuration.

Type: submodule

Default: { }

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.enable

Whether to enable SSO.

Type: boolean

Default: false

Example: true

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.authorization_policy

Require one factor (password) or two factor (device) authentication.

Type: one of “one_factor”, “two_factor”

Default: "one_factor"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.clientID

Client ID for the OIDC endpoint

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.endpoint

OIDC endpoint for SSO

Type: string

Example: "https://authelia.example.com"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.plugin

Pluging used for SSO authentication.

Type: package

Default: <derivation jellyfin-plugin-sso-3.5.2.4>

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.provider

OIDC provider name

Type: string

Default: "Authelia"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret

OIDC shared secret for Jellyfin.

Type: submodule

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.request

Request part of the secret contract.

Options set by the requester module enforcing some properties the secret should have.

Type: submodule

Default: ""

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.request.group

Linux group owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.request.mode

Mode of the secret file.

Type: string

Default: "0440"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.request.owner

Linux user owning the secret file.

Type: string

Default: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.request.restartUnits

Systemd units to restart after the secret is updated.

Type: list of string

Default:

[
  "jellyfin.service"
]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.result

Result part of the secret contract.

Options set by the provider module that indicates where the secret can be found.

Type: submodule

Default:

{
  path = "/run/secrets/secret";
}

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecret.result.path

Path to the file containing the secret generated out of band.

This path will exist after deploying to a target host, it is not available through the nix store.

Type: absolute path

Default: "/run/secrets/secret"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia

OIDC shared secret for Authelia.

Type: submodule

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.request

Request part of the secret contract.

Options set by the requester module enforcing some properties the secret should have.

Type: submodule

Default: { mode = 0400; owner = config.shb.authelia.autheliaUser; group = root; restartUnits = [ ]; }

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.request.group

Linux group owning the secret file.

Type: string

Default: "root"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.request.mode

Mode of the secret file.

Type: string

Default: "0400"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.request.owner

Linux user owning the secret file.

Type: string

Default: config.shb.authelia.autheliaUser

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.request.restartUnits

Systemd units to restart after the secret is updated.

Type: list of string

Default: [ ]

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.result

Result part of the secret contract.

Options set by the provider module that indicates where the secret can be found.

Type: submodule

Default:

{
  path = "/run/secrets/secret";
}

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.sso.sharedSecretForAuthelia.result.path

Path to the file containing the secret generated out of band.

This path will exist after deploying to a target host, it is not available through the nix store.

Type: absolute path

Default: "/run/secrets/secret"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>
shb.jellyfin.subdomain

Subdomain under which home-assistant will be served.

Type: string

Example: "jellyfin"

Declared by:

<selfhostblocks/modules/services/jellyfin.nix>