• January 3, 2026

How to Deploy Jellyfin – Open-Source Media Server

How to Deploy Jellyfin – Open-Source Media Server

Set up a self-hosted Jellyfin media server with Docker Compose and Traefik TLS.

Jellyfin is a free and open-source media server that lets you organize, manage, and stream your personal collection of movies, TV shows, music, and photos to any device. Jellyfin is fully self-hosted with no subscription tiers or proprietary cloud services. It gives you full control over your media library and runs entirely on your own server environment.

In this article, you will deploy Jellyfin using Docker Compose, configure persistent storage for application data and media files, and set up Traefik as a reverse proxy to securely access your Jellyfin instance.

Prerequisites

Before you begin, you need to:

  • Have access to an Ubuntu 24.04-based server as a non-root user with sudo privileges.
  • Install Docker and Docker Compose.
  • Configure a domain A record pointing to your server’s IP address (for example, jellyfin.example.com).

Set Up the Directory Structure and Environment Variables

In this section, you prepare the required directory structure for Jellyfin and define environment variables in a .env file.

  1. Create the directory structure for Jellyfin.
    console
    $ mkdir -p ~/jellyfin-media-server/{cache,config,media}

    This command creates the following directories:

    • config: Stores Jellyfin’s configuration files, metadata, and database.
    • cache: Holds temporary transcoding and runtime cache data.
    • media: Contains your actual media library (movies, TV shows, music, photos).
  2. Navigate into the jellyfin-media-server directory.
    console
    $ cd ~/jellyfin-media-server
  3. Find the UID and GID of the user who owns your media files. Replace USERNAME with the username of your media file owner.
    console
    $ id USERNAME

    From the output, note the uid and gid of the user.

  4. Create a .env file.
    console
    $ nano .env

    Add the following variables:

    ini
    UID=YOUR_USER_UID
    GID=YOUR_USER_GID
    
    DOMAIN=jellyfin.example.com
    LETSENCRYPT_EMAIL=admin@example.com

    Replace:

    • YOUR_USER_UID/YOUR_USER_GID with your actual UID and GID.
    • jellyfin.example.com with your domain.
    • admin@example.com with your email.

    Save and close the file.

Deploy with Docker Compose

In this section, you create and deploy the Docker Compose stack that runs Jellyfin behind Traefik. Docker Compose manages both containers, applies the environment variables from your .env file, and automatically configures HTTPS routing through Traefik.

  1. Create a new Docker Compose manifest.
    console
    $ nano docker-compose.yaml
  2. Add the following content, replacing UID:GID with the user and group IDs that own your media files on the host, jellyfin.example.com with your domain name, and admin@example.com with your email address.
    yaml
    services:
      traefik:
        image: traefik:v3.6
        container_name: traefik
        command:
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--entrypoints.websecure.address=:443"
          - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
          - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
          - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
          - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
          - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
          - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - "letsencrypt:/letsencrypt"
          - "/var/run/docker.sock:/var/run/docker.sock:ro"
        restart: unless-stopped
    
      jellyfin:
        image: jellyfin/jellyfin:latest
        container_name: jellyfin
        user: "${UID}:${GID}"
        hostname: jellyfin
        expose:
          - "8096"
        volumes:
          - "./config:/config"
          - "./cache:/cache"
          - "./media:/media"
        environment:
          - JELLYFIN_PublishedServerUrl=https://${DOMAIN}
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.jellyfin.rule=Host(`${DOMAIN}`)"
          - "traefik.http.routers.jellyfin.entrypoints=websecure"
          - "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
        restart: unless-stopped
    
    volumes:
      letsencrypt:

    Save and close the file.

    In the above manifest:

    • services: Defines two containers that Docker Compose manages:
      • traefik: Acts as the reverse proxy, router, and TLS termination layer.
      • jellyfin: Hosts the Jellyfin media server application.
    • image: Specifies the container image for each service.
    • container_name: Assigns a fixed name to each container, making it easier to reference when checking logs or managing containers.
    • command (Traefik): Configures Traefik’s core behavior, including Docker discovery, entry points for HTTP/HTTPS traffic, automatic HTTP-to-HTTPS redirection, and Let’s Encrypt certificate generation.
    • ports (Traefik): Exposes ports 80 (HTTP) and 443 (HTTPS) on the host so Traefik can route external traffic to your services.
    • user (Jellyfin): Runs the container process under a specific non-root UID and GID that owns the media files on the host.
    • expose (Jellyfin): Makes the internal container port (8096) visible to Traefik without publishing the port directly to the host.
    • volumes:
      • Host bind mounts (./config./cache./media) persist application data for Jellyfin.
      • The letsencrypt named volume stores Traefik TLS certificates from Let’s Encrypt.
      • The Docker socket (/var/run/docker.sock) allows Traefik to automatically detect running services.
    • environment (Jellyfin): Defines application-specific environment variables, such as the public URL that Jellyfin reports to clients.
    • labels (Jellyfin): Registers the Jellyfin container with Traefik and configures how Traefik routes HTTPS traffic based on the domain name.
    • restart: unless-stopped: Ensures both services restart automatically unless you stop them manually.
  3. Create and start the services.
    console
    $ docker compose up -d
  4. Verify that the services are running.
    console
    $ docker compose ps

    Output:

    NAME       IMAGE                      COMMAND                  SERVICE    CREATED          STATUS                    PORTS
    jellyfin   jellyfin/jellyfin:latest   "/jellyfin/jellyfin"     jellyfin   33 minutes ago   Up 33 minutes (healthy)   8096/tcp
    traefik    traefik:v3.6               "/entrypoint.sh --pr…"   traefik    33 minutes ago   Up 33 minutes             0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp

    The above output confirms that both services are running successfully. Jellyfin is healthy, and Traefik is listening on ports 80 and 443 for incoming traffic.

  5. View the logs for the services.
    console
    $ docker compose logs

    For more information on managing a Docker Compose stack, see the How To Use Docker Compose article.

Configure Jellyfin Media Server

In this section, you use the Jellyfin web interface to configure your server, create an administrator account, and add your media libraries.

  1. Open the Jellyfin web interface in your browser.
    https://jellyfin.example.com
  2. On the Welcome screen, enter a Server Name and select your preferred Display Language, then click Next.
  3. Create an Administrator Account by providing a username and password, then click Next.
  4. Click Add Media Library to add your media folders.
  5. Choose the Content Type (Movies, TV Shows, Music, Photos, etc.).
  6. Click Folders, then + Add and navigate to the media folder you mounted in Docker (for example, /media/movies), select it, and click OK.
  7. (Optional) Adjust Metadata Settings and Advanced Settings if needed, then click OK and Next.
  8. Choose your Preferred Metadata Language and Country, then click Next.
  9. Review the Remote Access settings. The default settings are appropriate for most setups. Click Next, then Finish to complete the setup.
  10. After setup completes, Jellyfin will reload and display the login screen. Sign in using the administrator account you just created. The page redirects to the Jellyfin Dashboard, where you can manage libraries, users, streaming, and transcoding options.

Conclusion

You have successfully set up your own Jellyfin media server and made it accessible through a secure domain. You deployed the required services using Docker Compose, created persistent storage directories for configuration, cache, and media files, configured Traefik as the reverse proxy, and enabled HTTPS encryption with Let’s Encrypt.

Leave a Reply

Your email address will not be published. Required fields are marked *