How to Set Up a Home Media Server (Jellyfin / Plex) on Fedora

Install Jellyfin or Plex on Fedora using their native RPM packages or Podman containers, then configure firewall rules so other devices on your network can reach the web interface.

You just finished organizing your movie collection

You just finished organizing your movie collection on an external drive. You want to stream it to your living room TV and your phone. You install the media server package, point it at the drive, and the web interface shows an empty library. Or worse, the service refuses to start entirely. The terminal prints a cryptic permission denied error, and the logs point to SELinux. This happens because Fedora ships with strict security defaults that protect the system from untrusted processes. You need to tell the system exactly where your media lives and what the server is allowed to touch.

What's actually happening

Media servers are just long-running background processes. They read files from disk, transcode video streams on the fly, and serve HTML over HTTP. Fedora treats every new service as untrusted until you explicitly grant it access. The firewall blocks incoming connections by default. SELinux prevents the service from reading arbitrary directories. Systemd manages the lifecycle but will not start a service that fails its security checks. You are not fighting the OS. You are teaching it your layout.

When you install a media server, it drops a systemd unit file into /usr/lib/systemd/system/. That directory ships with the package and gets overwritten on updates. Never edit files there. If you need to change service behavior, create a drop-in override in /etc/systemd/system/<service>.service.d/. The override merges with the base unit and survives package upgrades.

Install the server package

Jellyfin and Plex both provide RPM packages. The installation path differs slightly because of how each project distributes updates. Jellyfin maintains an official Fedora repository. Plex distributes a standalone RPM that you install directly.

Here is how to install Jellyfin from the official repository. The command pulls the server and web interface packages in one transaction.

# Replace 41 with your current release number. Use rpm -E %fedora to check.
sudo dnf install https://repo.jellyfin.org/fedora/server/41/latest/x86_64/jellyfin-server-*.rpm \
                 https://repo.jellyfin.org/fedora/server/41/latest/x86_64/jellyfin-web-*.rpm
# --best fixes dependency resolution when multiple package versions exist
# dnf automatically enables the repo metadata for future upgrades
sudo systemctl enable --now jellyfin
# enable creates the symlink so it starts on boot. --now starts it immediately.

Here is how to install Plex from the official download page. You download the RPM first, then install it locally.

# Download the latest stable RPM from plex.tv/media-server-downloads
# Install the local file. dnf resolves dependencies from configured repos.
sudo dnf install ./plexmediaserver-*.rpm
# Start the service and register it for boot
sudo systemctl enable --now plexmediaserver

Run dnf upgrade --refresh weekly to keep the server binaries current. Use dnf system-upgrade only when crossing major Fedora releases. They are different commands with different purposes.

Open the firewall

Neither server is reachable from other devices until you allow the relevant port. Fedora's firewall uses zones and runtime/persistent configurations. Changes to the persistent configuration do not apply until you reload the runtime.

Here is how to open the correct port for your chosen server. Run the permanent rule first, then reload the firewall to apply it immediately.

# Jellyfin listens on port 8096 by default
sudo firewall-cmd --permanent --add-port=8096/tcp
# Plex listens on port 32400 by default
# sudo firewall-cmd --permanent --add-port=32400/tcp
# Reload applies the permanent rules to the active firewall
sudo firewall-cmd --reload

Always run firewall-cmd --reload after every rule change. Otherwise the runtime config and the persistent config diverge, and you will spend an hour wondering why your rules vanished after a reboot.

Fix SELinux permissions for custom media paths

If your media library lives outside the default paths, SELinux will block the server from reading it. SELinux uses file contexts to decide what a process can do. A directory labeled user_home_t is not readable by a service running as jellyfin or plex. You must relabel the directory to a type that allows read-only access for network services.

Here is how to set the correct context for a custom media directory. The command updates the SELinux policy database and applies the label to existing files.

# Add a context rule for the media directory and all subdirectories
sudo semanage fcontext -a -t samba_share_t "/mnt/media(/.*)?"
# Apply the new label to the filesystem immediately
sudo restorecon -Rv /mnt/media
# -R processes recursively. -v prints every relabeled file.

The samba_share_t type is the standard label for shared media directories. It allows read-only access for services that need to serve files over the network. If you prefer a broader approach for testing, you can toggle a boolean that allows services to read user home directories.

# Allow network services to read files in /home
sudo setsebool -P samba_export_all_ro 1
# -P makes the boolean persistent across reboots

SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. Disabling it removes a critical security layer and usually masks the real configuration problem.

Verify it worked

The service is running, the firewall is open, and the labels are correct. You need to confirm the web interface loads and the library scans successfully.

Here is how to check the service state and recent logs in one command. This is the standard first step for any systemd unit.

# Check status and tail recent journal entries for the service
sudo journalctl -xeu jellyfin
# Replace jellyfin with plexmediaserver if using Plex
# -x adds explanatory hints. -e jumps to the end of the log.

Open a browser on the same machine and navigate to the local address. Jellyfin uses http://localhost:8096. Plex uses http://localhost:32400/web. Complete the initial setup wizard to point the server at your media directories. From other devices on your local network, replace localhost with the Fedora host's IP address.

Run journalctl -xe first. Read the actual error before guessing. Half the time the symptom is a missing codec or a misnamed directory, not a broken service.

Common pitfalls and what the error looks like

The most common failure mode is a permission denied error during library scanning. The web interface shows an empty folder or a red warning banner. The journal prints a specific SELinux denial.

# ...output truncated for clarity
Nov 12 14:22:01 fedora-server jellyfin[1234]: Permission denied: '/mnt/media/Movies/Inception.mkv'
Nov 12 14:22:01 fedora-server jellyfin[1234]: Failed to scan library. Check directory permissions and SELinux context.

The error is intentional. The service is running in a confined context and cannot read user_home_t or default_t directories. Run restorecon -Rv /mnt/media again. Verify the label with ls -Z /mnt/media. The output should show samba_share_t on every file.

Another common issue is hardware transcoding failing silently. The server falls back to CPU encoding, which spikes CPU usage and drops frames. Fedora ships with VA-API drivers, but the service needs the correct environment variables to find the GPU.

# Create a drop-in override to pass GPU device paths to the service
sudo mkdir -p /etc/systemd/system/jellyfin.service.d
# Write the override file with device access and environment variables
sudo tee /etc/systemd/system/jellyfin.service.d/gpu.conf <<'EOF'
[Service]
DevicePolicy=closed
DeviceAllow=/dev/dri rw
Environment=LIBVA_DRIVER_NAME=iHD
Environment=VDPAU_DRIVER=nvidia
EOF
# Reload systemd and restart the service
sudo systemctl daemon-reload
sudo systemctl restart jellyfin

The DeviceAllow directive grants read-write access to the DRM render nodes. The environment variables tell the media pipeline which driver to load. Check vainfo on the host to confirm the driver name matches your hardware.

When to use this vs alternatives

Use native RPM installation when you want the service to integrate directly with systemd, use host hardware acceleration without extra configuration, and receive updates through your standard dnf upgrade --refresh workflow. Use rootless Podman containers when you want strict process isolation, plan to run multiple media services side by side, or need to test a new version without touching the host filesystem. Use a dedicated VM when you are running a Windows-only media server or need complete hardware passthrough for capture cards. Stay on the upstream Workstation if you only deviate from the defaults occasionally.

Where to go next