SparkBox/Guides/Jellyfin permission errors

Jellyfin shows "Permission denied", boots unhealthy, or returns HTTP 000000

Your Jellyfin container won't install plugins, won't write its logs or database, gets marked unhealthy, and its API answers with the dreaded HTTP 000000 (no response at all). In almost every case the cause is the same: a folder that Jellyfin needs to write to is owned by root, but Jellyfin runs as a non-root user inside the container — so every write is denied.

Jellyfin home screen on a SparkBox server
Jellyfin home screen on a SparkBox server

Rather not hand-fix folder ownership? SparkBox sets the right ownership on every media app folder at install time and ships a repair command for the case that bites people. See the walkthrough →

The 10-second version: Jellyfin (and the *arr apps and Seerr) run as a non-root user defined by PUID/PGID. If a mounted folder is owned by root:root, those containers can't write inside it. Set the folder's owner to match the PUID/PGID the container uses, then restart — the Permission denied and HTTP 000000 errors go away.

First, understand what PUID/PGID means

Jellyfin's common Docker images (the linuxserver.io builds) don't run as root inside the container. They create an internal user named abc and map it to two numbers you set: PUID (the user ID) and PGID (the group ID). The default is usually 1000:1000. The *arr apps (Sonarr, Radarr, etc.) and the request app Seerr work the same way.

The point of this design is security: if the app is ever compromised, it isn't running as the all-powerful root user. The trade-off is that every folder you mount into the container must be writable by that PUID/PGID. When a folder is owned by root and only root can write to it, the app gets EACCES — "permission denied" — the moment it tries to create its own logs, cache or database subfolder. It then crashes or stalls during boot, never opens its web port, and Docker reports it as unhealthy. A health check or status probe hitting it gets no reply, which is what HTTP 000000 means.

Cause 1: A wipe-and-restart left the new folder root-owned

This is the most common path into the problem. The usual "fix it by starting clean" recipe looks like this:

sudo rm -rf /path/to/jellyfin/config
sudo docker start jellyfin

The trap is in the two words sudo and rm -rf. When you delete the folder as root and then let the container recreate the bind-mount target, the freshly created directory belongs to root:root. The installer normally sets ownership correctly when you first install, but this manual recovery path skips that step. So Jellyfin — running as PUID 1000 — tries to write into a root-owned folder and crashes on boot. The same thing knocks over the *arr apps and Seerr at the same time, because they all share this PUID/PGID model.

Fix: set ownership to match the container's PUID/PGID

  1. Find out what PUID/PGID Jellyfin runs as. Check the environment variables in your compose file or run:
    docker exec jellyfin id abc
    That prints something like uid=1000(abc) gid=1000(abc). The two numbers are your PUID and PGID. If your stack uses 1000:1000 (the common default), use those below.
  2. Check the current ownership of the folder Jellyfin mounts. Replace the path with your real config path:
    ls -la /path/to/jellyfin/config
    If you see root root in the owner columns, that's the bug.
  3. Hand ownership to the PUID/PGID the container uses. Using the default 1000:1000:
    sudo chown -R 1000:1000 /path/to/jellyfin/config
    Run the same chown on any other folder the container mounts and writes to — typically a separate config and a cache directory, plus your media folders if Jellyfin needs to write metadata into them.
  4. Restart the container.
    docker restart jellyfin
    It should now boot, open its web port, and drop the unhealthy status.

Do the same for the apps that crashed alongside it. Seerr and the *arr apps share the PUID/PGID model, so the same root-owned wipe takes them down too. On SparkBox the Seerr case has a dedicated, tested fix: sparkbox repair-seerr (shipped in v1.6.102). A broader ownership audit covering the rest is queued for v1.7.x. If you're hand-fixing, just repeat the chown -R 1000:1000 on each app's config folder.

Cause 2: The post-install script wrote to root-owned paths and built a doubled media path

The second cause is two compounding bugs in the media finalisation step (media-finish.sh), and it produces the same Permission denied symptoms plus a confusing duplicate folder.

SparkBox Apps page — one-click install cards for every module
SparkBox Apps page — one-click install cards for every module

Bug (a): a doubled /media/ segment

