Skip to content

Jellyfin

Free and Open Source Media Server

Jellyfin is a free, open source media server that puts you in control of your media. It streams movies, TV shows, music, and photos to virtually any device, with no subscriptions or tracking.

Why Choose Jellyfin?

  • Completely free - no premium tier, no tracking, fully open source
  • Multi-device - web, mobile, TV apps, and Chromecast support
  • Hardware transcoding - CPU and GPU accelerated transcoding
  • Rich metadata - automatic fetching of posters, summaries, and subtitles

Install

Infrastructure as Code

This Jellyfin instance is deployed using OpenTofu with a custom Incus image. The infrastructure configuration manages container provisioning, persistent storage, and media library access.

Custom Jellyfin Image

The Jellyfin Incus image is built and maintained at forgejo.benoit.jp.net/Benoit/Laminar.

Infrastructure Configuration

The OpenTofu configuration provisions:

  • Incus instance running the custom Jellyfin image with 6 CPU cores
  • Persistent storage volume for /var/lib/jellyfin (application data, metadata, database)
  • Proxy device exposing port 8096 on the host network interface
  • Read-only media share mounting TrueNAS downloads at /mnt/truenas/downloads
OpenTofu Configuration
resource "incus_storage_volume" "jellyfin_var_lib_jellyfin" {
  name = "jellyfin_var_lib_jellyfin"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid" = "104"
    "initial.uid" = "103"
    "initial.mode" = "755"
  }
}

resource "incus_instance" "jellyfin" {
  name  = "jellyfin"
  image = "laminar.incus:jellyfin-10.10.6-1benoitjpnet"

  config = {
    "limits.cpu" = 6
  }

  device {
    name = "jellyfin_var_lib_jellyfin"
    type = "disk"
    properties = {
      path   = "/var/lib/jellyfin"
      source = incus_storage_volume.jellyfin_var_lib_jellyfin.name
      pool   = incus_storage_pool.default.name
    }
  }

  device {
    name = "http"
    type = "proxy"
    properties = {
      listen  = "tcp:192.168.1.2:8096"
      connect = "tcp:127.0.0.1:8096"
    }
  }

  device {
    name = "mnt_truenas_downloads"
    type = "disk"
    properties = {
      path   = "/mnt/truenas/downloads"
      source = "/mnt/truenas/downloads"
    }
  }
}

Deploy Infrastructure

Apply OpenTofu configuration
tofu apply

After provisioning, Jellyfin is accessible at http://192.168.1.2:8096. Complete the initial setup wizard to configure your media libraries, pointing them at /mnt/truenas/downloads.

Upgrade

Stateless Upgrade

Because all application data lives on the persistent /var/lib/jellyfin storage volume, upgrading is a simple image version bump, with no backup or restore steps required.

Update the image version in OpenTofu

Edit your OpenTofu configuration to reference the new image version:

resource "incus_instance" "jellyfin" {
  name  = "jellyfin"
  image = "laminar.incus:jellyfin-NEW-VERSION"  # (1)!
  # ...
}
  1. Replace NEW-VERSION with the target Jellyfin version tag.

Apply the infrastructure change

Provision new Incus container
tofu apply

Upgrade Complete

The new container starts with the existing /var/lib/jellyfin volume attached, preserving all metadata, libraries, and user settings.