Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rivenmedia/riven/llms.txt

Use this file to discover all available pages before exploring further.

Riven is distributed as a Docker image (spoked/riven:latest) and is designed to run alongside a PostgreSQL database and a media server in a single Docker Compose stack. This page covers every aspect of a production deployment: the full Compose file, all supported environment variables, volume mount requirements, FUSE bind mount setup, and options for making the mount persistent across reboots.

Docker Compose (primary deployment method)

The Compose file below is the canonical starting point — it is identical to the upstream docker-compose.yml with inline annotations. Copy it to your host, replace the /path/to/riven/* placeholders with real host paths, and follow the bind mount instructions in the next section before starting the stack.
services:
    # Optional: riven-frontend
    # Uncomment if you want to run the separate frontend container
    # (spoked/riven-frontend:latest) alongside the backend.
    # riven-frontend:
    #     image: spoked/riven-frontend:latest
    #     container_name: riven-frontend
    #     restart: unless-stopped
    #     tty: true
    #     environment:
    #         - TZ=Etc/UTC
    #     ports:
    #         - 3000:3000
    #     volumes:
    #         - /path/to/riven/frontend:/riven/config
    #     depends_on:
    #         riven:
    #             condition: service_started

    riven:
        image: spoked/riven:latest
        container_name: riven
        restart: unless-stopped
        ports:
            - "8080:8080"
        tty: true
        cap_add:
            - SYS_ADMIN          # required for FUSE mount operations
        security_opt:
            - apparmor:unconfined # required on AppArmor-enabled hosts (e.g. Ubuntu)
        shm_size: 1024m
        devices:
            - /dev/fuse          # FUSE device access for RivenVFS
        environment:
            - PUID=1000
            - PGID=1000
            - TZ=America/New_York
            - RIVEN_FORCE_ENV=true
            - RIVEN_DATABASE_HOST=postgresql+psycopg2://postgres:postgres@riven-db/riven
        healthcheck:
            test: curl -s http://localhost:8080 >/dev/null || exit 1
            interval: 30s
            timeout: 10s
            retries: 10
        volumes:
            - /path/to/riven/data:/riven/data
            - /path/to/riven/mount:/mount:rshared,z
        depends_on:
            riven_postgres:
                condition: service_healthy

    riven_postgres:
        image: postgres:17-alpine
        container_name: riven-db
        environment:
            - PGDATA=/var/lib/postgresql/data/pgdata
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=postgres
            - POSTGRES_DB=riven
        volumes:
            - /path/to/riven/db:/var/lib/postgresql/data/pgdata
        healthcheck:
            test: ["CMD-SHELL", "pg_isready -U postgres"]
            interval: 10s
            timeout: 5s
            retries: 5

    jellyfin:
        image: jellyfin/jellyfin
        container_name: jellyfin
        user: 1000:1000
        restart: unless-stopped
        ports:
            - "8096:8096"
        volumes:
            - /path/to/jellyfin/config:/config
            - /path/to/riven/mount:/mount:rslave,z
The riven_postgres service uses a health check (pg_isready). The riven service declares depends_on: condition: service_healthy, so Docker will not start the Riven backend until PostgreSQL is accepting connections.

Environment variables

Container runtime variables

These variables are consumed by the container entrypoint script (entrypoint.sh) before the Python process starts. They control the OS-level user and group that Riven runs as inside the container.
VariableDefaultDescription
PUID1000UID of the user created inside the container. Set this to match your host user’s UID so volume-mounted files have correct ownership (id -u).
PGID1000GID of the group created inside the container. Set this to match your host user’s primary GID (id -g).
TZAmerica/New_YorkTimezone string (e.g. Europe/London, Etc/UTC). Used for log timestamps and scheduling.
DEBUG(unset)Set to any non-empty value to start the backend under debugpy on port 5678, enabling remote Python debugging.

Application variables

These variables are read by the Python application at startup and map directly into Riven’s settings.json.
VariableDefaultDescription
RIVEN_FORCE_ENVfalseWhen true, Riven reads all RIVEN_* prefixed environment variables and merges them into settings.json at startup. This lets you configure any nested setting without editing the file.
RIVEN_DATABASE_HOSTpostgresql+psycopg2://postgres:postgres@localhost/rivenFull SQLAlchemy connection string for the PostgreSQL database. In a Compose stack, point this at the service name of your riven_postgres container (e.g. …@riven-db/riven).
SETTINGS_FILENAMEsettings.jsonOverride the name of the settings file loaded from /riven/data/. Useful when running multiple Riven instances sharing the same data volume.
RIVEN_FORCE_ENV=true combined with the RIVEN_ prefix convention lets you override any nested settings key from your Compose file without touching settings.json. For example, RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=your_key will set settings.downloaders.real_debrid.api_key.

Server port

The Riven FastAPI server port is configured via the --port CLI argument when launching src/main.py. Inside the container, the default is 8080. The docker-compose.yml maps "8080:8080" accordingly. If you need to change the host-side port, update the ports mapping rather than the container-side port.

Volume mounts

Container pathPurpose
/riven/dataPersistent data directory. Contains settings.json, log files, and database snapshots. Mount a host directory here so settings survive container restarts.
/mountRivenVFS FUSE mount point. Must be mounted with :rshared,z so that FUSE sub-mounts are propagated to other containers that share this path.
The /mount volume must include the :rshared,z mount options in the Riven container. Without rshared, media server containers will not see the FUSE filesystem that RivenVFS creates, and your libraries will appear empty.

Bind mount setup

RivenVFS uses FUSE to present debrid-cached files as a filesystem at /mount. For the media server container to receive these mount events, the host directory must be marked as a shared bind mount before Docker is started.

One-time setup (run after every host reboot until automated)

sudo mkdir -p /path/to/riven/mount
sudo mount --bind /path/to/riven/mount /path/to/riven/mount
sudo mount --make-rshared /path/to/riven/mount
Verify the propagation flag:
findmnt -T /path/to/riven/mount -o TARGET,PROPAGATION
# Expected: PROPAGATION = shared  or  rshared
Verify propagation inside the media server container (Plex or Jellyfin):
docker exec -it jellyfin sh -c 'findmnt -T /mount -o TARGET,PROPAGATION,OPTIONS,FSTYPE'
# PROPAGATION should be rslave or rshared
# FSTYPE should show fuse when RivenVFS is active

Making the bind mount persistent on boot

Choose one of the two options below.
The Riven Dockerfile configures user_allow_other in /etc/fuse.conf automatically, so you do not need to set it yourself. On SELinux-enabled hosts, add :z to the bind mount command if volume relabelling is required.

Media server mount propagation

The media server container must mount the same host path with :rslave,z (or :rshared,z) to receive FUSE mount events from RivenVFS.
# In your Plex or Jellyfin Compose service:
volumes:
  - /path/to/riven/mount:/mount:rslave,z
In your media server library settings, point libraries at the container-internal path — for example /mount/movies and /mount/shows — not the host path. Both Riven and the media server refer to /mount inside their respective containers, and mount propagation ensures they see the same FUSE filesystem.

Troubleshooting: /mount appears empty after Riven restart

If your media server shows an empty /mount after restarting Riven, the most common causes are:
  1. Missing rshared on the host — re-run the bind mount commands above and verify with findmnt.
  2. Stale FUSE mount — if Riven crashed without cleanly unmounting, run:
    sudo fusermount -uz /path/to/riven/mount || sudo umount -l /path/to/riven/mount
    
    Then restart the Riven container.
  3. Wrong path in Riven settings — ensure the VFS mount path in Riven settings is set to the container-internal path /mount, not the host path.

Health check

Riven exposes a health endpoint at GET /api/v1/health. It returns {"message": "True"} once the program has fully initialised.
curl http://localhost:8080/api/v1/health
The Docker health check in docker-compose.yml polls http://localhost:8080 every 30 seconds with up to 10 retries, giving Riven up to 5 minutes to complete startup before being marked unhealthy.

Required container capabilities

The Riven container requires the following Linux capabilities and device access to run RivenVFS:
RequirementPurpose
cap_add: SYS_ADMINAllows FUSE mount operations inside the container.
devices: /dev/fuseGrants access to the host FUSE character device.
security_opt: apparmor:unconfinedPrevents AppArmor from blocking FUSE syscalls on Ubuntu and other AppArmor-enabled hosts.
shm_size: 1024mIncreases shared memory available to the container, used by RivenVFS caching.

Persistence summary

Path on hostMounted atWhat is stored
/path/to/riven/data/riven/datasettings.json, log files, database snapshots
/path/to/riven/db/var/lib/postgresql/data/pgdataPostgreSQL data directory (media item state)
/path/to/riven/mount/mountRivenVFS FUSE mount (no persistent files — virtual)

Build docs developers (and LLMs) love