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
The Audiobookshelf Docker image runs as root by default. If you try to run it as a non-root user with the user: directive in Docker Compose, the container crashes immediately with permission errors:
FATAL: [Server] Unhandled rejection: [Error: EACCES: permission denied, mkdir '/metadata/streams'] {
errno: -13,
code: 'EACCES',
syscall: 'mkdir',
path: '/metadata/streams'
}
The root cause is in the Dockerfile and Server.js. The image never pre-creates /config and /metadata with open permissions. Docker auto-creates volume mount points owned by root, and then the Node app (running as your non-root user) tries to mkdirSync subdirectories like /metadata/streams and /metadata/logs and gets denied. Two community PRs have proposed fixes: #4474 (still open) pre-creates the directories during the Docker build, and #4700 (closed without merge) added PUID/PGID support via an entrypoint script. Neither approach has landed in the official image.
This hits Docker Compose users, Podman/Quadlet users, and anyone on Unraid or Synology where file permissions are locked down.
Pre-create and own the directories
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
# (change 1000:1000 to match your setup)
chown -R 1000:1000 ./config ./metadata
Then use bind mounts in your compose file instead of Docker-managed volumes:
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 ABS install running as root and want to switch to non-root, you need to fix ownership on the existing data first:
# Stop the container
docker compose down
# Change ownership of config and metadata
# (your audiobook files probably already have the right permissions)
chown -R 1000:1000 ./config ./metadata
# Update compose to add user: "1000:1000" and restart
docker compose up -d
Check that ABS starts cleanly. If it doesn’t, look at the logs for which specific path is failing and chown that too.
Podman and rootless containers
If you’re using Podman with quadlet files, ABS is trickier because Podman defaults to rootless operation. The same fix applies: pre-create and own the directories. One user on the issue thread also ran into SELinux issues with volume labels. If you’re on a SELinux-enabled system (Fedora, RHEL), use the lowercase :z option on your volume mounts, not :Z:
- ./config:/config:z
- ./metadata:/metadata:z
The lowercase :z marks the content as shared between containers. The uppercase :Z marks it as private, which can break bind mounts for other services sharing the same paths.
If that didn’t work
If the basic pre-create approach doesn’t cover it (some setups have additional subdirectories that need to exist), you can create all the subdirectories ABS expects:
mkdir -p ./config
mkdir -p ./metadata/streams
mkdir -p ./metadata/logs
mkdir -p ./metadata/cache
mkdir -p ./metadata/items
chown -R 1000:1000 ./config ./metadata
Another option is running with a custom entrypoint that fixes permissions at startup. A community member wrote a PR that adds PUID/PGID environment variable support (similar to how linuxserver.io images work). The PR was closed without merge, but the approach works. If you’re comfortable building the image yourself, their fork has the entrypoint script you’d need.
Affected versions
This affects all current versions of Audiobookshelf’s Docker image. The Dockerfile has never included the VOLUME instruction or pre-created directories with non-root-friendly permissions. If you’re setting up a new Docker install, factor in the directory ownership from the start and you’ll avoid the issue entirely.