media-finish.sh builds its library folders as ${MEDIA_ROOT}/media/{Movies,TV,Music} — note the extra /media/ segment. The bootstrap script that the *arr apps trust (arr-bootstrap.sh) builds them as ${MEDIA_ROOT}/{Movies,TV,Music} instead, with no extra segment. The two disagree. When MEDIA_ROOT falls back to its default of ${SB_ROOT}/data/media, the finish script ends up creating the doubled path /opt/sparkbox/data/media/media/. Your media lands in one tree and Jellyfin and the *arr apps look in another, so libraries show up empty even though the files exist.

Bug (b): the script ran unprivileged against root-owned targets

The same script also writes under ${SB_ROOT} (/opt/sparkbox, which is root-owned) and into the Jellyfin plugins directory, but it runs without the privileges to do so. The result is exactly what you'd expect: "Permission denied" on /opt/sparkbox and on the Jellyfin plugin folder, and because the Jellyfin API never comes up cleanly, an HTTP 000000 when anything tries to talk to it.

Fix: align the path and correct ownership

  1. Check whether the doubled folder exists.
    ls -la /opt/sparkbox/data/media
    If you see a nested media folder (a second media/ inside the first), your media may have been written there.
  2. Point everything at one path. Decide on a single library root — the one your *arr apps already use, ${MEDIA_ROOT}/{Movies,TV,Music} — and move any content out of the doubled media/media/ tree into it so Jellyfin and the *arr apps agree.
  3. Fix ownership on the folders Jellyfin needs to write. The plugin directory and the config folder must belong to Jellyfin's PUID/PGID, not root:
    sudo chown -R 1000:1000 /path/to/jellyfin/config
  4. Restart Jellyfin and re-scan your libraries. Once it's writing to its own folders and reading from the correct media path, the plugin errors clear and the API responds normally.

Gotcha: Don't "fix" a doubled path by deleting the parent with sudo rm -rf and letting the container rebuild it — that lands you straight in Cause 1, with a root-owned folder and a fresh round of EACCES crashes. Move the files, then correct ownership, rather than wiping.

How to confirm the fix worked

  1. Health status: docker ps should show Jellyfin as healthy (or at least running without restarting in a loop).
  2. Logs: docker logs jellyfin should no longer print Permission denied or EACCES near startup.
  3. API: hitting the Jellyfin web port should return a real page or status, not HTTP 000000.
  4. Plugins: open the Jellyfin dashboard and confirm the plugin catalog loads and an install completes.

Frequently asked

Why does Jellyfin say Permission denied when it tries to write its plugin folder?

Jellyfin runs as a non-root user inside the container (the linuxserver.io images use the abc user mapped to PUID/PGID). If the mounted folder is owned by root, that user can't create or change files there, so plugin installs, logs and the database all fail with Permission denied.

Why is Jellyfin marked unhealthy and the API returns HTTP 000000?

HTTP 000000 means the probe got no response, because Jellyfin never finished starting. When it can't write its config or database directory it exits or stalls during boot, the web port never opens, and Docker reports it as unhealthy.

I wiped my config and now everything crashes on boot. What happened?

Deleting a folder with sudo rm -rf and recreating it via the container leaves the new empty directory owned by root:root. Non-root containers — Jellyfin, the *arr apps, Seerr — then can't create their own subfolders and crash with EACCES. Set ownership back to the PUID/PGID they run as (commonly 1000:1000) before starting them.

Does a fresh install have this problem?

No. The installer sets ownership correctly at first install. This affects the manual wipe-and-restart recovery path, which skipped that step, plus the doubled-path and unprivileged-write bugs in media-finish.sh. On SparkBox the Seerr case is fixed via sparkbox repair-seerr in v1.6.102, with a broader audit queued for v1.7.x.

Skip the chown archaeology

SparkBox sets the right PUID/PGID ownership on every media-app folder at install, and ships sparkbox repair-seerr for the recovery path that trips people up — so Jellyfin, the *arr apps and Seerr come up writable the first time.

Get SparkBox → Or read the media-server walkthrough →

About this guide: Written and tested by the SparkBox team on a UGREEN DXP4800 Plus and a $7/month Hostinger VPS, both running SparkBox 1.6.314. The causes above are the real ones we've diagnosed in d/sparkbox. If something doesn't match, tell us on YouTube.