Skip to content

Docker guide

Run a media server in Docker with Compose

How to run a self-hosted media server in Docker: Quven Server with Compose on Linux AMD64 or ARM64, data that survives upgrades, and GPU transcoding.

A small server inside an open wall cabinet, with the docker compose commands that run Quven
By Quven Editorial Team Updated Verified

The official Quven Server image runs on Linux AMD64 and ARM64. This is the Server-only edition: it has no desktop client and doesn’t host the web app. These steps take you from an empty directory to a linked server, then through the GPU profiles and upgrades.

Requirements

Requirement What Quven expects
Host Linux on AMD64 or ARM64
Container runtime Docker Engine with Docker Compose v2. Docker Desktop isn’t supported
Network Host networking, so the apps find the server on the LAN
Library A folder the container’s user, uid 10001, can write to, or a read-only mount
Hardware acceleration Optional: the NVIDIA Container Toolkit, an Intel or AMD render node, or a V4L2 device on ARM

Docker Desktop can’t offer host networking or LAN discovery, which is how Quven reaches its clients. A modest machine is enough, and the self-hosted media server guide explains when a graphics card starts to matter.

1. Download the Compose files

mkdir -p quven && cd quven
curl -fsSLO https://quven.tv/docker/1.5.7/compose.yaml
curl -fsSL https://quven.tv/docker/1.5.7/env.example -o .env

Open .env and set QUVEN_MEDIA_DIR to the absolute path of your library. Leave the image on ghcr.io/nickayl/quven:latest: a version number there holds the server on the release it names, where none of the later corrections can reach it.

The quven-data volume holds the database, the configuration and the logs, so you can recreate the container without losing them.

docker compose config
docker compose pull
docker compose up -d
docker compose ps
curl --fail --silent --show-error http://127.0.0.1:5181/health/container -o /dev/null
docker compose exec quven /app/Quven.Local.Server account link

Open the URL that account link prints, sign in to your Quven Account and approve the server. Native clients find it on the LAN, or you can point them at http://<server-ip>:5181. With Pro, app.quven.tv reaches it through the managed relay as well.

The image runs as UID/GID 10001, drops Linux capabilities, blocks privilege escalation and keeps its root filesystem read-only. Don’t turn the service into a privileged container to get around a host permission problem.

3. Add a library

Libraries are added from the Quven desktop app on any computer on the same network, or from app.quven.tv with Pro. Phones and televisions can’t create them, so a server that only meets a television client stays empty.

When the app asks for folders, pick them below /media: a host library at /srv/media shows up inside Quven as /media.

Library permissions

Your media is mounted writable, because Quven writes the subtitles it generates beside the film, and the covers of books and albums when you ask for them, so the folder has to be writable by uid 10001. Without write access the copy fails with a warning in the server log, and the subtitle still plays from Quven’s own store in the data volume.

To keep the server from ever writing into the library:

echo 'QUVEN_MEDIA_READ_ONLY=true' >> .env
docker compose up -d

You only give up the copy that travels with the file if you move it.

Quven follows symbolic links inside a library, as long as the container sees the target at the path the link names. A link reading /mnt/decypharr/film.mkv is unreachable inside a container that was only given /media. Mount the target under the same path, in the same service, beside the media mount:

volumes:
  - type: bind
    source: /mnt/decypharr
    target: /mnt/decypharr

Check what the container can read before scanning a whole library:

docker compose exec quven ls /mnt/decypharr

Hardware acceleration

Play one title on the base service first, since that proves the network, the library and the account path before a GPU enters the picture. Then add one override, the one that matches an interface you’ve seen on the host. The options are alternatives: don’t combine them, except where the Raspberry Pi profile asks for the V4L2 base.

NVIDIA

With the NVIDIA Container Toolkit installed:

curl -fsSLO https://quven.tv/docker/1.5.7/compose.nvidia.yaml
docker compose -f compose.yaml -f compose.nvidia.yaml config
docker compose -f compose.yaml -f compose.nvidia.yaml up -d

The 1.5.7 profile asks for the GPU with runtime: nvidia, which every Compose release accepts once the Container Toolkit has registered its runtime:

nvidia-ctk runtime configure --runtime=docker

A host set up for CDI instead of that runtime drops that key and asks with gpus: all, which older Compose releases reject. Keep NVIDIA_DRIVER_CAPABILITIES as published: without the video capability the driver carries no NVENC, and the server encodes in software without telling you.

Intel or AMD

Pass the host’s render and video group IDs into the container:

