Deploy Jellyfin in Kubernetes with Helm: Step-by-Step Guide

Deploy, configure, and access your personal media server in a Kubernetes cluster using Helm — scalable, resilient, and fully under your control.

This guide provides a practical, step-by-step tutorial to help you set up, configure, and troubleshoot Jellyfin on a Kubernetes cluster, giving you a fully functional media server that is free, legal, and fully under your control.

Prerequisites

Before deploying Jellyfin on Kubernetes, make sure you understand the basics of how Jellyfin works and what makes it different from Plex and Emby.

  1. Hosting Options
    • DigitalOcean (Managed & Beginner-Friendly) (Recommended)
      • You get free trial credits, preconfigured clusters, built-in load balancers, and scaling.
    • Hostinger VPS
      • A budget-friendly VPS option where you can deploy your own Kubernetes setup.
    • Local machine (Free)
      • The completely free route — run Minikube, K3s, or MicroK8s on your existing hardware and deploy Jellyfin without paying for hosting.
  2. A Running Kubernetes Cluster
    • You need access to a Kubernetes cluster.
    • Note: For tutorial / small scale purposes, a single-node cluster is sufficient.
  3. kubectl Installed
    • The kubectl command-line tool is required to interact with your Kubernetes cluster.
    • Check installation:
      • kubectl version --client
  4. Helm Installed
    • Helm simplifies deploying Jellyfin by handling all the Kubernetes resources in one command.
    • Check Helm:
      • helm version
  5. Sufficient Node Resources
    • At least 4GB RAM for the node running Jellyfin.
    • CPU cores: Minimum 2 cores for smooth playback and transcoding.
    • Optional GPU if you plan to enable hardware-accelerated transcoding.
  6. Network Access
    • Ensure your client device can reach the Kubernetes cluster.
    • You will use port-forwarding or NodePort/Ingress to access Jellyfin via a browser.

Deploy Jellyfin using Helm

Deploying Jellyfin in Kubernetes is much simpler with Helm, the Kubernetes package manager. Using the official Jellyfin Helm chart, you can spin up your media server in just a few commands, including persistent storage and service exposure.

Open the Control Plane and follow these steps:

  1. Add the Jellyfin Helm Repository
    • First, add the official Jellyfin chart repository and update Helm:
    • helm repo add jellyfin https://jellyfin.github.io/jellyfin-helm
    • helm repo update
  2. Install Jellyfin with Default Settings
    • You can install Jellyfin using default chart settings.
    • This will create a Deployment, Service, and PersistentVolumeClaims for configuration and media storage:
    • helm install jellyfin jellyfin/jellyfin
    • After installation, verify the pods and services:
    • kubectl get pods,svc
  3. Customize the Deployment
    • For most home-lab setups or single-node clusters, you might want to override some default settings.

Accessing Jellyfin Dashboard

Once Jellyfin is deployed in your Kubernetes cluster, the next step is to access the dashboard so you can configure your media server, add libraries, and start streaming content.

If you don’t have a NodePort or Ingress configured, the simplest way to access Jellyfin is via kubectl port-forward:

kubectl port-forward svc/jellyfin 8096:8096

This forwards traffic from your local machine port 8096 to the Jellyfin service in the cluster.

Open your browser and visit:

http://localhost:8096

Adding Persistent Storage (Media Files)

When running Jellyfin in Kubernetes, persistent storage is essential. Without it, any media files, libraries, or configuration will be lost whenever the pod restarts or redeploys. Kubernetes allows you to attach PersistentVolumes (PV) and PersistentVolumeClaims (PVC) to make your data durable.

  • Media library persistence: Movies, TV shows, and music remain safe across pod restarts.
  • Configuration persistence: User accounts, settings, and metadata are retained.
  • Cluster migration: Storage can be reused if you move Jellyfin to another cluster or node.

The Jellyfin Helm chart supports persistence out-of-the-box. You can enable and configure it using values.yaml or --set overrides:

helm install jellyfin jellyfin/jellyfin \
  --set persistence.config.enabled=true \
  --set persistence.media.enabled=true \
  --set persistence.media.size=10Gi

Explanation:

  • persistence.config.enabled=true → saves Jellyfin configuration.
  • persistence.media.enabled=true → saves your media library.
  • persistence.media.size=10Gi → allocates 10GB for media storage.

For a local single-node setup, you can mount a local directory as storage:

persistence:
  media:
    enabled: true
    storageClass: ""
    accessMode: ReadWriteOnce
    size: 10Gi
    mountPath: /media

For multi-node or production setups:

  • Use NFS, AWS EBS, GCP Persistent Disk, or other storage classes.
  • Ensure that your PVC points to the storage class compatible with your cluster.

Example:

persistence:
  media:
    enabled: true
    storageClass: nfs-storage
    accessMode: ReadWriteMany
    size: 100Gi

After deployment, check that PVCs are bound:

kubectl get pvc

Enable Hardware Acceleration (Optional)

For users streaming high-resolution content, such as 4K videos, Jellyfin can use hardware acceleration to reduce CPU usage and improve performance. In Kubernetes, this can be achieved by enabling GPU support on your nodes and configuring Jellyfin to use it.

  • Efficient transcoding: Offloads video encoding/decoding from CPU to GPU.
  • Smooth playback: Reduces buffering for high-bitrate media.
  • Supports multiple streams: More users can stream simultaneously without overloading the pod.

Verify Jellyfin is Running

After deploying Jellyfin in Kubernetes using Helm, it’s important to confirm that your pod, services, and storage are functioning correctly. This ensures that your media server is ready for use and accessible.

Check Pods:

List all pods in your namespace (default if not specified):

kubectl get pods

Expected output for the Jellyfin pod:

NAME                       READY   STATUS    RESTARTS   AGE
jellyfin-xxxxxxxxx         1/1     Running   0          3m

Check Services:

Ensure the Jellyfin service is exposed correctly:

kubectl get svc

Example output:

NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
jellyfin  NodePort   10.96.123.45    <none>        8096:30096/TCP   5m

Check Persistent Volumes:

Make sure your persistent storage is bound to your pods:

kubectl get pvc

Example:

NAME                  STATUS   VOLUME             CAPACITY
jellyfin-config-pvc    Bound   pvc-config-xyz    1Gi
jellyfin-media-pvc     Bound   pvc-media-abc     5Gi

Troubleshooting Common Issues

Even with Helm simplifying deployment, you may encounter some common issues when running Jellyfin in Kubernetes. This section helps you quickly identify and resolve them.

  1. Pod Stuck in Pending
    • The Jellyfin pod status stays Pending for a long time.
    • Ensure your PVC is Bound.
    • Verify node resources are sufficient (kubectl describe node <node>).
    • Check for image pull errors.
  2. Incorrect mountPath in Helm values. Ensure Jellyfin pod has read/write access to the volume
  3. If Videos buffer or CPU spikes during playback. Reduce the number of simultaneous streams or lower transcoding quality.

Summary

Deploying Jellyfin in Kubernetes allows you to run a scalable, self-hosted media server that keeps your movies, TV shows, music, and photos organized and accessible from anywhere.

Using Helm, the deployment becomes simple and reproducible, with support for persistent storage and optional GPU hardware acceleration for smooth streaming.

With Kubernetes, your Jellyfin pod benefits from automatic restarts, easy scaling, and persistent volumes to protect both your media and configuration. You can access the dashboard via port-forwarding or NodePort and start adding media libraries right away.

For more advanced tips and best practices, check out our Kubernetes guide to manage clusters and workloads effectively.

Author

Sharukhan is the founder of Tecktol. He has worked as a software engineer specializing in full-stack web development.