Media Servers guide

Jellyfin Docker Compose Setup: Media, Permissions, and Hardware Acceleration

Deploy Jellyfin with Compose, correct config and media mounts, predictable permissions, optional GPU acceleration, and safe backups.

Published and reviewed by OpenAlt · September 22, 2026

Smart TV displaying streaming content in modern living room setting with exposed brick wall.
Photo by www.kaboompics.com on Pexels

Use Docker Compose to run Jellyfin with a predictable configuration, cache, and media layout. Expose the container’s web port, map each persistent directory deliberately, and confirm that the container user can read the media files. Hardware acceleration is optional; correct mounts and permissions come first. Start from Jellyfin on OpenAlt.

Table of Contents

Choose mounts before launch

Choose mounts based on what must survive container recreation. Jellyfin needs separate locations for configuration, cache, and media. The official container guide documents the jellyfin/jellyfin image and these mount categories. Read the official container documentation.

Use a layout with clear roles:

  • config stores the database, server settings, users, plugins, and library metadata.
  • cache stores temporary working data.
  • media contains the movies, shows, music, or other files Jellyfin scans.

Separate host paths from container paths. Jellyfin only sees paths inside the container. If /srv/media on the host is mounted as /media, the library wizard must use /media, not /srv/media.

Preflight

Confirm that:

  • Docker and Compose are available on the Linux or NAS host.
  • The host directories for config, cache, and media exist.
  • The container’s numeric UID/GID can access those directories.
  • TCP port 8096 is available for the web interface.
  • UDP port 7359 is available if you intend to use the documented discovery path.
  • The media mount points to the intended storage volume rather than an empty placeholder.
  • Your backup destination is separate from the live configuration directory.

Compose service explained

Use a minimal service with the official image, explicit ports, and explicit mounts. This example mounts media read-only, which is appropriate when Jellyfin only needs to scan and play files.

services:
  jellyfin:
    image: jellyfin/jellyfin
    user: "1000:1000"
    ports:
      - "8096:8096/tcp"
      - "7359:7359/udp"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /srv/media:/media:ro

Replace 1000:1000 with the UID and GID that should access the application data. Replace /srv/media with the actual host media path. Keep the container-side paths /config, /cache, and /media stable because Jellyfin will use them.

Start the documented stack with:

docker compose up

Watch for permission or mount errors, then open http://server-address:8096.

UID/GID permissions

Set the container UID/GID to an account that can read the media and write to the config and cache directories. Root ownership or an arbitrary identity can create failures that appear later as missing libraries or unwritable settings.

Check the current identity and numeric ownership:

id
ls -ln ./config ./cache /srv/media

Compare the displayed ownership and permissions with the UID/GID in Compose. Jellyfin generally needs write access to /config and /cache, while media can remain read-only unless your workflow deliberately requires file changes.

If the service starts but libraries are empty, inspect the media directory on the host and verify that the same content is visible at /media inside the container. Correct ownership, group membership, ACLs, or the mount path before changing library settings.

First-run wizard

Complete the wizard using container paths. Create the administrator account, then add libraries using directories beneath /media.

Typical locations include:

  • /media/movies
  • /media/tv
  • /media/music

Use only directories that exist under the mounted path. If the wizard uses /srv/media/movies, Jellyfin will look for that path inside the container and fail unless that exact container-side path exists.

Keep the first configuration narrow. Add one known-good library, confirm that it scans, and then add additional libraries. This separates path and permission problems from metadata or naming problems. After local playback works, continue with the Jellyfin remote-access guide if you need access beyond the local network.

Detailed view of a server rack with a focus on technology and data storage.
Photo by panumas nikhomkhai on Pexels
Detailed image of illuminated server racks showcasing modern technology infrastructure.
Photo by panumas nikhomkhai on Pexels

Media libraries

Point each library at the smallest correct container directory. A library aimed at /media may work, but specific paths make organization and troubleshooting clearer.

Before adding a library, confirm that:

  1. The host directory contains the expected files.
  2. Compose maps that directory into the container.
  3. The container-side directory exists and is readable.
  4. The library type matches the content.
  5. Jellyfin can write to /config and /cache.

