Skip to content
Author Nejat Hakan
eMail nejat.hakan@outlook.de
PayPal Me https://paypal.me/nejathakan


Media Server Jellyfin

Introduction

Welcome to this comprehensive guide on self-hosting your own media server using Jellyfin. In an era where streaming services dominate, taking control of your personal media library offers unparalleled freedom, privacy, and customization. Jellyfin stands out as a premier, free, and open-source software solution that allows you to collect, manage, and stream your movies, TV shows, music, photos, and live TV from anywhere, to virtually any device.

Self-hosting Jellyfin means you are not reliant on third-party companies, their terms of service, or their subscription fees. You own the data, you control the access, and you tailor the experience precisely to your needs. This guide is designed for university students and enthusiasts eager to dive deep into the world of self-hosting. We will progress from the fundamental concepts and basic setup to intermediate configurations like user management and transcoding, culminating in advanced topics such as secure remote access, hardware acceleration, and robust maintenance strategies.

We assume a basic understanding of computer concepts and a willingness to engage with command-line interfaces, particularly in the Linux environment which is highly recommended for stability and performance. Each section is structured to build upon the previous one, providing detailed theoretical explanations followed by practical, hands-on "Workshop" sections. These workshops are designed as step-by-step tutorials, allowing you to apply the learned concepts in a real-world project context, building your own Jellyfin server from the ground up. Prepare to embark on a rewarding journey into media freedom!

Basic Level

1. Understanding Jellyfin Fundamentals

Before diving into installation and configuration, it's crucial to grasp what Jellyfin is, its core functionalities, and why it's a compelling choice for a self-hosted media server.

What is a Media Server?

At its heart, a media server is a dedicated computer application (and often the hardware it runs on) designed to store, organize, and deliver digital media content (videos, music, photos) over a network. Think of it as your personal Netflix or Spotify, but populated entirely with your own media files. It centralizes your collection, making it accessible from various client devices like smartphones, tablets, smart TVs, laptops, and streaming boxes, both within your home network and potentially remotely. Key functions typically include:

  • Library Management: Scanning designated folders, identifying media files, fetching metadata (like posters, summaries, cast info, ratings), and organizing everything into a user-friendly library interface.
  • Streaming: Delivering the media content over the network to client devices for playback.
  • Transcoding: (Often necessary) Converting media files on-the-fly from their original format/codec/bitrate/resolution into one that is compatible with the requesting client device or suitable for the available network bandwidth.
  • User Management: Allowing multiple users to access the server, often with individual watch progress tracking, personalized settings, and access restrictions.

Introducing Jellyfin

Jellyfin emerged as a community-driven fork of Emby Media Server after Emby transitioned to a more closed-source, proprietary licensing model. Jellyfin is committed to remaining Free and Open Source Software (FOSS), developed entirely by volunteers and licensed under the GNU General Public License (GPL). This means its source code is publicly available, can be audited, modified, and distributed freely, ensuring it will never lock features behind a paywall or compromise user privacy for commercial gain.

Core Features:

  • Wide Media Support: Manages movies, TV shows, music, photos, books, and even Live TV/DVR (requires compatible tuner hardware).
  • Metadata Fetching: Automatically downloads rich metadata and artwork from various online sources like The Movie Database (TMDb), The TVDB, Open Movie Database (OMDb), MusicBrainz, and more.
  • Cross-Platform Clients: Offers official and third-party client applications for numerous platforms including Web browsers, Android, iOS, Android TV, Fire TV, Roku, Kodi, Desktop (via Jellyfin Media Player), and more.
  • Transcoding Engine: Includes a powerful built-in transcoder based on FFmpeg, capable of handling a vast array of codecs and formats. It supports hardware acceleration via Intel Quick Sync Video (QSV), NVIDIA NVENC/NVDEC, AMD AMF, and VAAPI for significantly improved performance and lower CPU usage on compatible hardware.
  • User Management: Supports multiple user accounts with individual watch history, preferences, and granular access control, including parental controls based on content ratings or tags.
  • Plugin System: Extensible architecture allowing installation of plugins for additional metadata sources, subtitle downloads (e.g., Open Subtitles), utility functions (e.g., Trakt synchronization), authentication methods (LDAP), and more.
  • Live TV & DVR: Can integrate with TV tuner hardware (like HDHomeRun) to watch live television and schedule recordings.
  • SyncPlay: Allows synchronized viewing of media with multiple users across different locations.
  • Offline Mode: Some clients support downloading media for offline playback.

Jellyfin vs. Alternatives (Plex, Emby)

  • Plex: Very popular, polished interface, wide client support. However, it's closed-source, requires a Plex account (cloud dependency for authentication), and many advanced features (like hardware transcoding, mobile sync, parental controls, Live TV/DVR) require a paid Plex Pass subscription. Focuses heavily on curated online content alongside personal media.
  • Emby: Jellyfin's origin. Similar feature set to Plex and Jellyfin. Also closed-source with a subscription model (Emby Premiere) required for features like hardware transcoding, offline downloads, and certain client apps.
  • Jellyfin: The key differentiator is its 100% free and open-source nature. All features are available without subscriptions. It offers greater transparency, user control, and privacy. While sometimes perceived as slightly less polished than Plex in certain areas or having fewer niche client apps, it's rapidly evolving and offers a robust, feature-complete experience preferred by many self-hosting enthusiasts who value FOSS principles.

System Requirements

Jellyfin's resource needs depend heavily on usage, particularly the amount of transcoding:

  • CPU: A modern multi-core processor is recommended.
    • Direct Play/Stream: Minimal CPU load. Even a Raspberry Pi 4 can handle a few direct streams.
    • Software Transcoding (CPU-based): Very CPU-intensive. For transcoding a single 1080p H.264 stream, a rough guideline is a CPU PassMark score of 2000. For a 4K HEVC stream, you might need a PassMark score exceeding 10000-15000. Multiple simultaneous transcodes require proportionally more power.
    • Hardware Transcoding (GPU-based): Significantly reduces CPU load. A compatible Intel CPU with integrated graphics (6th gen or newer recommended for good codec support) or a dedicated NVIDIA/AMD GPU can handle multiple transcodes efficiently. This is often the most cost-effective way to handle transcoding.
  • RAM: 2GB is a bare minimum for the OS and Jellyfin itself. 4GB is recommended for a smoother experience, especially with larger libraries. 8GB or more is beneficial if handling many simultaneous streams, extensive transcoding, or running other services on the same machine.
  • Storage: Enough space for the Operating System, the Jellyfin server application and its metadata cache (can grow to several GBs depending on library size), and most importantly, your media files (which can range from gigabytes to many terabytes). Fast storage (SSD) for the OS and Jellyfin's configuration/database/cache directories is highly recommended for responsiveness. Media files can reside on slower, larger HDDs.
  • Network: A stable network connection is essential. Gigabit Ethernet (1 Gbps) is strongly recommended for the server connection within your home network, especially for high-bitrate 4K content or multiple streams. Upload speed becomes critical for remote access quality.

Understanding these fundamentals provides a solid foundation before proceeding with the installation and configuration of your own Jellyfin instance.

Workshop Exploring the Jellyfin Demo

Before installing anything, let's get acquainted with the Jellyfin user interface and core navigation by exploring the official public demo instance. This allows you to see what a populated Jellyfin server looks like and how users interact with it.

  • Objective: Familiarize yourself with the Jellyfin web UI, library browsing, media playback, and basic settings exploration.
  • Prerequisites: A modern web browser and an internet connection.

Steps:

  1. Access the Demo: Open your web browser and navigate to the official Jellyfin Demo website: https://demo.jellyfin.org/stable

    • Note: This is a public instance, potentially subject to load or occasional resets. Performance may vary.
  2. Login: You'll likely be presented with a login screen. Use the publicly provided demo credentials (often demo / demo, or check the Jellyfin website if it changes). Select the demo user if multiple are presented.

  3. Explore the Home Screen: Once logged in, you'll see the main dashboard or home screen. Observe the layout:

    • Libraries: Typically listed on the left sidebar or prominently displayed. You'll see categories like "Movies," "TV Shows," "Music."
    • Continue Watching: Shows media you've partially watched.
    • Next Up: Displays the next episodes for TV shows you're currently watching.
    • Latest Additions: Shows recently added media to specific libraries.
    • Take a moment to scroll through and understand how content is presented.
  4. Browse a Library (Movies):

    • Click on the "Movies" library.
    • Notice how movies are displayed with posters. Hover over a movie to see basic information.
    • Look for sorting and filtering options (usually at the top). Try sorting by Release Date, Name, or Rating. Explore filtering by Genre, Year, etc. This demonstrates Jellyfin's organizational capabilities.
  5. View Media Details:

    • Click on a specific movie poster.
    • Examine the details page: Poster, backdrop/fanart, title, year, rating, summary/overview, cast list (often clickable), director, genres, runtime.
    • Look for icons indicating available video/audio streams, subtitles, or special features if present.
  6. Simulate Playback:

    • Click the "Play" button on a movie's detail page.
    • The media should start playing within the browser's player interface.
    • Explore the player controls: Play/Pause, volume, timeline scrubbing.
    • Look for a settings cogwheel icon within the player. Click it. Here you can usually adjust:
      • Quality: Select different resolutions/bitrates (this might trigger transcoding on the server).
      • Audio Track: Choose different language audio streams if available.
      • Subtitles: Select available subtitle tracks or turn them off.
    • Note: Actual playback quality and transcoding behavior depend on the demo server's load and configuration.
  7. Browse a TV Show Library:

    • Navigate back to the Home screen (usually via a home icon or Jellyfin logo).
    • Click on the "TV Shows" library.
    • Click on a show poster. Notice it takes you to the show's main page, listing seasons.
    • Click on a season. Now you see the list of episodes for that season, often with thumbnails and summaries.
    • Click on an episode to view its details or play it directly. Observe the "Next Up" integration automatically queuing the subsequent episode.
  8. Explore Settings (Limited View):

    • Look for a user icon or settings menu (often top right). Click it.
    • Explore options like "Settings" or "User Settings".
    • In a demo account, you usually cannot access administrative settings (like library setup or user management), but you can often adjust personal playback preferences, subtitle appearance, home screen layout, etc. Browse through the available options to get a feel for user-level customization.
  9. Logout: When finished exploring, find the logout option (usually under the user menu) and sign out.

This brief tour should give you a solid visual understanding of what Jellyfin offers from an end-user perspective, preparing you for the next steps of installing and configuring your own instance.

2. Installation Methods Overview

With a grasp of Jellyfin's fundamentals, the next step is understanding how to install it on your chosen hardware. There are several ways to get Jellyfin running, each with its advantages and disadvantages. Choosing the right method depends on your technical comfort level, operating system, and long-term maintenance preferences.

Common Installation Approaches:

  1. Native Packages (OS Package Manager):

    • How it works: Jellyfin provides official repositories for major Linux distributions (Debian, Ubuntu, Fedora, CentOS) and potentially packages for other operating systems. You add the Jellyfin repository to your system's package manager (like apt for Debian/Ubuntu, dnf for Fedora) and then install Jellyfin like any other system application (e.g., sudo apt install jellyfin).
    • Pros:
      • Relatively straightforward for users familiar with their OS's package management.
      • Integration with the host system (e.g., potentially easier setup for hardware transcoding drivers, systemd service management).
      • Updates can often be handled alongside regular system updates (e.g., sudo apt update && sudo apt upgrade).
    • Cons:
      • Tightly coupled with the host OS and its libraries. OS upgrades or library changes could potentially break the Jellyfin installation if dependencies conflict.
      • Jellyfin configuration files and data are stored directly on the host filesystem (typically under /etc/jellyfin, /var/lib/jellyfin, /var/log/jellyfin), requiring careful backup procedures.
      • May install numerous dependencies directly onto the host system.
      • Availability and version might lag slightly behind Docker releases depending on the packager's speed.
  2. Docker (Containerization):

    • How it works: Jellyfin provides official Docker images. Docker allows you to run applications in isolated environments called containers. You pull the Jellyfin image and run it as a container, mapping persistent storage (for configuration and cache) and media folders from your host system into the container. This is typically managed using docker run commands or, more commonly, docker-compose files for easier configuration.
    • Pros:
      • Isolation: Jellyfin and its dependencies run isolated from your host OS. This prevents conflicts and keeps your host system cleaner.
      • Consistency: The container runs the same way regardless of the underlying host OS (as long as Docker is supported).
      • Dependency Management: All necessary libraries are bundled within the image, simplifying setup.
      • Easy Updates: Updating is often as simple as pulling the latest image and restarting the container (docker-compose pull && docker-compose up -d).
      • Portability: Easily move your Jellyfin setup (docker-compose file and mapped volumes) to another Docker-enabled host.
      • Reproducibility: The docker-compose.yml file defines the exact configuration, making it easy to recreate the setup.
    • Cons:
      • Requires understanding basic Docker concepts (images, containers, volumes, ports).
      • Hardware transcoding setup can require extra configuration steps to pass hardware devices (like GPUs) into the container and ensure correct permissions.
      • Slight overhead compared to native installation, though generally negligible for Jellyfin's workload.
  3. Manual Build (From Source):

    • How it works: You download the Jellyfin source code from its repository (e.g., GitHub) and follow instructions to compile and build it yourself. This typically involves installing specific development tools and libraries (.NET SDK, Node.js, etc.).
    • Pros:
      • Access to the absolute latest, cutting-edge code (even before official releases).
      • Maximum control over build options and dependencies.
      • Deepest understanding of the software's internals (for developers).
    • Cons:
      • Most complex method, requiring significant technical expertise and troubleshooting skills.
      • Time-consuming process for initial setup and updates.
      • Managing dependencies manually can be challenging.
      • Generally not recommended for typical users; primarily for developers or specific advanced use cases.

Why Docker is Often Preferred for Self-Hosting

For most self-hosting scenarios, Docker (using Docker Compose) strikes the best balance between ease of management, reliability, and flexibility. The isolation it provides significantly reduces potential conflicts with the host system or other self-hosted applications. The ease of updating and the reproducibility offered by docker-compose files make long-term maintenance much simpler compared to native installations, where OS updates can sometimes introduce unexpected issues. While native packages might seem simpler initially for basic setups, Docker's advantages become increasingly apparent as your self-hosting setup grows or when dealing with updates and migrations.