curl -fsSLO https://quven.tv/docker/1.5.7/compose.vaapi.yaml
echo 'QUVEN_RENDER_GID='"$(getent group render | cut -d: -f3)" >> .env
echo 'QUVEN_VIDEO_GID='"$(getent group video | cut -d: -f3)" >> .env
docker compose -f compose.yaml -f compose.vaapi.yaml config
docker compose -f compose.yaml -f compose.vaapi.yaml up -d

If the right GPU isn’t /dev/dri/renderD128, set QUVEN_DRI_RENDER_DEVICE in .env, with the same path inside and outside the container.

Raspberry Pi or other ARM boards

This is an advanced setup, and the base service already plays most libraries well. Hosts that expose V4L2 M2M codecs can use compose.v4l2m2m.yaml. A Raspberry Pi 4 can also add compose.raspberry-pi.yaml for HEVC Request API decode. Both profiles need the real device paths the system reports and refuse to start when the setup is incomplete.

Inspect the interfaces first, and never expose all of /dev:

v4l2-ctl --list-devices

Scan new files automatically

Open Settings, then the Server tab, then the Automatic scanning card. It comes with Pro, Max and Lifetime and has four switches, all applied when you save, with no restart:

  • Detect new files in the folders: starts an incremental scan about thirty seconds after the last file stops changing. It never forces a rematch or spends an AI credit.
  • Periodic scan: scans every library once a day unless you pick another interval, and skips a library that is already being scanned.
  • Save each book’s cover beside its file: writes the cover next to the book under the same name, Dune.jpg beside Dune.epub, and keeps an image already in the folder.
  • Save each album’s cover in its folder: writes the cover the recordings carry as cover.jpg, unless the folder already holds an image.

A bind mount of a local disk is watched directly. A share mounted over SMB or NFS can’t be watched, because the kernel can’t see what another machine writes there, so Quven compares those folders against their last snapshot every five minutes and the card shows polling. A folder that reads as unreachable is one the container can’t open at that path, and the row’s tooltip says why. After moving a whole folder, run a manual scan.

Upgrade

  1. Note the digest in use, so you know what you would be going back to.

  2. Stop the service and copy the data volume, .env and the Compose files. Never delete them during an update or a rollback.

  3. Pull and start the current release:

    docker compose stop
    docker volume ls --filter name=quven-data
    docker run --rm -v <volume-name>:/data -v "$PWD":/backup alpine tar czf /backup/quven-data.tgz -C /data .
    docker compose pull
    docker compose up -d
  4. Check health, then play one direct-play title and one that must transcode.

A release can change the database schema, and an older image can’t read a database a newer one has already migrated. Going back to an earlier digest in QUVEN_IMAGE therefore means restoring the database too, from your own copy of the volume or from the snapshot the server writes under backups/quven-pre-migration-<timestamp>.db inside it before a schema change. Only the latest snapshot is kept, since each new one replaces the one before.

Verify what you run

The image is signed and carries an SPDX attestation:

curl -fsSLO https://quven.tv/docker/1.5.7/cosign-public-key.pem
cosign verify --key cosign-public-key.pem ghcr.io/nickayl/quven:latest
cosign verify-attestation --key cosign-public-key.pem --type spdxjson ghcr.io/nickayl/quven:latest

The public 1.5.7 manifest holds the image-index digest and each platform digest. Keep it beside the configuration you approved.

Frequently asked questions

Can I run it with Docker Desktop?

No. Docker Desktop provides neither host networking nor LAN discovery, so you need Docker Engine on Linux with Docker Compose v2.

Should I pin the version or follow latest?

Follow latest. Each release fixes problems found in the earlier ones, and a pinned tag keeps every one of them. The manifest of each release records its digest, so you can always tell what a pull installed.

Will Quven write to my media folder?

Only the subtitles it generates, beside the film they belong to, and the covers of books and albums once you turn on the two cover switches of the Automatic scanning card. A scan writes nothing else. Set QUVEN_MEDIA_READ_ONLY=true in .env to stop even that, and Quven keeps them in its own volume.

Does hardware transcoding work in Docker?

Yes, on every plan. Each kind of hardware has its own profile, so play one title on the base service and then add the one that matches your host.

Which hardware profile do I add?

One, and only after you’ve seen the matching interface on the host. Several at once produce a configuration that looks convenient and fails on the first difficult file.

Quven Lifetime

Every Max capability, with one payment

Lifetime is Max without renewal: the managed relay and the web client, subtitles written from the audio, the subtitle editor with translation, and the repair of damaged files, today and as Max grows. It also lets you share the server with up to 150 people.

€129, once.

Keep learning

Related guides

Go deeper into the parts that materially affect the result on your hardware and screens.

Bring your library home.

Free for personal use, installed on your own hardware in a few minutes.