• Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below
  • Forum
  • Website
  • GitHub
  • Status
  • Translation
  • Features
  • Team
  • Rules
  • Help
  • Feeds
User Links
  • Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below

    Useful Links Forum Website GitHub Status Translation Features Team Rules Help Feeds
    Jellyfin Forum Support Guides, Walkthroughs & Tutorials Containerized Jellyfin media server - openSUSE MicroOS

     
    • 0 Vote(s) - 0 Average

    Containerized Jellyfin media server - openSUSE MicroOS

    Including Caddy, DuckDNS, SELinux and systemd
    User 7547
    Offline

    Unregistered
    Posts: 5
    Threads: 2
    Joined: 2024 Mar
    Country:Hungary
    #1
    2024-07-23, 02:04 PM (This post was last modified: 2024-07-24, 05:33 AM by User 7547. Edited 4 times in total.)
    Hello there,

    I hope my guide will help a few of you to configure your own Jellyfin media server or give you some ideas to improve. 
    Please keep in mind, I'm just an end-user, this took me a month to figure out and put it together. I'm open to suggestions. Enjoy.


    OS Installation

    I prefer openSUSE MicroOS, so if you use something else, you can skip this part.

    Enable firewall at the installation settings (optional).
    If you install MicroOS Container Host, as I do, add ‘policycoreutils-python-utils’ during the installation process, which is required for ‘semanage’. If you install, for example the Gnome (RC) version, this package is included by default.


    SSH & firewall rules

    If you'd like to ssh directly to the root user, allow it. You have to create the following file and it’ll be used from now on if you restart the “sshd” service.
    vi /etc/ssh/sshd_config.d/auth.conf
    #Put this into the file
    PermitRootLogin yes
    systemctl restart sshd

    If not, because of security, you can create a user which you'll use for this.
    useradd <user>
    passwd <user>


    First, enable the SSH port. If you didn’t enable the firewall, skip this step.
    firewall-cmd --permanent --add-service=ssh

    These ports should be enabled too, because the containers we install need these ports to function properly. If you didn’t enable the firewall, skip this step.
    firewall-cmd --zone=public --add-port=8096/tcp --permanent
    firewall-cmd --zone=public --add-port=443/tcp --permanent
    firewall-cmd --reload

    Keep in mind, port 443 is only required, if you are planning to use Caddy for the automatic TLS for HTTPS. In this case, you have to port forward this port on your router too. This is only necessary, if you plan to reach Jellyfin outside of your local network.


    Change the start of the unprivileged port number (You can skip this step if you just want to use Jellyfin locally)

    Setting net.ipv4.ip_unprivileged_port_start=443 allows non-root processes to bind to ports starting from 443. Caddy sets up the necessary configuration to handle these challenges on port 443. If port 443 is not open or accessible from the internet, the Let's Encrypt validation will fail, and Caddy won't be able to obtain or renew TLS certificates automatically.
    vi /etc/sysctl.conf
    #Put this into the file
    net.ipv4.ip_unprivileged_port_start=443


    SELinux booleans (If you don't use SELinux, skip this)

    You must turn on the container_manage_cgroup boolean to run containers with systemd.
    setsebool -P container_manage_cgroup on

    Enabling this boolean may grant the necessary SELinux permissions for containers to access and use DRI devices on the system. It is necessary for Hardware Acceleration.
    setsebool -P container_use_dri_devices 1


    User creation/configuration

    Lets create and set up the user for the containers.
    Creating a dedicated user for the containers, without default home directory. If you are fine with the default, '-M' is not needed.
    useradd -M <user>

    Add the user to the 'render' and ‘video’ group.
    usermod -a -G render,video <user>

    Create a directory to the user. If you created the user to have a default home directory, this step is not needed.
    mkdir -p /var/my_data/home/<user>

    Change the owner and group of the directory to the user. If you created the user to have a default home directory, this step is not needed.
    chown <user>:<user> /var/my_data/home/<user>

    Only the user has full access. This is up to you.
    chmod 700 /var/my_data/home/<user>

    Make the directory to the user's home directory. If you created the user to have a default home directory, this step is not needed.
    usermod -d /var/my_data/home/<user> <user>

    The enable-linger option ensures that the user's session persists across reboots and allows background tasks to continue running.
    loginctl enable-linger $(id -u <user>)


    Directories for the containers

    Create the necessary directories for our containers. Do it with the root user.
    sudo -iu <user> mkdir -p /var/my_data/home/<user>/jellyfin/{config,cache,media}
    sudo -iu <user> mkdir -p /var/my_data/home/<user>/jellyfin/media/{subdir1,subdir2,subdir3}

    You can create subdirectories if you want, like ‘/media/family_videos’, /media/<etc>.

    Caddy and DuckDNS are only required, if you plan to reach Jellyfin outside of your network in a secure way. If you only plan to use it locally, you can skip the Caddy and DuckDNS configurations.
    sudo -iu <user> mkdir -p /var/my_data/home/<user>/duckdns
    sudo -iu <user> mkdir -p /var/my_data/home/<user>/caddy/caddy_data


    DuckDNS configuration (If you only plan to use Jellyfin locally, skip this step)

    Configure DuckDNS to automatically update your dynamic IP. For more details https://www.duckdns.org/install.jsp
    cd /var/my_data/home/<user>/duckdns
    vi duckdns.sh
    #Put this into the file
    echo url="https://www.duckdns.org/update?domains=<your_domain>&token=<your_token_from_duckdns>&ip=" | curl -k -o ~/duckdns/duck.log -K -

    Make it executable. Try it out ./duckdns.sh, cat duck.log, if it is OK, then it is working fine.
    chmod +x duckdns.sh


    Caddyfile configuration (If you only plan to use Jellyfin locally, skip this step)

    Here, I prefer to switch to our user. I use DuckDNS for my setup, so modify it according to your needs. The IP, is the internal IP assigned to my server.
    su - <user>
    cd /var/my_data/home/<user>/caddy
    vi Caddyfile
    #Put this into the file
    <your_domain>.duckdns.org {
    (tab)reverse_proxy <your_internal_IP>:8096
    (tab)tls {
    (tab)dns duckdns <your_token_from_duckdns>
    (tab)}
    }


    DuckDNS systemd service/timer (If you only plan to use Jellyfin locally, skip this step)

    Switch back to root user. Make the duckdns.sh to run every 5 minutes. First we need a systemd timer.
    vi /etc/systemd/system/duckdns.timer
    #Put his into the file
    [Unit]
    Description=Run DuckDNS script every 5 minutes

    [Timer]
    OnCalendar=*:0/5
    Persistent=true

    [Install]
    WantedBy=timers.target

    Then we need a systemd service. It will be run by the user, but is fine via root too. It is not needed to be user specific.
    vi /etc/systemd/system/duckdns.service
    #Put this into the file
    [Unit]
    Description=Update DuckDNS dynamic DNS

    [Service]
    Type=oneshot
    User=<user>
    ExecStart=/bin/bash -c '/var/my_data//home/<user>/duckdns/duckdns.sh'

    [Install]
    WantedBy=multi-user.target

    systemctl enable duckdns.timer
    systemctl enable duckdns.service


    Fstab configuration


    Auto mount your external drive. If you do not have/need one, skip these steps. For more details https://www.youtube.com/watch?v=LkwZZIsY9uE
    To get the name of the device that you are looking for.
    fdisk -l

    To get the UUID for the device and also the format type.
    blkid

    Here, I give a permanent mount point for my external drive which, if you plan to use it like I do, will be the ‘/var/my_data/home/<user>/jellyfin/media’.
    vi /etc/fstab
    #Put this into the file
    UUID=<UUID>(tab)/path/to/dir(tab)ext4(tab)defaults(tab)0(tab)0

    This will change the ownership of your ‘path/to/dir’ so set it back.


    Jellyfin and Caddy containers


    Switch to your user.
    Create the Jellyfin container. https://jellyfin.org/docs/general/instal...er/#podman

    Some description:

    ’--label "io.containers.autoupdate=registry"’ Assigns a label to the container, indicating that it should be automatically updated from the registry.
    ’--publish <your_internal_IP>:8096:8096/tcp’ This publishes port 8096 on the host's IP address <your_internal_IP> to port 8096 in the container, allowing access to Jellyfin service.
    ’--network bridge’ Places the container on the default network bridge. You can use 'host' if you have performance issue with this, 'birdge' has higher CPU utilization but more secure.
    ’--user 0:0’ Gives the user root privilege inside the container.
    ’--group-add=$(getent group render | cut -d: -f3)’ This adds the group ID of the "render" group inside the container.
    ’--device /dev/dri/renderD128:/dev/dri/renderD128:rwm’ This makes the render device available inside the container with read, write, and mknod permissions.

    IMPORTANT! Due to SELinux, be careful how you use this in the future. In the podman config, '/config:Z' means unshared, so only that specific container will be able to see its content where it belongs to. For shared you have to use lowercase 'z'. For the media, I used '--mount', so it is obvious at the end that those directories under media will be shared with other programs too. Otherwise, pointing other programs toward that directory, they won't be able to see it's content.

    podman create --replace \
      --label "io.containers.autoupdate=registry" \
      --name jellyfin \
      --publish <your_internal_IP>:8096:8096/tcp \
      --network bridge \
      --user 0:0 \
      --group-add=$(getent group render | cut -d: -f3) \
      --device /dev/dri/renderD128:/dev/dri/renderD128:rwm \
      --volume /var/my_data/home/<user>/jellyfin/cache:/cache:Z \
      --volume /var/my_data/home/<user>/jellyfin/config:/config:Z \
      --mount type=bind,source=/path/to/dir,destination=/media,ro=true,relabel=shared \
      docker.io/jellyfin/jellyfin:latest


    If you only plan to use Jellyfin locally, skip this step. Create the DuckDNS specific Caddy container. If you are not using DuckDNS, better to take a look at https://github.com/Sjord/caddy-modules, and pull what you need.

    podman create --replace \
      --label "io.containers.autoupdate=registry" \
      --name caddy \
      --publish <your_internal_IP>:443:443/tcp \
      --network bridge \
      --user 0:0 \
      --volume /var/my_data/home/<user>/caddy/Caddyfile:/etc/caddy/Caddyfile:z \
      --volume /var/my_data/home/<user>/caddy/caddy_data:/data:Z \
      docker.io/serfriz/caddy-duckdns:latest


    Apply SELinux container policy


    Switch back to the root user (exit).
    The first command sets up the file context specification based on an existing one, and the second command ensures that the contexts are applied and set to their default values recursively within the specified directory.
    semanage fcontext -a -e /var/lib/containers /var/my_data/home/<user>/.local/share/containers
    restorecon -R -F /var/my_data/home/<user>/.local/share/containers


    Systemd services for the containers


    Systemd configuration, so the containers will start automatically at system boot, with the dedicated user.
    vi /etc/systemd/system/jellyfin.service
    #Put this into the file
    [Unit]
    Description=Jellyfin container

    [Service]
    Restart=always
    User=<user>
    ExecStartPre=/bin/sleep 30
    ExecStart=/usr/bin/podman start -a jellyfin
    ExecStop=/usr/bin/podman stop -t 2 jellyfin

    [Install]
    WantedBy=default.target


    vi /etc/systemd/system/caddy.service
    #Put this into the file
    [Unit]
    Description=Caddy container

    [Service]
    Restart=always
    User=<user>
    ExecStart=/usr/bin/podman start -a caddy
    ExecStop=/usr/bin/podman stop -t 2 caddy

    [Install]
    WantedBy=default.target

    systemctl daemon-reload
    systemctl enable jellyfin.service caddy.service

    If the containers are not running, start them.
    systemctl start jellyfin.service caddy.service


    That's it. Hope I could help.

    Mirronth

    Edit: Typo
    1
    TheDreadPirate
    Offline

    Community Moderator

    Posts: 15,374
    Threads: 10
    Joined: 2023 Jun
    Reputation: 460
    Country:United States
    #2
    2024-07-23, 02:34 PM (This post was last modified: 2024-07-23, 02:34 PM by TheDreadPirate. Edited 1 time in total.)
    Very detailed guide. Thank you for spending so much time on this!
    Jellyfin 10.10.7 (Docker)
    Ubuntu 24.04.2 LTS w/HWE
    Intel i3 12100
    Intel Arc A380
    OS drive - SK Hynix P41 1TB
    Storage
        4x WD Red Pro 6TB CMR in RAIDZ1
    [Image: GitHub%20Sponsors-grey?logo=github]
    Donovadrews
    Offline

    Junior Member

    Posts: 1
    Threads: 0
    Joined: 2024 Aug
    Reputation: 0
    Country:United States
    #3
    2024-08-08, 04:08 AM
    If you could elaborate, that would be fantastic, or maybe offer me a link to a detailed explanation of the approach.
    User 7547
    Offline

    Unregistered
    Posts: 5
    Threads: 2
    Joined: 2024 Mar
    Country:Hungary
    #4
    2024-08-14, 08:04 PM
    I wanted a reliable Jellyfin server for myself. MicroOS offers easy backup solution and the containerized Jellyfin can be easily recreated anytime.
    « Next Oldest | Next Newest »

    Users browsing this thread: 1 Guest(s)


    • View a Printable Version
    • Subscribe to this thread
    Forum Jump:

    Home · Team · Help · Contact
    © Designed by D&D - Powered by MyBB
    L


    Jellyfin

    The Free Software Media System

    Linear Mode
    Threaded Mode