Skip to content
SoundLeaf
App Store
Menu

Server guides · Troubleshooting

Running Audiobookshelf as a non-root user in Docker

Fix EACCES permission denied errors when running Audiobookshelf Docker with user: 1000:1000 by pre-creating config and metadata directories

Updated

All server guides
On this page
  1. Create the folders and set their owner
  2. If you’re already running as root and want to switch
  3. Run Audiobookshelf with rootless Podman
  4. Check permissions on existing subfolders
  5. TrueNAS: EPERM crash loop on 2.37 with user 568
  6. Affected versions
Illustrated cross-section of a Docker whale built from stacked file system layers, with red root-owned layers at the bottom and a small user icon locked outside

To run Audiobookshelf as a non-root Docker user, create the config and metadata folders first and give that user ownership of them. The image runs as root by default. If you set user: in Docker Compose and the folders are owned by root, startup fails with permission errors like this:

FATAL: [Server] Unhandled rejection: [Error: EACCES: permission denied, mkdir '/metadata/streams'] {
  errno: -13,
  code: 'EACCES',
  syscall: 'mkdir',
  path: '/metadata/streams'
}

Here’s why. Audiobookshelf creates folders like /metadata/streams and /metadata/logs when it starts. When Docker makes a missing host folder for a volume, root owns it, so a non-root user isn’t allowed to create anything inside. Two community pull requests tried to fix this in the image: #4474 (still open) and #4700 (closed without merge). Neither has landed as of 2.37.1.

This hits Docker Compose users, Podman/Quadlet users, and Unraid or Synology setups where the container user can’t write to these folders.

Create the folders and set their owner

The simplest workaround is to create the config and metadata directories on the host, owned by the user you want the container to run as, before starting it.

# Create the directories with your user
mkdir -p ./config ./metadata

# Make sure they're owned by the UID:GID you'll pass to Docker.
# Find yours with `id -u` and `id -g`, and change 1000:1000 to match.
# Use sudo if the folders already exist and belong to root.
sudo chown -R 1000:1000 ./config ./metadata

Then use bind mounts (folders you pick on the host, like ./config) in your compose file instead of named volumes that Docker manages for you:

services:
  audiobookshelf:
    image: ghcr.io/advplyr/audiobookshelf:latest
    container_name: audiobookshelf
    restart: unless-stopped
    user: "1000:1000"
    environment:
      - TZ=America/Denver
    ports:
      - 13378:80
    volumes:
      - ./audiobooks:/audiobooks
      - ./config:/config        # bind mount, not a named volume
      - ./metadata:/metadata    # bind mount, not a named volume

The key difference: Docker-managed volumes (the audiobookshelf-config: syntax) get created as root. Bind mounts to pre-existing directories keep whatever ownership you set.

If you’re already running as root and want to switch

If you have an existing Audiobookshelf install running as root and want to switch to non-root, you need to fix ownership on the existing data first. The commands below assume your data is in bind mounts at ./config and ./metadata. If you use named volumes or other paths, keep your current volume lines and change the ownership of the folders they point to instead:

# Stop the container
docker compose down

# Change ownership of config and metadata (root owns them now, so use sudo)
# (your audiobook files probably already have the right permissions)
sudo chown -R 1000:1000 ./config ./metadata

# Update compose to add user: "1000:1000" and restart
docker compose up -d

Check that Audiobookshelf starts cleanly. If it doesn’t, the log names the path that failed. If it’s inside config or metadata, see check permissions on existing subfolders. If it’s one of your media folders, check its permissions before you change anything.

Run Audiobookshelf with rootless Podman

Quadlet is Podman’s way of running containers as system services. If you run Podman rootless (for example through a Quadlet user service), there’s a twist: user numbers inside the container are mapped to different numbers on the host. So the folder owner you set on the host may not be the user the container sees.

Creating the folders and setting their owner still applies, but you also need the numbers to line up. Podman’s --userns=keep-id option (UserNS=keep-id in a quadlet file) runs the container as your own host user, so folders you own on the host are yours inside too. Remove any user: setting for this container, or make it match your host user. The Podman run docs explain the options.

On a system with SELinux (a Linux security system used by Fedora and RHEL) turned on, volumes also need a label, or the container can’t read them. A Quadlet user on the issue thread ran into this. The Podman volume docs describe both labels. Use :Z when only this container uses the folder, and :z when other containers share it too:

- ./config:/config:Z
- ./metadata:/metadata:Z

The uppercase :Z labels the folder as private to this container. The lowercase :z labels it as shared between containers.

Check permissions on existing subfolders

If the error names a subfolder of config or metadata (like /metadata/streams), that subfolder may still have the wrong owner. Check it with ls -la. If the owner is wrong, run the ownership command again for the whole folder, so every subfolder is covered, then start the container. Audiobookshelf creates any missing subfolders itself once it’s allowed to:

sudo chown -R 1000:1000 ./config ./metadata
docker compose up -d

If the owner already matches your container user, look at the folder’s permissions, then the Podman user mapping and SELinux labels (see the Podman section above).

Audiobookshelf doesn’t read PUID or PGID settings like some other images do. The comment in the official compose example says “Audiobookshelf does not use PUID or GUID environment variables” and shows user: instead. The community pull request that added them (#4700) was closed without merge.

TrueNAS: EPERM crash loop on 2.37 with user 568

The TrueNAS SCALE setups in #5637 run the container as user 568 with /config on a ZFS dataset. With 2.37.0 and 2.37.1, they crash-loop (fail and restart over and over) at startup with a different error:

[cause]: [Error: EPERM: operation not permitted, copyfile '/app/dist-server/server/migrations/v2.15.0-series-column-unique.js' -> '/config/migrations/v2.15.0-series-column-unique.js']
FATAL: [Server] Unhandled rejection: Error: Database migration failed

The reporters still saw this error after confirming that user 568 can write to /config. They suspect the way Node.js 24 copies files, which isn’t confirmed. They reported that rolling back to 2.36.1 gets the server starting again. It’s tracked in #5637, open as of October 17, 2026. The 2.37 section of the upgrade crash guide has the rollback steps.

Affected versions

The permission errors on this page apply to the Docker image up to 2.37.1, the latest release as of October 2026. Its Dockerfile doesn’t create the config and metadata folders for a non-root user. If you’re setting up a new Docker install, sort out the folder ownership from the start. That prevents the startup errors caused by root-owned config and metadata folders. The TrueNAS crash above is a separate problem.