Therefore, this guide will primarily focus on using Docker and Docker Compose for installing and managing Jellyfin.

Workshop Setting Up Your Host Environment (Conceptual)

This workshop focuses on preparing the underlying system where you will install Docker and eventually run Jellyfin. We will focus conceptually on Linux, as it's the most common and recommended platform for self-hosting stable services like Jellyfin.

  • Objective: Understand the prerequisites and basic steps for preparing a Linux host system suitable for running Jellyfin via Docker.
  • Prerequisites: Access to a machine (physical or virtual) where you can install an operating system, or an existing Linux installation.

Steps:

  1. Choose a Linux Distribution:

    • For servers, stable and well-supported distributions are recommended. Excellent choices include:
      • Ubuntu Server (LTS versions, e.g., 22.04, 24.04): Very popular, large community, extensive documentation, regular long-term support releases. Good balance of up-to-date software and stability.
      • Debian Stable: Known for its rock-solid stability and adherence to FOSS principles. Updates are less frequent but heavily tested. Ideal if stability is paramount.
      • Fedora Server: More cutting-edge, featuring newer kernel versions and software packages. Good if you need recent features, but release cycle is faster.
      • CentOS Stream / AlmaLinux / Rocky Linux: Enterprise-focused distributions known for stability, often used in corporate environments.
    • Recommendation: For most users, especially those newer to Linux or wanting ample online resources, Ubuntu Server LTS is an excellent starting point.
    • Action: If installing fresh, download the ISO image for your chosen distribution and install it on your target hardware or virtual machine, following the installer's prompts. A minimal server installation (without a desktop environment) is usually sufficient and recommended for resource efficiency.
  2. Establish Secure Remote Access (SSH):

    • During installation or afterwards, ensure the SSH server is installed and enabled. On Ubuntu/Debian, this is typically done via:
      sudo apt update
      sudo apt install openssh-server
      sudo systemctl enable ssh
      sudo systemctl start ssh
      
    • SSH (Secure Shell) allows you to connect to your server's command line remotely and securely from another computer (your laptop/desktop).
    • Action: From your main computer (Windows users might use PuTTY, WSL, or Windows Terminal; macOS/Linux users use the built-in ssh command), connect to your server:
      ssh your_username@your_server_ip_address
      
      Replace your_username with the user you created during installation and your_server_ip_address with the server's local IP address (you can find this on the server console using ip addr or ifconfig).
  3. Basic Linux Command Familiarization:

    • Once connected via SSH, you'll interact with the server via the command line. Here are a few essential commands to know:
      • ls: List directory contents (ls -la for detailed view including hidden files).
      • cd <directory>: Change directory (e.g., cd /home/your_username, cd .. to go up one level).
      • pwd: Print working directory (shows your current location).
      • mkdir <directory_name>: Create a new directory.
      • rm <file_name>: Remove a file (rm -r <directory_name> to remove a directory and its contents - use with caution!).
      • cp <source> <destination>: Copy files or directories.
      • mv <source> <destination>: Move or rename files or directories.
      • sudo <command>: Execute a command with administrative privileges (Super User Do). You'll use this frequently for installation and system changes.
      • nano <file_name> or vim <file_name>: Edit text files (nano is generally easier for beginners).
      • cat <file_name>: Display file contents.
      • grep <pattern> <file_name>: Search for text within a file.
      • systemctl status <service_name>: Check the status of a system service (e.g., systemctl status ssh).
      • apt update / apt upgrade (Debian/Ubuntu) or dnf update (Fedora): Update the package list and upgrade installed packages.
    • Action: Try navigating your home directory (cd ~), creating a test directory (mkdir test-dir), listing its contents (ls), changing into it (cd test-dir), and then removing it (cd .. && rm -r test-dir). Practice using sudo for a safe command like sudo apt update.
  4. System Updates:

    • It's crucial to keep your server's operating system and packages up-to-date for security and stability.
    • Action: Run the appropriate commands for your distribution:
      • Debian/Ubuntu:
        sudo apt update
        sudo apt full-upgrade -y # Use full-upgrade to handle changing dependencies
        sudo apt autoremove -y  # Remove unused packages
        
      • Fedora:
        sudo dnf check-update
        sudo dnf upgrade -y
        sudo dnf autoremove -y
        
    • It's good practice to reboot after significant updates, especially kernel updates: sudo reboot. Reconnect via SSH after the server comes back online.

By completing these conceptual steps (choosing an OS, ensuring SSH access, familiarizing yourself with basic commands, and performing initial updates), you have prepared a solid foundation upon which to install Docker and Docker Compose in the next stage, ready for your Jellyfin deployment.

3. Basic Jellyfin Installation with Docker

Now that your host environment is conceptually prepared, we'll proceed with installing Jellyfin using Docker, specifically leveraging Docker Compose for better configuration management. This method encapsulates Jellyfin and its dependencies, making setup and maintenance more streamlined.

Brief Docker Concepts Refresher:

  • Image: A read-only template containing the application (Jellyfin), its runtime environment, and all necessary libraries. You pull images from a registry (like Docker Hub).
  • Container: A runnable instance of an image. It's isolated from the host and other containers but can interact through defined networks and volumes. Containers are typically ephemeral unless persistent data is stored in volumes.
  • Volume: A mechanism to persist data generated by and used by Docker containers. Volumes map a directory on your host machine to a directory inside the container. This ensures your Jellyfin configuration and cache data survive container restarts or updates.
  • Port Mapping: Allows network traffic directed at a specific port on your host machine to be forwarded to a specific port inside the container where the application is listening.
  • Docker Compose: A tool for defining and running multi-container Docker applications. It uses a YAML file (docker-compose.yml) to configure the application's services (in our case, just Jellyfin initially), networks, and volumes, making it much easier than long docker run commands.

Why Docker Compose?

While you could start Jellyfin with a single docker run command, it quickly becomes long and difficult to manage, especially when adding more configuration options (like device mappings for hardware transcoding later). Docker Compose allows you to define the entire service configuration declaratively in a docker-compose.yml file. This file serves as documentation, is easily version-controlled, and makes starting, stopping, and updating Jellyfin trivial (docker-compose up -d, docker-compose down, docker-compose pull).

Installing Docker and Docker Compose

Before creating the Jellyfin service, you need Docker Engine and Docker Compose installed on your host system.

  • Follow Official Docker Instructions: The best way to install Docker is always to follow the official instructions for your specific Linux distribution. Do NOT install Docker from your distribution's default repositories, as they are often outdated.
    • Go to the Docker documentation: https://docs.docker.com/engine/install/
    • Select your Linux distribution (Ubuntu, Debian, Fedora, CentOS).
    • Follow the steps precisely, which usually involve setting up Docker's official repository and then installing docker-ce (Community Edition), docker-ce-cli, and containerd.io.
  • Install Docker Compose: Docker Compose V2 is now integrated as a plugin with Docker CLI. It's typically installed along with Docker Engine if you follow the latest official instructions. You can verify by running docker compose version. If it's not installed, follow the "Install the Compose plugin" section on the Docker Engine install page for your OS.
  • Add User to Docker Group (Post-installation): By default, running Docker commands requires sudo. To run Docker commands without sudo, add your user to the docker group (this group is created during Docker installation):
    sudo usermod -aG docker $USER
    
    Important: You need to log out and log back in (or reboot) for this group membership change to take effect. Verify by running docker ps without sudo. You should not get a permission error.

Creating the Jellyfin Docker Compose File

Now, let's define the Jellyfin service using Docker Compose.