Keep media read-only when Jellyfin only scans and plays it. Grant write access only for a deliberate workflow that needs Jellyfin to modify files. This keeps application state separate from media and limits the impact of an incorrect setting.

Hardware acceleration

Configure hardware acceleration only after ordinary playback and transcoding work without it. The official guide explains supported approaches and host requirements; follow it for device and driver details rather than guessing. Read Jellyfin’s hardware acceleration documentation.

The basic distinction is:

  • Direct Play may require little or no transcoding.
  • Transcoding can require additional CPU resources.
  • Hardware acceleration can move supported transcoding work to compatible hardware.
  • A GPU does not correct incorrect mounts, missing permissions, or an unusable media file.

Add hardware access only after the host is configured and you have a playback case that needs transcoding. Change one setting at a time, then verify the result. A GPU device mapping alone does not prove that acceleration is active.

Verify direct play and transcoding

Verify the path from Compose to playback rather than checking only whether the web interface opens.

  1. Run docker compose ps and confirm that Jellyfin is running.
  2. Review docker compose logs jellyfin for mount, permission, or startup errors.
  3. Open the server on TCP port 8096.
  4. Play a file from a known-good library.
  5. Inspect playback information in the client or server interface.
  6. Confirm whether the session is direct playing or transcoding.
  7. Test another client or file that represents normal use.

If direct playback works but transcoding fails, separate media access from transcoding configuration. If both fail, return to mount paths and UID/GID permissions before changing hardware settings. Use the official guide for acceleration-specific checks. Use the official acceleration guide for the final check.

Backup, update, and rollback

Back up Jellyfin’s configuration before updates and preserve the Compose file that defines the service. The official backup and restore documentation identifies the configuration data to preserve and the restoration procedure. Review the official backup and restore guide.

Use this operating routine:

  1. Stop or pause the service before making a consistent configuration backup.
  2. Copy the /config contents to separate storage.
  3. Preserve the Compose file and record the host-to-container mappings.
  4. Verify that the backup can be listed or opened.
  5. Update the image and restart the service.
  6. Confirm login, libraries, and playback.
  7. If the update fails, restore the known-good configuration and image reference.

Media files are not a substitute for a Jellyfin configuration backup. Protect the media separately, especially when it is stored on another disk, NAS share, or removable volume.

Common failures

Fix the underlying path or identity before repeatedly changing library settings.

  • The page does not open: confirm that the service is running and TCP 8096 is reachable.
  • The container starts, but no files appear: verify both paths. Jellyfin must use /media, not the host path.
  • Config errors appear in logs: confirm that the configured UID/GID can write to /config and /cache.
  • Libraries scan incompletely: inspect permissions and confirm that the mount points to the expected storage volume.
  • Discovery does not work: verify the UDP 7359 mapping and local network conditions.
  • Transcoding fails: verify playback without acceleration, then follow the official hardware guide.
  • An update behaves badly: restore the backed-up config and previous known-good image reference.

Next step

After local playback and the backup routine are verified, inspect Jellyfin on OpenAlt. You can also browse the media-server directory and use the remote-access guide when you are ready to expose the service beyond the local network.

Frequently Asked Questions

Which port does Jellyfin use?

Jellyfin uses TCP port 8096 for the web interface and UDP port 7359 for the documented discovery path. Map both when your network setup requires them.

What paths should I use in the library wizard?

Use container-side paths such as /media/movies, /media/tv, or /media/music. Do not enter the host path unless it is also mounted at that exact path inside the container.

Do media mounts need write access?

No. Read-only media access is sufficient for ordinary scanning and playback. Grant write access only when your specific workflow needs Jellyfin to modify media files.

Is a GPU required?

No. A GPU is not required for the Compose container or basic playback. Hardware acceleration is optional for supported transcoding workloads.

What should be backed up?

Back up the Jellyfin /config data, the Compose file, and the mount-path documentation. Protect the media separately, then follow the official backup and restore procedure for recovery.