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

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
- Preflight
- Compose service explained
- UID/GID permissions
- First-run wizard
- Media libraries
- Hardware acceleration
- Verify direct play and transcoding
- Backup, update, and rollback
- Common failures
- Next step
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:
configstores the database, server settings, users, plugins, and library metadata.cachestores temporary working data.mediacontains 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
8096is available for the web interface. - UDP port
7359is 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.


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:
- The host directory contains the expected files.
- Compose maps that directory into the container.
- The container-side directory exists and is readable.
- The library type matches the content.
- Jellyfin can write to
/configand/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.
- Run
docker compose psand confirm that Jellyfin is running. - Review
docker compose logs jellyfinfor mount, permission, or startup errors. - Open the server on TCP port
8096. - Play a file from a known-good library.
- Inspect playback information in the client or server interface.
- Confirm whether the session is direct playing or transcoding.
- 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:
- Stop or pause the service before making a consistent configuration backup.
- Copy the
/configcontents to separate storage. - Preserve the Compose file and record the host-to-container mappings.
- Verify that the backup can be listed or opened.
- Update the image and restart the service.
- Confirm login, libraries, and playback.
- 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
8096is 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
/configand/cache. - Libraries scan incompletely: inspect permissions and confirm that the mount points to the expected storage volume.
- Discovery does not work: verify the UDP
7359mapping 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.