Workshop Your First Jellyfin Instance via Docker Compose

  • Objective: Create a basic docker-compose.yml file for Jellyfin, launch the container, and perform the initial web-based setup.
  • Prerequisites:
    • A Linux host system prepared as per the previous workshop.
    • Docker Engine and Docker Compose plugin installed and working.
    • Your user added to the docker group (and you've logged out/in).
    • SSH access to your server.

Steps:

  1. Create Project Directory:

    • Connect to your server via SSH.
    • Choose a location for your Docker configurations. A common practice is to create a dedicated directory in your user's home directory.
      cd ~
      mkdir jellyfin-docker
      cd jellyfin-docker
      
  2. Create docker-compose.yml File:

    • Use a text editor (like nano or vim) to create the Docker Compose configuration file:
      nano docker-compose.yml
      
  3. Paste and Explain the Configuration:

    • Copy and paste the following configuration into the nano editor:

      version: '3.8' # Specifies the Docker Compose file format version
      
      services:
        jellyfin:
          image: jellyfin/jellyfin:latest # Use the official Jellyfin image from Docker Hub
          container_name: jellyfin       # Assign a fixed, readable name to the container
          # Optional: User/Group ID mapping - Uncomment and adjust if needed
          # user: 1000:1000               # Replace 1000 with your host user's UID and GID (find with `id -u` and `id -g`)
                                          # Ensures file permissions match between host and container
          network_mode: 'host'           # Use host networking for simplicity initially (easier local access, potential conflicts)
                                          # Alternative: Bridge mode with port mapping (see commented section below)
          # ports:                         # Only needed if using bridge network mode (delete network_mode: 'host' above)
          #   - "8096:8096"                # Map host port 8096 to container port 8096 (HTTP)
          #   - "8920:8920"                # Map host port 8920 to container port 8920 (HTTPS - if enabled in Jellyfin)
          #   - "1900:1900/udp"            # Map UDP port 1900 for service discovery (DLNA)
          #   - "7359:7359/udp"            # Map UDP port 7359 for client discovery
          volumes:
            - ./jellyfin-config:/config    # Map host directory './jellyfin-config' to container's /config
                                           # Stores database, settings, logs, plugins
            - ./jellyfin-cache:/cache      # Map host directory './jellyfin-cache' to container's /cache
                                           # Stores temporary files like transcoded data, artwork cache
            - /path/to/your/movies:/media/movies:ro # Map your host Movies folder READ-ONLY into the container
                                                    # IMPORTANT: Replace '/path/to/your/movies' with the ACTUAL path on your HOST
            - /path/to/your/tvshows:/media/tvshows:ro # Map your host TV Shows folder READ-ONLY into the container
                                                      # IMPORTANT: Replace '/path/to/your/tvshows' with the ACTUAL path on your HOST
            # Add more volume mappings here for other media types (e.g., music, photos)
            # - /path/to/your/music:/media/music:ro
      
          restart: unless-stopped        # Automatically restart the container unless manually stopped
          # Optional: Add device mappings for hardware transcoding later
          # devices:
          #   - /dev/dri:/dev/dri          # Example for Intel QSV/VAAPI
      
      # Optional: Define networks if not using host mode
      # networks:
      #   default:
      #     driver: bridge
      
    • Explanation:

      • version: '3.8': Defines the syntax version of the Compose file.
      • services:: Defines the different application containers. We only have one service: jellyfin.
      • image: jellyfin/jellyfin:latest: Specifies the Docker image to use. jellyfin/jellyfin is the official one, latest tag pulls the most recent stable release.
      • container_name: jellyfin: Gives the running container a predictable name.
      • user: 1000:1000: (Important for Permissions) This line (currently commented out) tells Docker to run the Jellyfin process inside the container with the specified User ID (UID) and Group ID (GID). You should uncomment this and replace 1000:1000 with your actual host user's UID and GID. Find these by running id -u and id -g on your host terminal. This ensures that files created by Jellyfin in the mapped volumes (like logs or metadata) have the correct ownership on your host system, preventing permission issues, especially when accessing mapped media folders.
      • network_mode: 'host': This makes the container share the host's network stack. Jellyfin will directly listen on the host's port 8096. It's simpler for initial local access and discovery but less isolated and can lead to port conflicts if other services use the same ports. The alternative (commented out ports: section and removing network_mode) is bridge mode, which is generally preferred for better isolation, especially when using a reverse proxy later. In bridge mode, you explicitly map host ports to container ports.
      • volumes:: This is crucial for persistence.
        • ./jellyfin-config:/config: Maps a directory named jellyfin-config (which will be created inside your jellyfin-docker project directory) to the /config directory inside the container. Jellyfin stores all its essential configuration, database, logs, and plugins here.
        • ./jellyfin-cache:/cache: Maps ./jellyfin-cache on the host to /cache inside the container for temporary data.
        • /path/to/your/movies:/media/movies:ro: CRITICAL: Maps the directory on your host system containing your movie files (replace /path/to/your/movies with the correct path, e.g., /srv/media/movies or /home/myuser/media/movies) to a directory named /media/movies inside the container. The :ro makes it read-only, which is safer – Jellyfin won't accidentally modify your original media files.
        • /path/to/your/tvshows:/media/tvshows:ro: Similarly maps your TV shows folder. Replace the host path. Add more lines like this for music, photos, etc.
      • restart: unless-stopped: Ensures the container restarts automatically if it crashes or if the Docker daemon restarts (e.g., after a system reboot), unless you explicitly stop it using docker-compose down.
  4. Create Media Directories (on Host):

    • Before starting Jellyfin, ensure the host directories you specified in the volumes section for your media actually exist. Replace the example paths below with the exact paths you used in your docker-compose.yml.
      # Example: If you used /srv/media/movies and /srv/media/tvshows
      sudo mkdir -p /srv/media/movies
      sudo mkdir -p /srv/media/tvshows
      # Adjust ownership if needed, especially if you didn't use the 'user:' directive
      # or if your media is on an external drive. Make sure the user running
      # the container (either root or the UID/GID specified) can READ the files.
      # Example: Allow the 'docker' group (or your user's group) read access
      # sudo chown -R your_username:your_group /srv/media
      # sudo chmod -R 755 /srv/media # Read/execute for group/others
      
    • Place some sample media files (if you have them) into these directories now so Jellyfin has something to scan later. Ensure they follow reasonable naming conventions (e.g., Movie Title (Year).mkv).
  5. Save and Close the File:

    • In nano, press Ctrl+X, then Y to confirm saving, and Enter to save with the name docker-compose.yml.
  6. Run Docker Compose:

    • Make sure you are still in the jellyfin-docker directory (where your docker-compose.yml file is located).
    • Execute the following command to download the Jellyfin image (if you don't have it) and start the container in detached mode (running in the background):
      docker compose up -d
      
    • Docker Compose will read the yml file, create the necessary networks/volumes (including the jellyfin-config and jellyfin-cache directories if they don't exist), pull the image, and start the container.
    • You can check the status of the container:
      docker compose ps
      # or
      docker ps # Should show the 'jellyfin' container running
      
    • To view the container's logs (useful for troubleshooting):
      docker compose logs -f jellyfin # Use Ctrl+C to stop following
      
  7. Access Jellyfin Web Interface:

    • Open a web browser on a computer on the same local network as your server.
    • Navigate to http://<your-server-ip>:8096. Replace <your-server-ip> with the actual local IP address of the machine running Docker.
    • You should be greeted by the Jellyfin first-time setup wizard.
  8. Initial Jellyfin Setup Wizard:

    • Follow the on-screen prompts:
      • Language: Select your preferred language.
      • Admin User: Create your administrator account (username and password). Remember these credentials!
      • Media Libraries: You can set up your libraries now or skip and do it later from the dashboard. If you set them up now, click "Add Media Library," choose the content type (e.g., Movies), give it a name (e.g., "My Movies"), and importantly, add the folder path as seen by the container. Based on our docker-compose.yml, this would be /media/movies for your movies library, /media/tvshows for TV shows, etc. Configure language and metadata settings as desired.
      • Metadata Settings: Confirm preferred language and country for metadata downloads.
      • Remote Access: Leave these settings as default for now (Allow remote connections checked, port mapping disabled - we used host networking or will handle this properly later with a reverse proxy).
      • Finish: Complete the wizard.

Congratulations! You now have a basic Jellyfin instance running in Docker, accessible on your local network. The next step is to configure it properly and add your media libraries if you haven't already.

4. Initial Configuration and Library Setup

After the first launch via the setup wizard (or if you skipped parts of it), you'll need to familiarize yourself with the Jellyfin dashboard and properly configure your media libraries so Jellyfin can find, identify, and present your content.

Accessing the Dashboard

Once you've completed the initial setup wizard and logged in with the administrator account you created, you gain access to the full Jellyfin interface. The administrative functions are primarily located in the Dashboard.

  • Click the hamburger menu icon (☰) usually in the top-left corner.
  • Select "Dashboard". This is your central control panel for managing the server.

Navigating the Dashboard

Take a moment to explore the sections available in the Dashboard sidebar:

  • Overview: Shows server information, activity, and currently playing media.
  • Settings: Contains general server settings (server name, language), networking, transcoding, etc.
  • Users: Manage user accounts, permissions, and parental controls.
  • Libraries: Add, edit, and scan your media libraries.
  • Playback: Configure transcoding settings, including hardware acceleration.
  • Scheduled Tasks: Manage automated tasks like library scans, subtitle downloads, database cleanup.
  • Plugins: Install and manage Jellyfin plugins.
  • Logs: View server logs for troubleshooting.
  • API Keys: Manage access for third-party applications.
  • Metadata: Configure metadata savers and providers.

Adding Media Libraries

This is the core step to make your media visible in Jellyfin. If you didn't add libraries during the initial wizard, or if you need to add more (like Music or Photos), follow these steps:

  1. Go to Dashboard -> Libraries.
  2. Click the "Add Media Library" button.
  3. Content Type: Select the type of media this library will hold (e.g., Movies, TV Shows, Music, Books, Photos, Mixed Content). This choice dictates how Jellyfin scans, identifies, and displays the content.
  4. Display Name: Enter a user-friendly name for the library (e.g., "4K Movies", "Kids TV Shows", "My Music Collection"). This is what users will see in the interface.
  5. Folders: This is the most critical part. Click the "+" button to add a folder path.
    • You need to enter the path as seen by the Jellyfin container, not the path on your host machine.
    • Based on the docker-compose.yml created in the previous workshop, if you mapped your host's /path/to/your/movies to /media/movies inside the container, you must enter /media/movies here for your Movies library.
    • Similarly, use /media/tvshows for the TV Shows library, /media/music for music, etc., corresponding exactly to the target paths defined in your Docker volume mappings.
    • You can add multiple folders to a single library if your media is split across different locations (ensure all locations are mapped into the container via volumes).
  6. Library Settings (Specific to Content Type): Configure the options presented, which vary based on the content type:
    • Preferred download language: Sets the priority language for metadata (summaries, titles).
    • Country: Sets the country for release dates and content ratings (important for parental controls).
    • Metadata downloaders (Images and Info): Check the sources Jellyfin should use to fetch information (e.g., The Movie Database, The TVDb, OMDb for videos; MusicBrainz, AudioDB for music). You can usually leave the defaults, but ensure relevant ones are enabled. The order often determines priority.
    • NFO Saver: Optionally enable saving metadata to .nfo files alongside your media. This provides a backup and can be used by other media managers (like Kodi). Decide if you want this; it modifies your media folders.
    • Artwork Savers: Similar to NFO saver, but for images (posters, fanart). Saves artwork locally.
    • Subtitle Modes: Configure how Jellyfin handles external subtitle files.
    • (TV Shows Specific) Display Order: Choose how shows are displayed (Aired Order, DVD Order, Absolute Order).
    • (TV Shows Specific) Season Folders: Enable if your TV shows are organized into season subfolders (highly recommended).
  7. Save: Click "OK" to save the new library configuration.

Scanning Libraries

Once a library is added, Jellyfin needs to scan the associated folders to find media files, identify them, fetch metadata, and add them to its database.

  • Automatic Scan: Jellyfin has scheduled tasks to scan libraries periodically (check Dashboard -> Scheduled Tasks -> Scan Media Libraries). It might also trigger a scan automatically when detecting changes if configured (requires specific host setup, often doesn't work reliably with Docker volumes without extra tools).
  • Manual Scan: To initiate a scan immediately:
    1. Go to Dashboard -> Libraries.
    2. Hover over the library you want to scan.
    3. Click the three-dot menu (...) that appears.
    4. Select "Scan Library".
    5. You can monitor the scan progress in the Dashboard Overview or by watching the logs (Dashboard -> Logs). The duration depends on the library size and network speed to metadata providers.

Metadata Downloaders and Identification

Jellyfin relies heavily on correct file and folder naming to accurately identify your media and fetch the correct metadata. If your files are named poorly (e.g., movie.mp4, final_episode.avi), identification will likely fail.

Recommended Naming Conventions:

  • Movies: Movie Title (Year).ext (e.g., Avatar (2009).mkv)
    • Place movies directly in the folder added to the library (e.g., /media/movies/Avatar (2009).mkv).
  • TV Shows: Show Name/Season XX/Show Name - SXXEYY - Episode Title.ext (e.g., /media/tvshows/Breaking Bad/Season 01/Breaking Bad - S01E01 - Pilot.mp4)
    • The Show Name folder should be inside the main TV Shows folder added to the library.
    • Season folders (Season 01, Season 02, etc.) are highly recommended.
    • The SXXEYY (Season XX, Episode YY) format is standard and works best.
  • Music: Organization is often based on embedded tags (ID3 tags in MP3/FLAC). However, a good folder structure helps: Artist Name/Album Name/Track Number - Track Title.ext (e.g., /media/music/Queen/A Night at the Opera/01 - Death on Two Legs.flac).

If media is misidentified, you can use the "Identify" feature (find the item in the library, click the three-dot menu, select "Identify") to manually search for the correct match using TMDb/TVDb IDs or refined search terms.

Workshop Adding and Scanning Your First Media Library

  • Objective: Add your Movies and TV Shows libraries to Jellyfin using the correct container paths and trigger a scan to populate the interface.
  • Prerequisites:
    • Jellyfin running via Docker Compose (from previous workshop).
    • You know the host paths to your media (e.g., /srv/media/movies) and the corresponding container paths defined in docker-compose.yml (e.g., /media/movies).
    • Some sample movie and TV show files placed in the host directories, preferably following recommended naming conventions.

Steps:

  1. Access Jellyfin Dashboard: Log in to your Jellyfin instance (http://<your-server-ip>:8096) with your admin account and navigate to the Dashboard (☰ menu -> Dashboard).

  2. Navigate to Libraries: Click on "Libraries" in the left sidebar.

  3. Add Movie Library:

    • Click "Add Media Library".
    • Content type: Select "Movies".
    • Display name: Enter "Movies" (or a name you prefer).
    • Folders: Click the "+" button. In the "Folder" field, enter the path inside the container that maps to your movies directory on the host. Based on our example docker-compose.yml, this is /media/movies. Click "OK".
    • Configure Settings:
      • Set "Preferred download language" (e.g., English).
      • Set "Country" (e.g., United States).
      • Review the "Metadata downloaders". Ensure "The Movie Database" and "The Open Movie Database" are checked.
      • Decide on NFO/Artwork saving (optional, default is usually off).
    • Click "OK" at the bottom to save the library.
  4. Add TV Shows Library:

    • Click "Add Media Library" again.
    • Content type: Select "TV Shows".
    • Display name: Enter "TV Shows".
    • Folders: Click "+". Enter the container path for your TV shows, e.g., /media/tvshows. Click "OK".
    • Configure Settings:
      • Set language and country as before.
      • Ensure "The TVDB" is checked under metadata downloaders.
      • Ensure "Enable Season Folders" is checked if your TV shows are organized like Show Name/Season 01/....
    • Click "OK" to save.
  5. Trigger Library Scans:

    • You should now see "Movies" and "TV Shows" listed under Libraries.
    • Hover over the "Movies" library, click the "..." menu, and select "Scan Library".
    • Repeat for the "TV Shows" library: hover, click "...", select "Scan Library".
  6. Monitor Scan and Verify:

    • Go to the "Overview" section of the Dashboard. You might see scan activity listed.
    • You can also check "Logs" for scanning messages.
    • Go back to the main Jellyfin interface (click the Home icon or Jellyfin logo top left).
    • As the scans complete, you should see your movies and TV shows appearing on the home screen under "Latest Movies," "Latest TV Shows," and within their respective library sections.
    • Click on items to verify that metadata (posters, summaries, cast info) has been downloaded correctly. If metadata is missing or incorrect, double-check your file names and folder structure against the recommended conventions. Use the "Identify" feature if necessary.

You have now successfully configured Jellyfin to recognize and display your media collection. You can repeat the process for other media types like music or photos by adding appropriate volume mappings to your docker-compose.yml and then creating the corresponding libraries in Jellyfin.

Intermediate Level

5. User Management and Access Control

As your Jellyfin server grows, or if you intend to share access with family members or friends, robust user management becomes essential. Jellyfin provides comprehensive tools to create separate accounts, control access to specific libraries, and implement parental controls. This ensures each user has a personalized experience and only sees content appropriate for them.

Why Multiple User Accounts?

  • Personalized Experience: Each user gets their own tracked watch progress ("Continue Watching," "Next Up"), watched status indicators, playback settings, and potentially customized home screen layouts or subtitle preferences.
  • Access Control: You can restrict which libraries certain users can access. For instance, you might want to hide your personal movie collection from guest users or restrict children to specific "Kids" libraries.
  • Parental Controls: Implement content restrictions based on official ratings (G, PG, PG-13, R, etc.) or block specific items using tags.
  • Security: Avoids sharing your administrator password. Regular users have limited permissions and cannot change server settings.

Creating New Users

  1. Navigate to Dashboard -> Users.
  2. Click the "+" icon or "Add User" button.
  3. Username: Enter a unique username for the new account.
  4. Password: Set a strong password for the user. You might need to confirm it.
  5. Click "Save". The user is created with default permissions.

Configuring User Permissions and Access

After creating a user, you need to configure their specific permissions and library access:

  1. On the Dashboard -> Users page, click on the username you want to configure.
  2. Profile Tab:
    • Allows setting a display name (if different from username) and managing the user's password.
    • Maximum Parental Rating: This is a key setting for parental controls. Select the highest rating (e.g., G, PG, TV-Y7) the user is allowed to view. Content rated higher than this will be hidden from the user. Ensure your libraries are configured to fetch ratings data (usually done by default if you set the country correctly during library setup).
    • Block items with tags: You can create specific tags (e.g., "scary," "explicit") and apply them to media items (via Edit Metadata). Enter those tags here to block any item containing them for this user, regardless of its rating. This offers fine-grained control beyond standard ratings.
    • Enable Access Schedule: Allows you to define specific times or days when the user is allowed to log in and use Jellyfin.
  3. Access Tab:
    • Enable access to all libraries: If checked, the user can see everything. Uncheck this for granular control.
    • Enabled Libraries: If the above is unchecked, you will see a list of all your configured libraries. Check the boxes only for the libraries this specific user should be able to access.
    • User Permissions: This section defines what the user can do on the server:
      • Is Administrator: Grants full administrative access (only give this to trusted server managers).
      • Enable User Deletion: Allows the user to delete their own profile from within their settings.
      • (Media Playback) Permissions related to playback control (usually enabled for all regular users).
      • (Live TV) Permissions for accessing Live TV features.
      • (SyncPlay) Permissions for creating/joining SyncPlay groups.
      • (Library Management) Permissions related to managing media content (e.g., editing metadata, deleting media). Be cautious granting these to non-admins, especially Enable deleting media from libraries.
      • (User Management) Permissions related to managing other users (only for admins).
      • (Dashboard) Allow access to the server dashboard (usually only for admins).
      • (Plugins) Allow managing plugins (usually only for admins).
  4. Parental Control Tab:
    • Provides another view/way to configure the Parental Rating and Blocked Tags settings found on the Profile tab.
    • Also allows setting an Access Schedule.
  5. Save: Remember to click "Save" after making any changes to a user's profile or access settings.

Password Policies and Security Best Practices

  • Strong Passwords: Encourage all users (especially the admin) to use strong, unique passwords.
  • Limit Admin Access: Only grant administrator privileges to users who absolutely need them for server management. Regular users should have minimal necessary permissions.
  • Regular Review: Periodically review user accounts and their permissions, removing accounts that are no longer needed.
  • Secure Remote Access: Implementing secure remote access (e.g., via a Reverse Proxy with HTTPS, covered later) is crucial to protect login credentials from being intercepted over the internet. Avoid exposing Jellyfin directly to the internet without encryption.

Effective user management is key to a secure and personalized multi-user media server environment.

Workshop Creating User Profiles and Setting Restrictions

  • Objective: Create a new non-administrator user, restrict their access to specific libraries, and apply parental controls based on ratings and tags.
  • Prerequisites:
    • Jellyfin server running.
    • Admin access to the Jellyfin Dashboard.
    • At least two libraries set up (e.g., "Movies" and "Kids Movies" or "TV Shows").
    • Some media items with standard content ratings (fetched during library scan).

Steps:

  1. Log in as Admin: Access your Jellyfin instance and log in with your administrator account.

  2. Navigate to User Management: Go to Dashboard -> Users.

  3. Create a New User:

    • Click the "+" icon.
    • Username: Enter KidUser.
    • Password: Set a password (e.g., password123 - use something more secure in a real scenario!). You might need to confirm it.
    • Click "Save".
  4. Configure Basic Access for KidUser:

    • Click on the newly created KidUser in the user list.
    • Go to the Access tab.
    • Crucially, uncheck the box for Enable access to all libraries.
    • A list of your libraries will appear below under "Enabled Libraries". Check the box(es) only for the library(ies) you want this user to see (e.g., check "Kids Movies" but leave "Movies" unchecked).
    • Review the User Permissions. Ensure Is Administrator is unchecked. Leave the default playback permissions enabled, but disable permissions like Enable deleting media from libraries unless specifically intended.
    • Click "Save" at the bottom.
  5. Apply Parental Controls (Rating):

    • Go to the Profile tab for KidUser.
    • Find the Maximum Parental Rating dropdown.
    • Select an appropriate rating limit (e.g., PG or TV-Y7). This means KidUser will not be able to see any movies rated PG-13, R, or TV shows rated TV-14, TV-MA, etc., even if they are in the libraries KidUser has access to.
    • Click "Save".
  6. Apply Parental Controls (Tag Blocking - Optional):

    • (Preparation) First, as the admin user, find a movie or show (even one within the allowed rating) that you want to specifically block (e.g., something potentially scary for a young child).
      • Navigate to the item in the main Jellyfin interface.
      • Click the three-dot menu (...) and select "Edit Metadata".
      • Scroll down to the "Tags" section.
      • Enter a unique tag, like scary-content, and press Enter or click Add.
      • Scroll down and click "Save".
    • (Configuration) Go back to Dashboard -> Users -> KidUser -> Profile tab.
    • In the Block items with tags field, enter the exact tag you just created: scary-content.
    • Click "Save".
  7. Set Access Schedule (Optional):

    • Go to the Parental Control tab for KidUser.
    • Under Access Schedule, click "Add Schedule".
    • Select the Day (e.g., Daily, Weekdays, or a specific day).
    • Set a Start time and End time during which the user is allowed access (e.g., Start 16:00, End 20:00 for after school).
    • Click "Save" on the schedule row, and then "Save" at the bottom of the user settings page.
  8. Verify Restrictions:

    • Log out of your admin account (click user icon top right -> Logout).
    • Log in as KidUser using the password you set.
    • Check Library Access: Verify that KidUser can only see the libraries you enabled (e.g., only "Kids Movies" appears, not "Movies").
    • Check Rating Restrictions: Browse the allowed library. Verify that items rated above the limit you set (e.g., PG-13 movies) are not visible.
    • Check Tag Restrictions: Verify that the specific item you tagged with scary-content is also not visible, even if its rating was within the allowed limit.
    • (If Schedule Set): Try logging in outside the allowed time window; access should be denied.

By completing this workshop, you've successfully created a restricted user account, demonstrating how to control library access and apply both rating-based and tag-based parental controls in Jellyfin.

6. Understanding Transcoding

Transcoding is one of the most critical, and often most resource-intensive, functions of a media server like Jellyfin. Understanding what it is, why it's needed, and how to configure it effectively is key to ensuring smooth playback for all your users across various devices and network conditions.

What is Transcoding?

Transcoding is the process of converting a digital media file (video or audio) from its original format, codec, container, resolution, or bitrate into another format on-the-fly as it's being streamed to a client device. It's essentially real-time file conversion tailored to the specific playback request.

Why is Transcoding Necessary?

There are two primary reasons why Jellyfin might need to transcode media:

  1. Client Compatibility: Not all client devices (Smart TVs, phones, browsers, streaming sticks) can play every single video and audio codec or container format in existence.
    • Example: Your movie file might be encoded using the modern HEVC (H.265) video codec for high efficiency, but the user is trying to watch it on an older Chromecast or web browser that only supports the older AVC (H.264) codec. Jellyfin's server detects this incompatibility and transcodes the video stream from HEVC to AVC in real-time so the client can play it. Similarly, audio formats like DTS or TrueHD might need transcoding to AC3 or AAC for compatibility with TVs or soundbars.
  2. Network Bandwidth Limitations: High-quality media files, especially 4K HDR content, can have very high bitrates (e.g., 50-100 Mbps or more). Streaming such files requires significant network bandwidth.
    • Example: A user is trying to watch a high-bitrate 4K movie remotely over their cellular connection, which has limited speed. Or, even within the home network, the Wi-Fi connection to a device might be weak. Jellyfin (or the user via client settings) can request a lower quality/bitrate stream (e.g., 1080p at 10 Mbps). The Jellyfin server will then transcode the original 4K file down to the requested resolution and bitrate to match the available bandwidth, preventing buffering and playback issues.

Direct Play vs. Direct Stream vs. Transcode

When a client requests media, Jellyfin determines the best delivery method:

  • Direct Play: The ideal scenario. The client device fully supports the video codec, audio codec, and container format of the original file, and there's enough network bandwidth. Jellyfin simply sends the original file directly to the client with minimal server resources used.
  • Direct Stream: Occurs when the client supports the underlying video and audio codecs, but not the container format (e.g., playing an MKV file on a device that prefers MP4). Jellyfin re-packages the existing video and audio streams into a compatible container format without re-encoding them. This process, often called "remuxing," uses very little CPU power.
  • Transcode: The most resource-intensive option. This happens when either the video codec, audio codec, or both are incompatible with the client, OR when the network bandwidth requires a lower bitrate/resolution version. Jellyfin must fully decode the original stream(s) and re-encode them into the required format/bitrate. This can consume significant CPU or GPU resources.

CPU vs. Hardware Acceleration (HWAccel)

Transcoding can be performed using two main methods:

  1. Software Transcoding (CPU): Uses the server's main processor (CPU) to perform the decoding and encoding tasks. This is universally compatible but extremely demanding on the CPU, especially for high-resolution video (1080p, 4K) or modern codecs (HEVC, AV1). A powerful CPU is required to handle even one or two simultaneous software transcodes without stuttering.
  2. Hardware-Accelerated Transcoding (HWAccel): Offloads the compute-intensive decoding and encoding tasks to specialized hardware components found in modern CPUs (integrated GPUs) or dedicated graphics cards (GPUs).
    • Intel Quick Sync Video (QSV): Found in most Intel Core processors since the 2nd generation (Sandy Bridge), though support for specific codecs (like HEVC, HEVC 10-bit, AV1) improved significantly in later generations (6th gen Skylake and newer are generally recommended for good HEVC support). Uses the integrated Intel GPU.
    • NVIDIA NVENC/NVDEC: Found in NVIDIA GeForce (consumer) and Quadro/Tesla (professional) graphics cards. NVENC handles encoding, NVDEC handles decoding. Offers excellent performance and wide codec support, especially on recent generations (Turing/Volta/Ampere/Ada Lovelace). Often requires dealing with driver session limits on consumer cards (though workarounds exist).
    • AMD AMF/VCN: Found in AMD Ryzen APUs (with integrated Radeon graphics) and dedicated Radeon GPUs. Support and performance have improved but historically lagged slightly behind Intel QSV and NVIDIA NVENC in terms of broad application support and sometimes quality/efficiency for certain codecs, though this gap is closing.
    • VAAPI / VDPAU (Linux): Lower-level APIs used on Linux to access hardware acceleration capabilities provided by Intel, AMD, and sometimes NVIDIA (via wrappers) drivers. Jellyfin often uses VAAPI for Intel and AMD on Linux.

Benefits of HWAccel: Massively reduces CPU usage during transcoding, allowing less powerful CPUs to handle multiple transcodes simultaneously. Consumes less power than heavy CPU transcoding. Drawbacks of HWAccel: Requires compatible hardware, correct driver installation on the host, and proper configuration within Jellyfin (and potentially Docker device mapping). Quality can sometimes be slightly lower than very high-quality software encodes, but for real-time streaming, the difference is often negligible and vastly outweighed by the performance gains.

Jellyfin Transcoding Settings

You can configure transcoding behavior in Dashboard -> Playback -> Transcoding:

  • Transcoding temporary path: Specifies where Jellyfin stores temporary transcoded file chunks during streaming. Ensure this location has sufficient free space (many GBs recommended, especially for long sessions or multiple users) and preferably resides on fast storage (SSD) for better performance. Inside Docker, this path corresponds to the /cache volume we mapped (e.g., ./jellyfin-cache on the host).
  • Hardware Acceleration: Dropdown menu to select the desired hardware acceleration method (e.g., VAAPI, QSV, NVENC, VDPAU, AMF). Selecting an option often reveals more specific settings below it (e.g., VAAPI device path). This requires compatible hardware and proper host setup first.
  • Enable hardware encoding: Check this box to allow Jellyfin to use HWAccel for the encoding part of the transcode (the most intensive part). You might also see options to enable specific encoders (HEVC, H.264). Only enable codecs your hardware actually supports for encoding.
  • Enable hardware decoding: Allows using HWAccel for the decoding part. Can sometimes help on very low-power systems or with specific codecs, but encoding is usually the bottleneck.
  • HDR Tone Mapping: If using HWAccel, this option attempts to convert High Dynamic Range (HDR) video to Standard Dynamic Range (SDR) during transcoding for clients that don't support HDR. This prevents colors from looking washed out. Quality varies depending on the HWAccel method and implementation.
  • Transcoding thread count: Limits how many CPU threads FFmpeg (the underlying transcoder) can use for software transcoding. 0 usually means auto (uses all available cores). Lowering this can prevent Jellyfin from overwhelming the CPU but will slow down transcoding.
  • Transcoding preset: Controls the trade-off between CPU usage and encoding quality/speed for software transcoding (e.g., veryfast, fast, medium). Faster presets use less CPU but result in lower quality/larger file size for the same bitrate.
  • Enable throttling: When enabled, Jellyfin tries to transcode slightly faster than real-time playback and then pauses, saving resources. Disabling it makes Jellyfin transcode as fast as possible, which might fill the temporary storage faster but can help ensure smooth playback on unstable connections by building a larger buffer ahead of time.

Understanding these options allows you to tailor Jellyfin's transcoding behavior to your specific hardware and needs, optimizing for either performance or quality.

Workshop Configuring and Testing Transcoding

  • Objective: Examine Jellyfin's transcoding settings, potentially enable hardware acceleration (if hardware is available and conceptually understood, even if not fully configured on host yet), and observe transcoding in action.
  • Prerequisites:
    • Jellyfin server running.
    • Admin access to the Jellyfin Dashboard.
    • At least one media file in your library that is likely to require transcoding for a typical web browser. Good candidates include files with:
      • HEVC (H.265) video codec (many browsers don't support it natively).
      • High bitrate 4K resolution (may exceed client/network limits).
      • "Unsupported" audio codecs like DTS, TrueHD, or multichannel FLAC (browsers typically want AAC or Opus).
    • Access to the server's command line to monitor resource usage (using htop or similar tools).

Steps:

  1. Install htop (Host Utility):

    • Connect to your server via SSH.
    • Install htop, a useful command-line system monitor:
      • Debian/Ubuntu: sudo apt update && sudo apt install htop -y
      • Fedora: sudo dnf install htop -y
  2. Review Default Transcoding Settings:

    • In Jellyfin, go to Dashboard -> Playback -> Transcoding.
    • Note the default Hardware Acceleration setting (usually "None").
    • Observe the Transcoding temporary path (should be /cache inside the container).
    • Check the default Transcoding thread count and preset.
  3. (Optional) Attempt to Configure Hardware Acceleration (Conceptual):

    • If you know you have compatible hardware (e.g., an Intel CPU newer than 6th Gen for QSV/VAAPI, or an NVIDIA GPU for NVENC):
      • Select the appropriate option from the Hardware Acceleration dropdown (e.g., VAAPI for Intel/AMD on Linux, NVENC for NVIDIA).
      • Review any new options that appear (e.g., VAAPI device path, usually /dev/dri/renderD128).
      • Check the boxes for Enable hardware encoding and potentially Enable hardware decoding for relevant codecs (e.g., H.264, HEVC).
      • Check Enable HDR Tone Mapping if you have HDR content and non-HDR clients.
      • Important: This step only configures Jellyfin. For HWAccel to actually work inside Docker, you would also need to:
        • Install the correct drivers on the host system.
        • Modify your docker-compose.yml to pass the graphics device(s) into the container (e.g., adding a devices section mapping /dev/dri).
        • Ensure correct file permissions for the device nodes.
        • (This full setup is covered in the Advanced section).
      • Click "Save". Even if HWAccel isn't fully set up on the host/Docker yet, saving the setting here allows you to see if Jellyfin attempts to use it later.
  4. Initiate Playback Requiring Transcoding:

    • Open Jellyfin in your web browser (acting as the client).
    • Find and play the media file you identified as likely needing transcoding (e.g., the HEVC or 4K file).
    • Let the video start playing.
  5. Monitor Server Resources:

    • Switch to your SSH terminal connected to the server.
    • Run htop.
    • Observe the CPU Usage bars at the top. If Jellyfin is software transcoding, you will likely see one or more CPU cores hitting near 100% utilization, and the ffmpeg process will be prominent in the process list.
    • (If you successfully configured HWAccel): If hardware acceleration is working, the CPU usage should remain relatively low, even while transcoding. You would need other tools (intel_gpu_top for Intel, nvidia-smi for NVIDIA) to see the GPU utilization instead.
  6. Analyze Playback Info in Jellyfin:

    • While the media is playing, go back to the Jellyfin Dashboard -> Activity -> Active Streams.
    • Find your current playback session. Click the "i" (Info) icon next to it.
    • A detailed overlay will appear showing the Playback Info. Look carefully:
      • Play Method: Does it say Transcode, Direct Stream, or Direct Play?
      • Transcode Reason: If it's transcoding, Jellyfin will usually state why (e.g., "Video codec not supported," "Audio codec not supported," "Container not supported," "Bitrate exceeds limit").
      • Source vs. Output: Compare the video codec, resolution, bitrate, and audio codec of the original file (Source) versus what's being sent to the client (Output). You should see the changes made by the transcoder.
  7. Experiment with Client Quality Settings:

    • Go back to the browser tab where the video is playing.
    • Click the settings cogwheel icon within the player controls.
    • Select Quality. You might see "Auto" or the original resolution.
    • Manually select a lower resolution (e.g., force 720p or 480p).
    • Go back to the Dashboard -> Activity -> Active Streams info panel and refresh it (or wait a few seconds). Observe how the Output details change and note the Transcode Reason (likely now includes "Bitrate exceeds limit" or similar). Monitor htop again; forcing a lower resolution might still require transcoding but could potentially use slightly less CPU/GPU resource than transcoding to a higher resolution.
  8. Stop Playback and Observe:

    • Stop the video playback in the client.
    • Observe htop on the server; the high CPU usage (or GPU usage if HWAccel was active) associated with ffmpeg should drop significantly.
    • Check the Dashboard -> Activity page; the active stream should disappear.

This workshop demonstrated how to identify when transcoding occurs, why it happens (client/network limitations), how to monitor its impact on server resources (CPU usage via htop), and how client-side quality settings interact with the server's transcoding decisions. It also highlighted where hardware acceleration is configured within Jellyfin, setting the stage for the more detailed HWAccel setup later.

7. Networking and Remote Access

By default, your freshly installed Jellyfin server is only accessible to devices on the same local network (LAN). To access your media library from outside your home (e.g., on your phone while traveling, or sharing with friends/family), you need to configure remote access. This involves understanding basic networking concepts and choosing a secure method to expose Jellyfin to the internet.

Basic Networking Concepts Recap:

  • IP Address: A unique numerical label assigned to each device connected to a network.
    • Private IP Address: Used within your local network (e.g., 192.168.1.x, 10.0.0.x). These are not reachable directly from the internet. Your server running Jellyfin has a private IP.
    • Public IP Address: Assigned to your router by your Internet Service Provider (ISP). This is the single address the outside world sees for your entire home network. It can be static (doesn't change) or dynamic (changes periodically).
  • Ports: Numerical identifiers (0-65535) used to differentiate services running on the same device/IP address. Jellyfin, by default, listens for HTTP traffic on port 8096 and potentially HTTPS on port 8920 if configured internally. Web browsers use port 80 for HTTP and 443 for HTTPS by default.
  • Firewall: Software or hardware that controls incoming and outgoing network traffic based on predefined rules, blocking potentially malicious connections. Your router has a built-in firewall, and your server OS likely has one too (like ufw on Ubuntu or firewalld on Fedora).
  • NAT (Network Address Translation): The mechanism your home router uses to allow multiple devices with private IPs on your LAN to share the single public IP address provided by your ISP. It translates outgoing requests and directs incoming responses back to the correct internal device.
  • Port Forwarding: A router configuration that tells the router to forward incoming traffic arriving on a specific public port to a specific private IP address and port within your local network. This is necessary to make an internal service like Jellyfin reachable from the outside.

Accessing Jellyfin Locally

As established, you can access Jellyfin from any device on your LAN by navigating to http://<server-local-ip>:8096 in a web browser or by using a Jellyfin client app, which often discovers the server automatically using protocols like UDP broadcast on ports 1900 and 7359 (if these ports are open on the server's firewall and potentially mapped in Docker if using bridge mode).

Methods for Remote Access

Exposing a service directly to the internet requires careful consideration of security. Here are the common methods, ranging from simple but less secure to more complex but highly recommended:

  1. Port Forwarding (Direct Exposure - Use with Caution):

    • How it works: You configure your home router to forward incoming traffic on a specific external port (e.g., 8096) directly to your Jellyfin server's internal IP address and port (8096).
    • Pros: Simple to set up (requires only router configuration).
    • Cons:
      • Major Security Risk: Exposes the Jellyfin login page directly to the entire internet on its default (or chosen) port. Your server becomes a target for automated bots scanning for open ports and known vulnerabilities. If a security flaw exists in Jellyfin or its dependencies, your server could be compromised.
      • No Encryption (HTTP): If you only forward port 8096, login credentials and streaming data are sent unencrypted (plain HTTP), making them vulnerable to interception (Man-in-the-Middle attacks), especially on public Wi-Fi.
      • Requires Public IP: You need to know your router's public IP address, which might be dynamic (changing). Dynamic DNS (DDNS) services can help manage changing IPs but add complexity.
    • Recommendation: Generally NOT RECOMMENDED for security reasons, especially without HTTPS.
  2. VPN (Virtual Private Network):

    • How it works: You set up a VPN server on your home network (e.g., WireGuard, OpenVPN, or using services like Tailscale/ZeroTier). Your remote devices connect to this VPN server, effectively placing them inside your local network securely. Once connected via VPN, you access Jellyfin using its private IP address (http://<server-local-ip>:8096) as if you were at home.
    • Pros:
      • Very Secure: All traffic between the remote client and your home network is encrypted through the VPN tunnel. Jellyfin itself is never directly exposed to the public internet.
      • Access to Other Services: Provides secure access to other services on your LAN as well.
    • Cons:
      • Requires setting up and managing a VPN server (can be complex, though tools like Tailscale simplify this significantly).
      • Requires installing and running a VPN client application on every remote device needing access.
      • Can sometimes introduce latency or slight bandwidth overhead.
    • Recommendation: Excellent security choice, especially if you already use a VPN for other purposes or prefer not to expose any services directly. Tailscale is particularly user-friendly for this.
  3. Reverse Proxy (Recommended Method):

    • How it works: You run a reverse proxy server (like Nginx, Caddy, Traefik, or Nginx Proxy Manager) on your network. This proxy is exposed to the internet (typically on standard ports 80 and 443). You configure your router to forward ports 80 (for HTTP verification/redirect) and 443 (for HTTPS) to the reverse proxy server. The reverse proxy then accepts incoming requests (e.g., to https://jellyfin.yourdomain.com), handles the SSL/TLS encryption (HTTPS), and forwards the request internally to the actual Jellyfin server (http://<server-local-ip>:8096).
    • Pros:
      • Strong Security (HTTPS): Encrypts all traffic between the client and the proxy, protecting credentials and data. Uses standard, trusted SSL/TLS certificates (often obtained automatically for free via Let's Encrypt).
      • Hides Jellyfin: The Jellyfin server itself and its specific port (8096) are not directly exposed to the internet. Only the proxy on ports 80/443 is visible.
      • Custom Domain Name: Allows accessing Jellyfin via an easy-to-remember domain name (e.g., jellyfin.mydomain.com) instead of an IP address. Requires owning a domain name.
      • Centralized Access Point: Can manage access for multiple self-hosted services through the same proxy.
      • Additional Security Features: Proxies can add security headers, rate limiting, access controls, etc.
    • Cons:
      • More complex initial setup than simple port forwarding (requires setting up the proxy service, obtaining a domain name, configuring DNS).
      • Requires understanding reverse proxy concepts.
    • Recommendation: This is the most common and highly recommended method for securely exposing web-based services like Jellyfin. It provides the best balance of security, usability (HTTPS, domain name), and flexibility. Tools like Nginx Proxy Manager or Caddy significantly simplify the setup process.

Focus Moving Forward

Given the security benefits, the Advanced section of this guide will focus on setting up a Reverse Proxy (specifically Nginx Proxy Manager as a user-friendly example) to provide secure HTTPS remote access to your Jellyfin instance.

Workshop Enabling Local Network Access and Understanding Ports

  • Objective: Verify Jellyfin is accessible on the local network, identify the server's IP and the port Jellyfin uses, and understand conceptually how port forwarding would work (while acknowledging its risks).
  • Prerequisites:
    • Jellyfin server running via Docker Compose (using network_mode: 'host' as in the basic setup, or using bridge mode with port 8096:8096 mapped).
    • Access to the server's command line (SSH).
    • Another device (computer, phone) on the same local network.

Steps:

  1. Identify Server's Local IP Address:

    • Connect to your Jellyfin server via SSH.
    • Run the ip addr command:
      ip addr show
      
    • Look for your main network interface (often named eth0, ensX, enpXsY, or wlan0 if wireless). Find the inet line under that interface. It will show the server's private IP address (e.g., inet 192.168.1.105/24). Note this IP address.
  2. Verify Local Access from Another Device:

    • On your other device (laptop, phone) connected to the same Wi-Fi or wired network, open a web browser.
    • In the address bar, type http://<server-local-ip>:8096, replacing <server-local-ip> with the IP address you found in Step 1. (e.g., http://192.168.1.105:8096).
    • The Jellyfin login page or main interface should load. This confirms local network access is working.
  3. Identify the Port:

    • Notice that you needed to include :8096 after the IP address. This is the default port Jellyfin listens on for HTTP connections.
  4. Relate Port to Docker Configuration:

    • Go back to your server's SSH session.
    • Navigate to your Jellyfin Docker project directory (cd ~/jellyfin-docker).
    • View your docker-compose.yml file (cat docker-compose.yml).
    • If using network_mode: 'host': Jellyfin directly uses the host's port 8096 because it shares the host's network.
    • If using bridge mode (no network_mode: 'host'): Look for the ports: section. You should see a line like - "8096:8096". This explicitly maps port 8096 on the host machine to port 8096 inside the Jellyfin container. The format is <host_port>:<container_port>. If you had changed the host port (e.g., - "8888:8096"), you would access Jellyfin locally via http://<server-local-ip>:8888.
  5. Conceptual Port Forwarding (Do Not Implement Blindly):

    • (This is purely conceptual for understanding - implementing this directly is insecure as discussed).
    • To allow access from the internet using this method, you would:
      • Log in to your home router's administration interface (usually via a web browser pointed at your router's IP, like 192.168.1.1 or 192.168.0.1).
      • Find the "Port Forwarding," "NAT Forwarding," or "Virtual Server" section.
      • Create a new rule:
        • External Port (or Service Port): 8096 (or another port if you want to obscure it slightly, e.g., 32401).
        • Internal Port: 8096 (the port Jellyfin is listening on).
        • Internal IP Address: The local IP address of your Jellyfin server (from Step 1, e.g., 192.168.1.105).
        • Protocol: TCP.
        • Save/Apply the rule.
    • Again, strongly advise against this due to security risks. You would then (theoretically) access Jellyfin remotely via http://<your-public-ip>:<external_port>.
  6. Reinforce Preferred Method:

    • Conclude by reiterating that while port forwarding is technically possible, it lacks encryption (HTTP) and directly exposes the service. Using a Reverse Proxy (like Nginx Proxy Manager, Caddy, Traefik) is the standard, secure way to achieve remote access with HTTPS encryption and a custom domain name. The reverse proxy handles the secure connection from the outside world and forwards traffic internally to Jellyfin.

This workshop confirmed local accessibility and illustrated the role of ports in accessing Jellyfin. It conceptually outlined port forwarding while emphasizing the security advantages of using a reverse proxy, which will be detailed in the advanced section.

Advanced Level

8. Securing Jellyfin with a Reverse Proxy and HTTPS

Directly exposing Jellyfin (especially over HTTP) to the internet is a significant security risk. The standard and highly recommended approach for secure remote access is to use a reverse proxy. This acts as a gateway, accepting connections from the internet (ideally over HTTPS), and forwarding them securely to your Jellyfin instance on your local network. We'll focus on setting up Nginx Proxy Manager (NPM), a user-friendly, Docker-based reverse proxy with a web UI that simplifies configuration and automates SSL certificate management using Let's Encrypt.

Why Nginx Proxy Manager (NPM)?

  • User-Friendly Web UI: Makes configuring proxy hosts, redirects, and SSL certificates much easier than manually editing Nginx configuration files.
  • Docker-Based: Runs as a Docker container, aligning well with our Jellyfin setup.
  • Let's Encrypt Integration: Automates the process of obtaining and renewing free, trusted SSL/TLS certificates, enabling HTTPS.
  • Feature Rich: Supports proxying to multiple backend services, access lists, custom Nginx configurations, basic HTTP authentication, and more.

Prerequisites for Reverse Proxy Setup:

  1. Domain Name: You need a registered domain name (e.g., yourdomain.com). You can purchase one from various registrars (Namecheap, GoDaddy, Cloudflare Registrar, etc.). Free subdomain services (like DuckDNS or No-IP) can also work but might be less reliable or have limitations.
  2. Public IP Address: Your home network needs a public IP address from your ISP. It can be static or dynamic.
    • If dynamic, you'll need a Dynamic DNS (DDNS) client configured either on your router or on a machine on your network (NPM itself doesn't have built-in DDNS client functionality, but many routers do, or you can run a separate DDNS updater container). This DDNS service will automatically update your domain's DNS record whenever your public IP changes.
  3. DNS Configuration: You need access to your domain's DNS settings (usually provided by your domain registrar or a service like Cloudflare). You must create an A record (or CNAME if using DDNS) for the subdomain you want to use for Jellyfin (e.g., jellyfin.yourdomain.com) and point it to your public IP address.
  4. Port Forwarding (Ports 80 & 443): Unlike forwarding Jellyfin's port, you must forward TCP ports 80 and 443 from your router to the local IP address of the machine running Nginx Proxy Manager.
    • Port 80 (HTTP) is required by Let's Encrypt for the HTTP-01 challenge validation process to issue SSL certificates.
    • Port 443 (HTTPS) is the standard port for secure web traffic, which NPM will listen on.
  5. Docker and Docker Compose: Assumed to be installed and working on the host where you'll run NPM.

Conceptual Flow:

  1. User requests https://jellyfin.yourdomain.com in their browser.
  2. DNS resolves jellyfin.yourdomain.com to your public IP address.
  3. Request hits your router on port 443.
  4. Router forwards the port 443 traffic to the internal IP address of the machine running NPM.
  5. Nginx Proxy Manager receives the request.
  6. NPM handles the TLS termination (decryption using its Let's Encrypt certificate).
  7. NPM looks up its configuration for jellyfin.yourdomain.com.
  8. NPM forwards the (now decrypted, HTTP) request internally to your Jellyfin container's IP address and port (e.g., http://192.168.1.105:8096).
  9. Jellyfin processes the request and sends the response back to NPM.
  10. NPM encrypts the response using TLS and sends it back to the user's browser.

Workshop Setting up Nginx Proxy Manager for Jellyfin with Let's Encrypt

  • Objective: Install Nginx Proxy Manager via Docker Compose, configure it to proxy requests to Jellyfin, and secure the connection with a free Let's Encrypt SSL certificate.
  • Prerequisites:
    • All prerequisites listed above (Domain name, DNS configured, Ports 80/443 forwarded to the host where NPM will run, Docker/Compose installed).
    • Jellyfin running in Docker (accessible via its local IP and port, e.g., http://192.168.1.105:8096). Determine the specific local IP of the machine running your Jellyfin container or if NPM runs on the same Docker host, you can often use the Jellyfin container name if they share a Docker network.

Steps:

  1. Create NPM Docker Compose File:

    • Connect via SSH to the host where you forwarded ports 80/443. This could be the same host running Jellyfin or a different one.
    • Create a directory for NPM:
      cd ~
      mkdir npm-docker
      cd npm-docker
      
    • Create the docker-compose.yml file:
      nano docker-compose.yml
      
    • Paste the following configuration:

      version: '3.8'
      services:
        app:
          image: 'jc21/nginx-proxy-manager:latest'
          container_name: npm-app
          restart: unless-stopped
          ports:
            # Public Ports - Make sure ports 80 and 443 are forwarded to this host machine
            - '80:8080'   # NPM internal HTTP port mapped to host's port 80
            - '443:4443'  # NPM internal HTTPS port mapped to host's port 443
            # Admin UI Port - Only expose this locally or secure it appropriately
            - '127.0.0.1:81:8181' # Map NPM Admin UI port 8181 to host port 81, BUT ONLY ON localhost
                                  # Access via SSH tunnel or from the host itself: http://localhost:81
                                  # To expose it on your LAN (use with caution, secure NPM login): replace 127.0.0.1:81 with just '81:8181'
          volumes:
            - ./data:/data # Persists NPM configuration and Let's Encrypt certificates
            - ./letsencrypt:/etc/letsencrypt # Persists Let's Encrypt certificates (redundant but good practice)
          environment:
            # Set to 1 to disable healthchecks if needed, e.g. behind another proxy
            DISABLE_IPV6: 'true' # Optional: Disable IPv6 if not used/configured
      
      # Optional: Define a network if Jellyfin and NPM are on the same host
      # and you want them to communicate via container name
      # networks:
      #  proxy-network:
      #    driver: bridge
      
      # Note: If Jellyfin is on the same host, you might need to add
      # the 'npm-app' service and the 'jellyfin' service to the same
      # docker network (e.g., 'proxy-network') in both compose files
      # so NPM can resolve the name 'jellyfin'. Otherwise, use Jellyfin's host IP.
      
    • Explanation:

      • image: jc21/nginx-proxy-manager:latest: Uses the official NPM image.
      • ports::
        • 80:8080: Maps host port 80 to NPM's internal HTTP port 8080. NPM listens for Let's Encrypt challenges here.
        • 443:4443: Maps host port 443 to NPM's internal HTTPS port 4443. This is where your secure traffic will arrive.
        • 127.0.0.1:81:8181: Maps NPM's internal admin web UI port 8181 to port 81 on the host, but only accessible from the host machine itself (127.0.0.1). This is a security measure. To access it from your laptop on the same LAN, you'd either use an SSH tunnel (recommended) or change 127.0.0.1:81 to 81:8181 (less secure, relies solely on NPM's login).
      • volumes:: Maps ./data and ./letsencrypt on the host to persist all NPM data and certificates.
      • environment: (Optional): DISABLE_IPV6 can prevent issues if IPv6 isn't fully configured on your network/host.
  2. Run NPM Docker Compose:

    • Save and close the docker-compose.yml file (Ctrl+X, Y, Enter in nano).
    • Start NPM:
      docker compose up -d
      
  3. Access NPM Web UI:

    • Method A (SSH Tunnel - Recommended): From your local machine (not the server), open a terminal and run:
      ssh -L 8181:localhost:81 your_username@your_npm_server_ip
      
      (Replace your_username and your_npm_server_ip). This forwards connections to port 8181 on your local machine through the SSH tunnel to port 81 on the server (where NPM's UI is listening locally). Now open your browser and go to http://localhost:8181.
    • Method B (Direct LAN Access - If you changed the port mapping): If you changed the mapping in docker-compose.yml to 81:8181, you can access NPM directly from another machine on your LAN via http://<npm-server-ip>:81.
    • First Login: The default administrator login credentials for NPM are:
      • Email: admin@example.com
      • Password: changeme
    • You will be immediately prompted to change these details. Use your real email address (important for Let's Encrypt notifications) and set a very strong password.
  4. Add Proxy Host for Jellyfin:

    • Once logged into NPM, navigate to Hosts -> Proxy Hosts.
    • Click "Add Proxy Host".
    • Details Tab:
      • Domain Names: Enter the subdomain you configured in your DNS (e.g., jellyfin.yourdomain.com). Add any variations if needed (e.g., www.jellyfin...).
      • Scheme: Select http (because NPM will talk to Jellyfin internally over plain HTTP).
      • Forward Hostname / IP: Enter the local IP address of your Jellyfin server (e.g., 192.168.1.105). Alternatively, if NPM and Jellyfin are running on the same Docker host and connected via a shared Docker network (requires adjusting both docker-compose.yml files to use the same network), you could potentially use the Jellyfin container name (e.g., jellyfin). Using the IP is often more reliable initially.
      • Forward Port: Enter the port Jellyfin is listening on (e.g., 8096).
      • Cache Assets: Optional, can improve performance for static assets.
      • Block Common Exploits: Recommended to enable this basic security feature.
      • Websockets Support: Enable this. Jellyfin uses WebSockets for real-time communication with clients (like playback progress updates).
    • SSL Tab:
      • SSL Certificate: Select "Request a new SSL Certificate".
      • Force SSL: Enable this. Automatically redirects any HTTP requests to HTTPS.
      • HTTP/2 Support: Recommended to enable for performance.
      • HSTS Enabled: Recommended to enable after confirming everything works. Tells browsers to always use HTTPS for this site.
      • Email Address for Let's Encrypt: Enter your real email address.
      • I Agree: Check the box to agree to the Let's Encrypt Terms of Service.
    • Save: Click the "Save" button.
  5. Wait for Certificate Issuance: NPM will now attempt to contact Let's Encrypt to obtain the certificate. This involves Let's Encrypt verifying that you control the domain by making an HTTP request to http://jellyfin.yourdomain.com/.well-known/acme-challenge/.... This is why port 80 forwarding must be working correctly and your DNS must be pointing to your public IP. You should see the status change from "Offline" to "Online" with a green lock icon if successful. It might take a minute or two. Check the logs (docker compose logs -f app in the npm-docker directory) if it fails. Common issues are incorrect port forwarding, firewall blocking port 80, or incorrect DNS settings.

  6. Test Remote Access:

    • On a device outside your local network (e.g., your smartphone using cellular data, or use an online proxy checker), open a web browser.
    • Navigate to https://jellyfin.yourdomain.com (using https).
    • You should see your Jellyfin login page, served securely over HTTPS (check for the padlock icon in your browser's address bar).
  7. (Optional but Recommended) Configure Jellyfin Networking Settings:

    • Log in to Jellyfin as admin.
    • Go to Dashboard -> Networking.
    • Public HTTPS port: Set this to 443.
    • Public HTTP port: Set this to 80 (even though traffic arrives on 443, internal redirects might use this).
    • Base URL: Enter your full public URL: https://jellyfin.yourdomain.com.
    • Known Proxies: Enter the Docker subnet IP range if NPM and Jellyfin are on the same Docker network, or the specific local IP address of the NPM host machine if they are on different machines/networks. This tells Jellyfin to trust headers like X-Forwarded-For from the proxy to correctly identify client IP addresses in logs, etc. Finding the Docker subnet can sometimes be tricky (e.g., docker network inspect <network_name>), often it's something like 172.16.0.0/12 or 192.168.0.0/16 depending on your Docker setup. You might need to experiment or check NPM logs to see what IP it's using to connect to Jellyfin. You can often just put the specific local IP of the machine running NPM.
    • Save the settings.

You have now successfully secured your Jellyfin instance using Nginx Proxy Manager, enabling encrypted remote access via HTTPS with a valid certificate. This is a crucial step for protecting your data and login credentials.

9. Hardware Transcoding In-Depth (QSV/NVENC/VAAPI)

While software transcoding works, it heavily taxes the CPU. Offloading this work to dedicated hardware using Hardware Acceleration (HWAccel) drastically improves performance, allows for more simultaneous transcodes, and reduces power consumption. This section delves deeper into configuring HWAccel within Jellyfin, focusing on the common methods used with Docker on Linux hosts: Intel Quick Sync Video (QSV) via VAAPI, and NVIDIA NVENC/NVDEC.

Understanding the APIs and Requirements:

  • VAAPI (Video Acceleration API): The standard framework for hardware video acceleration on Linux, primarily used by Intel (for QSV) and AMD GPUs. Requires appropriate drivers installed on the host system.
    • Intel: Needs the intel-media-driver (modern, recommended) or the older i965-va-driver. The specific package name varies by distribution (e.g., intel-media-driver-non-free on Debian/Ubuntu, intel-media-driver on Fedora).
    • AMD: Needs the mesa-va-drivers (for open-source amdgpu driver) or potentially proprietary amdgpu-pro drivers (less common for Jellyfin).
  • QSV (Intel Quick Sync Video): Intel's specific hardware implementation accessed via VAAPI on Linux or Intel's Media SDK on Windows. Performance and codec support vary significantly by CPU generation (newer is better, 6th Gen+ recommended for HEVC).
  • NVENC/NVDEC (NVIDIA): NVIDIA's proprietary hardware encoder (NVENC) and decoder (NVDEC). Requires the official NVIDIA proprietary drivers installed on the host system and the NVIDIA Container Toolkit installed for Docker. Offers excellent performance but consumer cards (GeForce) often have an artificial limit on the number of concurrent encoding sessions (typically 3-5, though decoding is usually unlimited). Patches exist to bypass this limit, but use them at your own risk.
  • AMF (AMD Advanced Media Framework): AMD's framework, often used on Windows. On Linux, VAAPI is more common for Jellyfin integration with AMD hardware.

Key Steps for Enabling HWAccel in Docker:

  1. Install Host Drivers: Ensure the correct drivers for your hardware (Intel integrated graphics or NVIDIA GPU) are installed and working on the Linux host system outside of Docker first.
  2. Install Docker Support Toolkit (NVIDIA Only): If using NVIDIA, install the nvidia-container-toolkit (follow official NVIDIA docs). This allows Docker containers to access the GPU and drivers.
  3. Modify docker-compose.yml: Add specific sections to your Jellyfin docker-compose.yml file to pass the necessary hardware device(s) or runtime information into the container.
  4. Set Permissions: Ensure the user running the Jellyfin process inside the container has the necessary permissions to access the hardware device nodes passed from the host (e.g., /dev/dri/* for VAAPI, NVIDIA handles this differently).
  5. Configure Jellyfin Dashboard: Select the appropriate HWAccel method in Dashboard -> Playback -> Transcoding and configure its specific options.
  6. Verify: Test transcoding and check logs/system utilities to confirm HWAccel is active.

Tone Mapping (HDR -> SDR)

A crucial aspect of HWAccel is tone mapping. If you have High Dynamic Range (HDR) content (often 10-bit HEVC) but are streaming to a Standard Dynamic Range (SDR) client (most monitors, older TVs, phones), transcoding without tone mapping will result in washed-out, incorrect colors. Enabling tone mapping during transcoding attempts to convert the HDR color/luminance range to SDR appropriately.

  • Jellyfin Support: Jellyfin supports tone mapping for VAAPI, QSV, and NVENC, but success and quality can depend heavily on the specific driver versions, hardware generation, and Jellyfin version.
  • Configuration: Usually a checkbox within the HWAccel settings in the Jellyfin Dashboard (e.g., "Enable HDR Tone Mapping"). It often requires specific codecs (like HEVC 10-bit) to be enabled for hardware decoding/encoding as well.

Troubleshooting Common Issues:

  • Permissions Denied: Jellyfin logs might show errors accessing /dev/dri/* (VAAPI) or GPU resources. This usually means the user ID/group ID running Jellyfin inside the container doesn't have access on the host. Solutions involve:
    • Using the user: UID:GID directive in docker-compose.yml matching a host user who is in the correct group (render or video for VAAPI, docker group might be relevant for NVIDIA toolkit).
    • Explicitly adding the device group's GID using group_add: in docker-compose.yml.
    • Less securely, changing host device permissions (not recommended).
  • Incorrect Drivers: HWAccel options might not appear, or transcoding fails immediately. Ensure correct host drivers are installed and compatible with your hardware and kernel. For NVIDIA, make sure the nvidia-container-toolkit is properly set up.
  • Unsupported Codec/Format: Not all hardware can accelerate all codecs (e.g., older Intel QSV might not handle HEVC 10-bit or AV1). Transcoding might fail or fall back to software for unsupported parts. Check your hardware's specifications.
  • Docker Device Mapping Incorrect: Double-check the devices: section (VAAPI) or runtime: nvidia and environment: variables (NVIDIA) in your docker-compose.yml.
  • FFmpeg Issues: Jellyfin relies on FFmpeg. Errors in the Jellyfin logs mentioning FFmpeg often point to configuration or driver problems. Check the detailed FFmpeg log accessible via the Dashboard during a transcode attempt.

Workshop Enabling and Verifying Intel QSV Hardware Transcoding via VAAPI in Docker

  • Objective: Configure Jellyfin running in Docker to use Intel Quick Sync Video (QSV) for hardware-accelerated transcoding via the VAAPI interface on a Linux host.
  • Prerequisites:
    • A Linux host system with an Intel CPU featuring Quick Sync Video (ideally 6th Gen "Skylake" or newer for good HEVC support).
    • Appropriate Intel graphics drivers installed on the host. On modern Debian/Ubuntu, this usually means installing intel-media-driver-non-free:
      sudo apt update
      sudo apt install intel-media-driver-non-free vainfo -y
      # On Fedora: sudo dnf install intel-media-driver libva-utils
      
    • Verify VAAPI is detected on the host: Run vainfo. It should not report errors like "Failed to initialize VAAPI connection" and should list supported codecs/profiles (e.g., H264, HEVC Main, HEVC Main 10). If it errors, driver installation failed or is incomplete.
    • Jellyfin running via Docker Compose.
    • SSH access to the host.
    • A media file that requires transcoding (e.g., HEVC 10-bit HDR if testing tone mapping, or just HEVC/H.264 if testing basic acceleration).

Steps:

  1. Check Host Device Permissions:

    • VAAPI uses device nodes under /dev/dri, typically renderD128. Check the group ownership:
      ls -l /dev/dri/renderD128
      
      Output will look like crw-rw---- 1 root render ... /dev/dri/renderD128. Note the group (here it's render). On some systems it might be video.
    • Find the GID (Group ID) of this group:
      getent group render | cut -d: -f3
      # Or: getent group video | cut -d: -f3
      
      Note this GID number (e.g., 108).
    • Find the UID and GID of the user you intend to run Jellyfin as (ideally your own user, if added to the docker group):
      id -u # Your UID
      id -g # Your GID
      
      Note these numbers (e.g., 1000 and 1000).
  2. Stop Jellyfin Container:

    • Navigate to your Jellyfin Docker project directory (cd ~/jellyfin-docker).
    • Stop and remove the existing container (your config is safe in the mapped volumes):
      docker compose down
      
  3. Modify docker-compose.yml:

    • Edit the file: nano docker-compose.yml.
    • Add the devices section and potentially group_add under the jellyfin service:

      version: '3.8'
      services:
        jellyfin:
          image: jellyfin/jellyfin:latest
          container_name: jellyfin
          user: 1000:1000 # IMPORTANT: Use the UID:GID found in Step 1
          network_mode: 'host' # Or bridge mode with ports as before
          volumes:
            - ./jellyfin-config:/config
            - ./jellyfin-cache:/cache
            - /path/to/your/movies:/media/movies:ro
            - /path/to/your/tvshows:/media/tvshows:ro
            # Add other media volumes
          # ---- Add/Modify lines below ----
          devices:
            - /dev/dri:/dev/dri # Pass the entire dri directory into the container
          group_add:
            - "108" # IMPORTANT: Add the GID of the 'render' or 'video' group found in Step 1
                    # Add as a string in quotes
          # ---- End of added/modified lines ----
          restart: unless-stopped
      
    • Explanation of Changes:

      • user: 1000:1000: Ensure this matches your host user's UID/GID for consistent permissions.
      • devices:: This maps the host's /dev/dri directory (containing the graphics device nodes) into the container at the same path /dev/dri.
      • group_add:: This adds the container user (defined by user:) to the specified group inside the container. By adding the GID of the host's render (or video) group, the Jellyfin process gains the necessary permissions to access the /dev/dri/renderD128 device node that was passed through.
  4. Save and Start Jellyfin:

    • Save the docker-compose.yml file (Ctrl+X, Y, Enter).
    • Start Jellyfin with the new configuration:
      docker compose up -d
      
  5. Configure Jellyfin Dashboard for VAAPI:

    • Log in to Jellyfin as admin.
    • Go to Dashboard -> Playback -> Transcoding.
    • Hardware Acceleration: Select VAAPI.
    • VAAPI Device: Enter the path to the render node, usually /dev/dri/renderD128.
    • Enable Hardware Encoding: Check this box. Select specific codecs (H.264, HEVC) if your hardware supports them via VAAPI (check vainfo output on host).
    • (Optional) Enable HEVC Encoding / Enable 10-bit HEVC Encoding: Check if needed and supported.
    • Enable Hardware Decoding: Optionally check this for relevant codecs.
    • Enable HDR Tone Mapping: Check this if you have HDR content and SDR clients, and your Intel GPU/driver supports VAAPI tone mapping (generally Gen 11 Iris Plus / Iris Xe or newer work best).
    • Save the settings at the bottom.
  6. Test Transcoding:

    • Play a media file known to require transcoding (e.g., HEVC file played in a browser that needs H.264).
    • Ideally, use an HDR file if testing tone mapping.
  7. Verify HWAccel is Active:

    • Jellyfin Dashboard: Go to Dashboard -> Activity -> Active Streams. Click the 'i' icon for your stream. Check the Playback Info. The Transcode Reason might still be the same, but the process should now be hardware accelerated. You might see references to VAAPI in the details, but often the best verification is external. Look closely at the FFmpeg log snippet available there.
    • Jellyfin Logs: Check the main logs (Dashboard -> Logs) or container logs (docker compose logs jellyfin) for messages related to FFmpeg and VAAPI initialization during the transcode start. Look for lines like Trying to use hardware accelerated transcoding VAAPI. Filter for FFmpeg: docker compose logs jellyfin | grep -iE 'ffmpeg|vaapi'. Success messages often include "[AVHWDeviceContext @ ...] VAAPI driver initialized" or similar. Error messages will indicate problems.
    • Host CPU Usage: Run htop on the host. CPU usage should be significantly lower during the transcode compared to software transcoding. The ffmpeg process should not be consuming 100% of multiple CPU cores.
    • Host GPU Usage (Intel): Install intel-gpu-tools (sudo apt install intel-gpu-tools or sudo dnf install intel-gpu-tools). Run sudo intel_gpu_top. During a VAAPI transcode, you should see activity bars increase for "Render/3D" and especially "Video/Media" (or similar labels depending on generation), indicating the integrated GPU is processing the video.

If intel_gpu_top shows activity and htop shows low CPU usage during a transcode, and there are no errors in the Jellyfin logs related to VAAPI or FFmpeg, you have successfully configured Intel QSV hardware acceleration via VAAPI in Docker. If testing HDR tone mapping, verify the colors look correct (not washed out) on your SDR display.

(Note: The process for NVIDIA NVENC involves installing nvidia-container-toolkit on the host, modifying docker-compose.yml to include runtime: nvidia and specific environment variables like NVIDIA_VISIBLE_DEVICES, selecting NVENC in Jellyfin, and verifying with nvidia-smi on the host.)

10. Jellyfin Plugins and Customization

Jellyfin's core functionality is extensive, but its capabilities can be further enhanced and tailored through a built-in plugin system. Plugins allow developers and the community to add features ranging from new metadata sources and subtitle downloaders to authentication methods and theme customizations, without modifying the core Jellyfin code.

Understanding the Plugin System

  • Repositories: Jellyfin maintains an official plugin repository containing tested and approved plugins. You can also add third-party repositories, but exercise caution as plugins from untrusted sources could introduce instability or security risks.
  • Installation: Plugins are typically installed directly from the Jellyfin Dashboard.
  • Configuration: Most plugins have their own configuration pages accessible after installation via the Dashboard.
  • Updates: Plugins can usually be updated via the Dashboard when new versions become available in the repository.
  • Restart Required: Installing, uninstalling, or sometimes configuring plugins often requires restarting the Jellyfin server for changes to take effect.

Finding and Installing Plugins

  1. Log in to Jellyfin as an administrator.
  2. Navigate to Dashboard -> Plugins.
  3. Catalog Tab: This tab lists plugins available in the configured repositories (by default, the official Jellyfin repository). You can browse categories or search for specific plugins.
  4. Installation: Find the plugin you want, click on it for details, and click the "Install" button.
  5. Restart: After installing one or more plugins, a notification will usually appear at the top prompting you to restart the server. Click the restart button. (Alternatively, restart via Docker: docker compose restart jellyfin).

Popular and Useful Plugin Categories:

  • Metadata Fetchers:
    • AniDB: Fetches metadata specifically for Anime series/movies.
    • AudioDB: Provides artist biographies and artwork for music libraries.
    • Fanart.tv: Downloads additional high-quality artwork (logos, banners, backgrounds) for movies and TV shows.
    • OMDb (Open Movie Database): An alternative/supplementary source for movie metadata, especially ratings. Requires generating a free API key from OMDb's website.
  • Subtitle Downloaders:
    • Open Subtitles: Downloads subtitles automatically from OpenSubtitles.org (requires a free account). Configurable languages and download triggers (e.g., during library scan or on-demand).
    • Addic7ed: Another popular source, often good for TV show subtitles.
  • Authentication:
    • LDAP: Allows users to authenticate against an existing LDAP or Active Directory server, centralizing user management.
    • SAML / OAuth: Enables single sign-on (SSO) integration with identity providers like Authelia, Keycloak, Google, etc.
  • Live TV / IPTV:
    • Plugins to integrate with specific IPTV providers or M3U/XMLTV sources, enhancing the built-in Live TV capabilities.
  • Synchronization & Tracking:
    • Trakt.tv: Scrobbles watch progress and syncs ratings/watched status between Jellyfin and your Trakt.tv account. Great for keeping track across multiple platforms or sharing recommendations.
  • Theme & Customization:
    • Plugins that allow for applying custom CSS themes or modifying the UI appearance.
  • Notifications:
    • Plugins to send notifications (e.g., about newly added media) to services like Discord, Gotify, Pushover.
  • Utilities:
    • Reports: Generates detailed reports about your media library (duplicates, missing episodes, file formats, etc.).
    • Playback Reporting: Logs detailed playback information, potentially useful for troubleshooting or analytics.

Plugin Configuration

  1. After installing a plugin and restarting Jellyfin, go back to Dashboard -> Plugins.
  2. My Plugins Tab: This tab lists your currently installed plugins.
  3. Click on an installed plugin's name or icon. If the plugin requires configuration, you'll be taken to its settings page.
  4. Configure the options as needed (e.g., enter API keys, usernames/passwords, select preferences).
  5. Save the plugin's configuration. Another restart might be required depending on the plugin.

Risks and Performance Considerations

  • Stability: Poorly coded or incompatible plugins can potentially cause Jellyfin to become unstable or crash. Stick to official plugins or well-regarded third-party ones.
  • Security: Plugins execute code on your server. Only install plugins from sources you trust. A malicious plugin could compromise your server or data.
  • Performance: Some plugins, especially those that perform intensive tasks during library scans or playback, might increase CPU/RAM usage or slow down certain operations. Monitor server performance after installing new plugins.
  • Updates: Keep plugins updated alongside Jellyfin itself to ensure compatibility and receive bug fixes/security patches.

Custom CSS (Advanced Customization)

Beyond plugins, you can apply custom CSS rules to tweak the appearance of the Jellyfin web interface.

  1. Go to Dashboard -> General.
  2. Scroll down to the Custom CSS section.
  3. Enter your CSS code directly into the text box.
  4. Save the changes. The UI should update immediately (or may require a refresh).
    • Example: body { background-color: #222 !important; } would change the main background color.
    • You'll need to use your browser's developer tools (usually F12) to inspect elements and find the CSS selectors you want to modify. This requires CSS knowledge.

Plugins offer a powerful way to extend Jellyfin, but always install them thoughtfully, considering potential impacts on stability, security, and performance.

Workshop Installing and Configuring the Open Subtitles Plugin

  • Objective: Install the Open Subtitles plugin, configure it with user credentials, enable it for a library, and manually trigger a subtitle download task.
  • Prerequisites:
    • Jellyfin server running.
    • Admin access to the Jellyfin Dashboard.
    • An account registered at https://www.opensubtitles.org/ (free). Note your username and password.
    • At least one movie or TV show library configured in Jellyfin.
    • A media item within that library that currently lacks subtitles.

Steps:

  1. Log in as Admin: Access your Jellyfin instance and log in with your administrator account.

  2. Navigate to Plugins: Go to Dashboard -> Plugins.

  3. Find Open Subtitles Plugin: Click on the Catalog tab. Scroll down or use the search bar to find the "Open Subtitles" plugin.

  4. Install Plugin: Click on the "Open Subtitles" plugin entry, then click the "Install" button. Confirm if prompted.

  5. Restart Jellyfin: A message "Plugin installed. Please restart Jellyfin to apply the changes." should appear at the top. Click the "Restart" button in that message. Wait for Jellyfin to restart (it might take 30-60 seconds). You may need to refresh your browser page after a short wait.

  6. Configure Plugin:

    • Log back into Jellyfin if necessary.
    • Go back to Dashboard -> Plugins.
    • Click on the My Plugins tab. You should now see "Open Subtitles" listed.
    • Click on the "Open Subtitles" plugin name/icon to access its configuration page.
    • Enter your OpenSubtitles Username and OpenSubtitles Password in the respective fields.
    • (Optional) Configure other settings: Review options like "Skip if embedded subtitles exist" or "Download hearing impaired subtitles" based on your preferences.
    • Click "Save" at the bottom.
  7. Enable Plugin for a Library:

    • Go to Dashboard -> Libraries.
    • Find the library containing the media for which you want subtitles (e.g., "Movies").
    • Click the three-dot menu (...) next to the library name and select "Manage Library".
    • Scroll down the library settings page until you find the "Subtitle downloaders" section.
    • Ensure the checkbox next to "Open Subtitles" is checked.
    • Configure Preferred Download Languages: In the text box below "Subtitle download languages", enter the language codes for the subtitles you want (e.g., eng for English, spa for Spanish, fre for French). You can add multiple, separated by commas; the order determines priority.
    • Scroll to the bottom and click "OK" to save the library settings. Repeat for any other libraries where you want subtitles downloaded.
  8. Manually Trigger Subtitle Download Task:

    • Go to Dashboard -> Scheduled Tasks.
    • Find the task named "Download Subtitles".
    • Click the play icon (▶) to the right of the task name to run it immediately.
    • You can monitor its progress in the list (it will show "Running"). It might take some time depending on your library size and Open Subtitles server load.
  9. Verify Subtitle Availability:

    • Once the "Download Subtitles" task shows as "Idle" again, navigate to the media item (movie or episode) in your library that previously lacked subtitles.
    • Start playing the item.
    • Click the settings cogwheel icon within the player controls.
    • Click on Subtitles.
    • You should now see one or more subtitle tracks listed (e.g., "English (External - SRT)"). Select it to enable subtitles.
    • Alternatively, check the file system: navigate to the media item's folder on your host system. You should see a new .srt (or similar) file alongside the video file, named similarly (e.g., Movie Title (Year).eng.srt).

By completing this workshop, you've successfully installed, configured, and used the Open Subtitles plugin to automatically fetch subtitles for your media, enhancing the viewing experience. Remember that subtitle availability depends on contributions to OpenSubtitles.org.

11. Backup, Restore, and Maintenance

A self-hosted Jellyfin server, like any important system, requires regular maintenance and a solid backup strategy to protect against data loss due to hardware failure, software issues, accidental deletion, or other unforeseen events. Losing your carefully curated libraries, user watch history, and server configuration can be incredibly frustrating.

Importance of Backups

Your Jellyfin setup contains several critical pieces of data:

  • Configuration: Server settings, user accounts, passwords, library definitions, device access history, plugin settings.
  • Database: Contains detailed information about your media, metadata links, collections, and crucially, user watch progress and watched statuses.
  • Plugins: Installed plugin files.
  • (Optionally) Local Metadata/Artwork: If you enabled NFO/artwork saving, this data resides alongside your media files.

Losing the configuration and database means starting from scratch: re-adding libraries, re-scanning everything, recreating users, and losing all watch history. Backups allow you to restore your server to a known good state quickly.

What to Back Up

Assuming you are using the Docker Compose setup described earlier:

  1. Jellyfin Configuration Volume: The most critical part. This is the directory on your host machine that you mapped to /config inside the container (e.g., ~/jellyfin-docker/jellyfin-config). This directory contains the SQLite databases (library.db, etc.), configuration XML files, user data, logs, and the plugins subdirectory.
  2. Jellyfin Cache Volume: The directory mapped to /cache (e.g., ~/jellyfin-docker/jellyfin-cache). This contains temporary transcoding data and downloaded artwork/metadata caches. Backing this up is less critical than /config (Jellyfin can regenerate most of it), but backing it up can speed up recovery and reduce initial load after a restore. However, it can also become very large. You might choose to exclude large subdirectories like transcodes.
  3. docker-compose.yml File: The file that defines your Jellyfin container setup (~/jellyfin-docker/docker-compose.yml). Essential for recreating the container with the correct settings (volumes, ports, device mappings).
  4. (Optional) Media Files: Your actual movie, TV show, and music files. These are often stored separately and might have their own backup strategy (e.g., using RAID, NAS snapshots, separate backups). While not strictly part of the Jellyfin application backup, losing your media is obviously catastrophic, so ensure it's protected!
  5. (Optional) Local Metadata: If you enabled NFO/artwork saving, these .nfo, .jpg, .png files are within your media folders. Backing them up (as part of your media backup) ensures you don't have to re-scrape everything if you restore the Jellyfin config but lose the cache.

Backup Strategies

Choose a method that suits your technical comfort level and frequency needs:

  1. Manual Copy/Archive:
    • Method: Periodically stop the Jellyfin container (docker compose down) and manually copy the essential host directories (jellyfin-config, docker-compose.yml, optionally jellyfin-cache) to a safe location (external hard drive, NAS, cloud storage). You can use cp -a or create a compressed archive (tar czvf jellyfin_backup_YYYYMMDD.tar.gz ./jellyfin-config ./docker-compose.yml).
    • Pros: Simple to understand.
    • Cons: Requires manual intervention, easy to forget, requires downtime during backup.
  2. Scripted Backups (e.g., rsync, tar + cron):
    • Method: Create a shell script that stops Jellyfin, uses rsync or tar to copy/archive the necessary data to a backup location, and then restarts Jellyfin. Schedule this script to run automatically using cron (e.g., nightly or weekly).
    • Pros: Automated, consistent. rsync is efficient for subsequent backups (only copies changes).
    • Cons: Requires scripting knowledge, still involves brief downtime during backup unless using filesystem snapshots (more advanced).
  3. Filesystem Snapshots (ZFS/Btrfs):
    • Method: If your host system uses a filesystem like ZFS or Btrfs for the Docker volumes, you can take near-instantaneous, atomic snapshots of the filesystem state without stopping the container. These snapshots can then be backed up (e.g., using zfs send/recv or btrfs send/receive) to another location.
    • Pros: Minimal to zero downtime, point-in-time consistency, very efficient storage.
    • Cons: Requires using specific advanced filesystems on the host. Steeper learning curve.
  4. Dedicated Backup Tools: Tools like Duplicati, BorgBackup, Restic, or commercial cloud backup agents can be configured to back up the required host directories, often offering features like deduplication, encryption, and cloud integration. They can be run on a schedule via cron.

Important Considerations:

  • Backup Frequency: How often can you afford to lose data? Daily or weekly backups are common for configuration.
  • Backup Location: Follow the 3-2-1 rule: 3 copies of your data, on 2 different media types, with 1 copy offsite. Don't just back up to another folder on the same drive! Use external drives, a NAS, or cloud storage.
  • Testing: Periodically test your restore process to ensure your backups are valid and you know how to use them.

Restore Process (Manual Example using tar archive):

  1. Stop Jellyfin: docker compose down
  2. Locate Backup: Find your backup archive (e.g., jellyfin_backup_YYYYMMDD.tar.gz).
  3. Clear Current (Potentially Corrupt) Data: rm -rf ./jellyfin-config/* (Use extreme caution with rm -rf!). Optionally clear ./jellyfin-cache/*. Ensure docker-compose.yml is present (restore it from backup if missing).
  4. Extract Backup: tar xzvf /path/to/your/jellyfin_backup_YYYYMMDD.tar.gz -C /path/to/your/jellyfin-docker/ (Ensure the -C path is correct so it extracts jellyfin-config etc. into your project directory).
  5. Check Permissions: Verify ownership/permissions on the restored jellyfin-config directory match what Jellyfin expects (based on the user: UID:GID in your compose file). Use ls -l and chown -R UID:GID ./jellyfin-config if necessary.
  6. Restart Jellyfin: docker compose up -d
  7. Verify: Access Jellyfin and check if users, libraries, settings, and watch history are restored.

Maintenance Tasks

Regular maintenance keeps your Jellyfin server running smoothly and securely:

  • Updating Jellyfin (Docker): This is usually straightforward:
    cd ~/jellyfin-docker # Navigate to your project directory
    docker compose pull jellyfin # Pull the latest official image
    docker compose up -d # Stop the old container, start a new one with the new image and existing volumes
    docker image prune -f # Optional: Clean up old, unused images
    
    Check the Jellyfin release notes before major updates for any breaking changes.
  • Updating Host OS and Docker: Keep your underlying operating system and Docker Engine/Compose up-to-date with security patches:
    • Debian/Ubuntu: sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y
    • Fedora: sudo dnf upgrade -y && sudo dnf autoremove -y
    • Reboot the host if necessary (especially after kernel updates).
  • Cleaning Caches: Jellyfin generally manages its internal cache (/cache/) and transcoding temporary files. However, you can periodically check the size of the host directory mapped to /cache. The Dashboard -> Scheduled Tasks also has tasks like "Clean Cache Directory".
  • Database Optimization: Jellyfin includes a scheduled task "Optimize Database" which performs maintenance on the SQLite database files. Ensure this task is enabled and runs periodically (e.g., weekly).
  • Review Logs: Occasionally check the Jellyfin logs (Dashboard -> Logs or docker compose logs jellyfin) for recurring errors or warnings that might indicate underlying issues.
  • Monitor Disk Space: Ensure the drives storing your OS, Docker volumes (/config, /cache), and media files have sufficient free space.

Consistent backups and routine maintenance are vital for the long-term health and reliability of your self-hosted Jellyfin service.

Workshop Performing a Manual Backup and Simulating a Restore

  • Objective: Manually back up the essential Jellyfin Docker configuration using tar and simulate a restore process to understand the steps involved.
  • Prerequisites:
    • Jellyfin running via Docker Compose in a project directory (e.g., ~/jellyfin-docker).
    • The host directory mapped to /config exists within the project directory (e.g., ./jellyfin-config).
    • The docker-compose.yml file exists in the project directory.
    • SSH access to the host server.

Part 1: Performing the Manual Backup

  1. Navigate to Project Directory:

    • Connect to your server via SSH.
    • Change to the directory containing your docker-compose.yml and mapped volumes:
      cd ~/jellyfin-docker
      pwd # Verify you are in the correct directory
      ls # You should see docker-compose.yml, jellyfin-config, jellyfin-cache etc.
      
  2. Stop the Jellyfin Container:

    • Ensure Jellyfin is stopped gracefully to prevent database corruption during backup:
      docker compose down
      
    • Verify it's stopped: docker compose ps (should show no running jellyfin service).
  3. Create Backup Archive:

    • Use the tar command to create a compressed archive (.tar.gz) containing the configuration directory and the compose file. We'll include the date in the filename.
      # Command explained:
      # tar: Tape Archive utility
      # c: Create an archive
      # z: Compress using gzip
      # v: Verbose (list files being archived)
      # f: Use archive file (filename follows)
      # jellyfin_backup_$(date +%Y%m%d).tar.gz: Output filename with current date
      # ./jellyfin-config: The main configuration directory to include
      # ./docker-compose.yml: The compose file to include
      # Optional: Add ./jellyfin-cache if you want to back it up too (can be large)
      
      tar czvf jellyfin_backup_$(date +%Y%m%d).tar.gz ./jellyfin-config ./docker-compose.yml
      
    • Wait for the command to complete. You should see a list of files being added.
  4. Verify Backup File:

    • List the directory contents: ls -lh. You should see the newly created jellyfin_backup_YYYYMMDD.tar.gz file with a reasonable size.
  5. Restart Jellyfin (Optional but recommended):

    • Start Jellyfin again to ensure it's still working after the brief stop:
      docker compose up -d
      
    • Quickly check if you can access the Jellyfin web UI.
  6. Move Backup to Safe Location:

    • This is crucial. Copy or move the jellyfin_backup_....tar.gz file off the Jellyfin server to a different physical location (external drive, NAS share, cloud storage using scp, rsync, or a cloud client).
      # Example using scp to copy to your local machine:
      # scp jellyfin_backup_....tar.gz your_local_username@your_local_ip:/path/on/local/machine
      # Or move to a mounted external drive:
      # mv jellyfin_backup_....tar.gz /mnt/my_backup_drive/jellyfin_backups/
      

Part 2: Simulating a Restore (Use Caution!)

  • Warning: This part involves deleting your active configuration. Only proceed if you have a verified backup (from Part 1) safely stored elsewhere, or if you are working on a non-critical test instance.

  • Ensure Backup Exists: Double-check that you have the jellyfin_backup_....tar.gz file available (either still in the project directory for this test, or know its location if moved).

  • Stop Jellyfin:

    docker compose down
    

  • Simulate Data Loss (Delete Current Config):

    • Navigate to the project directory (cd ~/jellyfin-docker if not already there).
    • DANGER ZONE: Carefully delete the contents of the current configuration directory.
      echo "WARNING: About to delete contents of ./jellyfin-config. Press Enter to proceed or Ctrl+C to abort."
      read -p ""
      rm -rf ./jellyfin-config/*
      # Optional: rm -rf ./jellyfin-cache/*
      # Also delete the compose file IF it was corrupted/lost and needs restoring from backup
      # rm ./docker-compose.yml
      
    • Verify the directory is empty: ls -l ./jellyfin-config (should show total 0).
  • Restore from Backup:

    • Use tar to extract the contents of your backup archive back into the project directory. Make sure you are in the ~/jellyfin-docker directory when running this.
      # Command explained:
      # tar: Tape Archive utility
      # x: Extract files from an archive
      # z: Decompress using gzip
      # v: Verbose (list files being extracted)
      # f: Use archive file (filename follows)
      # /path/to/your/jellyfin_backup_....tar.gz: Path to the backup file
      # -C .: Extract into the current directory ('.') - Tar preserves paths, so
      #       ./jellyfin-config inside the archive gets extracted as ./jellyfin-config here.
      
      tar xzvf /path/to/your/jellyfin_backup_....tar.gz -C .
      # (Replace /path/to/your/ with the actual path, or just the filename if it's in the current dir)
      
  • Verify Restored Files:

    • Check the contents of the configuration directory again: ls -l ./jellyfin-config. You should see the database files (library.db, etc.), config files, plugins directory, etc. restored.
    • Check that docker-compose.yml is present: ls -l docker-compose.yml.
  • Check File Permissions (Important):

    • Sometimes, restoring files (especially if done as root) might alter permissions. Check the ownership of the restored config directory:
      ls -ld ./jellyfin-config
      
    • Compare the UID/GID shown with the user: UID:GID specified in your docker-compose.yml. If they don't match, correct them:
      # Example: If compose file uses user 1000:1000
      sudo chown -R 1000:1000 ./jellyfin-config
      
  • Restart Jellyfin:

    • Start the container using the restored configuration and compose file:
      docker compose up -d
      
  • Verify Restoration Success:

    • Access the Jellyfin web UI.
    • Log in with your previous user account(s).
    • Check if your libraries are present.
    • Check if watched status and playback progress for some items are restored.
    • Check if user settings and plugin configurations (if any) are back.

If everything looks as it did before the simulated data loss, your backup and restore process was successful. This exercise demonstrates the importance of having reliable backups and knowing the procedure to recover your Jellyfin server.

Conclusion

Congratulations on completing this deep dive into self-hosting Jellyfin! We've journeyed from the foundational concepts of media servers and Jellyfin's open-source philosophy to the practical steps of installation using the robust Docker method. We explored essential configurations, library management, and the critical aspects of user management and parental controls.

Moving into the intermediate territory, we demystified the often-complex topic of transcoding, highlighting the differences between software and hardware acceleration, and laid the groundwork for secure remote access by discussing networking principles and the various methods available, emphasizing the advantages of using a reverse proxy.

Finally, in the advanced section, we tackled sophisticated configurations head-on. You learned how to implement secure, encrypted remote access using Nginx Proxy Manager and Let's Encrypt HTTPS certificates. We delved into the specifics of enabling hardware-accelerated transcoding within Docker for significantly improved performance, focusing on Intel QSV via VAAPI as a practical example. We explored the versatility of Jellyfin's plugin system, enhancing functionality with tools like subtitle downloaders, and concluded with the vital practices of creating reliable backups and performing routine maintenance to ensure the long-term health and stability of your server.

By following the detailed explanations and engaging with the hands-on workshops, you should now possess not only a functional Jellyfin media server but also a solid understanding of the technologies and best practices involved in self-hosting such a service. The power of Jellyfin lies in its freedom, flexibility, and the control it gives you over your own media.

This guide provides a strong foundation, but the world of self-hosting is vast and ever-evolving. Don't hesitate to explore further: consult the official Jellyfin documentation, engage with the vibrant community forums and subreddits, experiment with different plugins, and perhaps even contribute back to the open-source project. Continue learning, stay curious, and enjoy the unparalleled experience of your personally curated, self-hosted media library!