LogoRiven Wiki

Deployment Guide

Deploy Riven in production with Docker, environment variables, and reverse proxies

Deployment Guide

This guide covers production deployment of Riven using Docker, environment variables, and reverse proxies.


Prerequisites

  • Operating System: Linux (Ubuntu, Debian, Fedora, Arch, etc.) or Windows WSL2
  • Docker: Version 20.10+ with Docker Compose
  • Hardware:
    • CPU: 2+ cores recommended
    • RAM: 4GB minimum, 8GB+ recommended
    • Disk: Fast SSD preferred for database and cache
    • Network: Stable internet connection

Linux Only

Riven only supports Linux-based systems. On Windows, use WSL2. MacOS is not officially supported due to FUSE limitations.


Docker Deployment

Complete docker-compose.yml

Here's a complete production-ready docker-compose.yml:

docker-compose.yml
volumes:
    riven-frontend-data:
    riven-pg-data:

services:
    # Riven Frontend
    riven-frontend:
        image: spoked/riven-frontend:dev # See Docker Hub Tags in Installation Guide
        container_name: riven-frontend
        restart: unless-stopped
        ports:
            - "3000:3000"
        environment:
            - TZ=America/New_York
            - DATABASE_URL=/riven/data/riven.db
            - BACKEND_URL=http://riven:8080
            - BACKEND_API_KEY=backend_api_key # Found in backend settings.json or set via RIVEN_API_KEY
            - AUTH_SECRET=your_auth_secret # Generate using: openssl rand -base64 32
            - ORIGIN=http://localhost:3000 # Change to your domain
        depends_on:
            riven:
                condition: service_healthy
        volumes:
            - riven-frontend-data:/riven/data # Persistent data storage
        networks:
            - riven-network

    # Riven Backend
    riven:
        image: spoked/riven:dev # See Docker Hub Tags in Installation Guide
        container_name: riven
        restart: unless-stopped
        shm_size: 1024m # Used for VFS caching
        ports:
            - "8080:8080"
        tty: true
        cap_add:
            - SYS_ADMIN
        security_opt:
            - apparmor:unconfined
        devices:
            - /dev/fuse
        environment:
            - PUID=1000
            - PGID=1000
            - TZ=America/New_York

            # Database
            - RIVEN_FORCE_ENV=true
            - RIVEN_DATABASE_HOST=postgresql+psycopg2://postgres:postgres@riven-db:5432/riven

            # VFS Configuration
            - RIVEN_FILESYSTEM_MOUNT_PATH=/mount
            - RIVEN_UPDATERS_LIBRARY_PATH=/path/to/library/mount # e.g., /mnt/riven

            # Downloaders (configure as needed)
            # - RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false
            # - RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=

            # Content Services (configure as needed)
            # - RIVEN_CONTENT_OVERSEERR_ENABLED=false

            # Scrapers (configure as needed)
            # - RIVEN_SCRAPERS_TORRENTIO_ENABLED=true

        healthcheck:
            test: curl -s http://localhost:8080 >/dev/null || exit 1
            interval: 30s
            timeout: 10s
            retries: 10
        volumes:
            - ./data:/riven/data # Backend data storage
            - /path/to/library/mount:/mount:rshared,z # e.g., /mnt/riven:/mount:rshared,z
        depends_on:
            riven-db:
                condition: service_healthy
        networks:
            - riven-network

    # Database
    riven-db:
        image: postgres:17-alpine
        container_name: riven-db
        restart: unless-stopped
        environment:
            PGDATA: /var/lib/postgresql/data/pgdata
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: postgres # Change in production!
            POSTGRES_DB: riven
        volumes:
            - riven-pg-data:/var/lib/postgresql/data/pgdata
        healthcheck:
            test: ["CMD-SHELL", "pg_isready -U postgres"]
            interval: 10s
            timeout: 5s
            retries: 5
        networks:
            - riven-network

    # Optional: Media Server (Plex example)
    plex:
        image: plexinc/pms-docker:latest
        container_name: plex
        restart: unless-stopped
        network_mode: host
        environment:
            - PUID=1000
            - PGID=1000
            - TZ=America/New_York
            - VERSION=docker
        volumes:
            - ./plex/config:/config
            - /path/to/library/mount:/mount:rslave,z
        # networks:
        #   - riven-network  # Can't use with network_mode: host

networks:
    riven-network:
        driver: bridge

Environment Variables Reference

Core Settings

# Debug and Logging
RIVEN_LOG_LEVEL=INFO  # DEBUG, INFO, WARNING, ERROR
RIVEN_TRACEMALLOC=false

# Database
RIVEN_FORCE_ENV=true
RIVEN_DATABASE_HOST=postgresql+psycopg2://user:password@host/database

Filesystem (VFS) Settings

# Mount Configuration
RIVEN_FILESYSTEM_MOUNT_PATH=/mount
RIVEN_FILESYSTEM_SEPARATE_ANIME_DIRS=false

# Cache Configuration
RIVEN_FILESYSTEM_CACHE_DIR=/dev/shm/riven-cache
RIVEN_FILESYSTEM_CACHE_MAX_SIZE_MB=10240
RIVEN_FILESYSTEM_CACHE_TTL_SECONDS=7200
RIVEN_FILESYSTEM_CACHE_EVICTION=LRU  # or TTL
RIVEN_FILESYSTEM_CACHE_METRICS=true

# Streaming Configuration
RIVEN_FILESYSTEM_CHUNK_SIZE_MB=8
RIVEN_FILESYSTEM_FETCH_AHEAD_CHUNKS=4

See Filesystem (VFS) for detailed explanations.


Updater Settings

# General
RIVEN_UPDATER_INTERVAL=120
RIVEN_UPDATERS_LIBRARY_PATH=/mount

# Plex
RIVEN_PLEX_ENABLED=true
RIVEN_PLEX_TOKEN=your_plex_token
RIVEN_PLEX_URL=http://plex:32400

# Jellyfin
RIVEN_JELLYFIN_ENABLED=false
RIVEN_JELLYFIN_API_KEY=
RIVEN_JELLYFIN_URL=http://jellyfin:8096

# Emby
RIVEN_EMBY_ENABLED=false
RIVEN_EMBY_API_KEY=
RIVEN_EMBY_URL=http://emby:8096

Downloader Settings

# Video Extensions
RIVEN_DOWNLOADERS_VIDEO_EXTENSIONS=["mp4","mkv","avi"]

# Proxy (optional)
RIVEN_DOWNLOADERS_PROXY_URL=

# Real-Debrid
RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true
RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=your_api_key

# AllDebrid
RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false
RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=

Content Service Settings

# Overseerr
RIVEN_CONTENT_OVERSEERR_ENABLED=true
RIVEN_CONTENT_OVERSEERR_API_KEY=your_api_key
RIVEN_CONTENT_OVERSEERR_URL=http://overseerr:5055
RIVEN_CONTENT_OVERSEERR_USE_WEBHOOK=false
RIVEN_CONTENT_OVERSEERR_UPDATE_INTERVAL=60

# Plex Watchlist
RIVEN_CONTENT_PLEX_WATCHLIST_ENABLED=false
RIVEN_CONTENT_PLEX_WATCHLIST_RSS=["https://rss.plex.tv/..."]
RIVEN_CONTENT_PLEX_WATCHLIST_UPDATE_INTERVAL=60

# Mdblist
RIVEN_CONTENT_MDBLIST_ENABLED=false
RIVEN_CONTENT_MDBLIST_API_KEY=
RIVEN_CONTENT_MDBLIST_LISTS=[]
RIVEN_CONTENT_MDBLIST_UPDATE_INTERVAL=300

# Trakt
RIVEN_CONTENT_TRAKT_ENABLED=false
RIVEN_CONTENT_TRAKT_API_KEY=
RIVEN_CONTENT_TRAKT_UPDATE_INTERVAL=300

Scraper Settings

# Torrentio
RIVEN_SCRAPERS_TORRENTIO_ENABLED=true
RIVEN_SCRAPERS_TORRENTIO_URL=http://torrentio.strem.fun
RIVEN_SCRAPERS_TORRENTIO_FILTER=sort=qualitysize%7Cqualityfilter=480p,scr,cam
RIVEN_SCRAPERS_TORRENTIO_TIMEOUT=30
RIVEN_SCRAPERS_TORRENTIO_RATELIMIT=true

# Add similar patterns for:
# - Jackett
# - Prowlarr
# - Zilean
# - Comet
# - Mediafusion

Host Mount Setup

Before starting Docker containers, set up the host mount point:

# Create mount directory
sudo mkdir -p /path/to/riven/mount

# Make it a shared bind mount
sudo mount --bind /path/to/riven/mount /path/to/riven/mount
sudo mount --make-rshared /path/to/riven/mount

# Verify
findmnt -T /path/to/riven/mount -o TARGET,PROPAGATION
# Should output: shared or rshared

Make Persistent (systemd)

Create /etc/systemd/system/riven-mount.service:

[Unit]
Description=Riven VFS Mount Preparation
After=local-fs.target
Before=docker.service

[Service]
Type=oneshot
ExecStart=/usr/bin/mount --bind /path/to/riven/mount /path/to/riven/mount
ExecStart=/usr/bin/mount --make-rshared /path/to/riven/mount
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Enable it:

sudo systemctl daemon-reload
sudo systemctl enable --now riven-mount.service

Reverse Proxy Configuration

Nginx

server {
    listen 80;
    server_name riven.example.com;

    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name riven.example.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    # Frontend
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Backend API
    location /api/ {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Caddy

riven.example.com {
    # Frontend
    reverse_proxy /* localhost:3000

    # Backend API
    reverse_proxy /api/* localhost:8080
}

Caddy handles SSL automatically with Let's Encrypt!


Traefik

# docker-compose.yml additions
services:
    riven-frontend:
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.riven-frontend.rule=Host(`riven.example.com`)"
            - "traefik.http.routers.riven-frontend.entrypoints=websecure"
            - "traefik.http.routers.riven-frontend.tls.certresolver=letsencrypt"
            - "traefik.http.services.riven-frontend.loadbalancer.server.port=3000"
        # Remove ORIGIN env variable

    riven:
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.riven-api.rule=Host(`riven.example.com`) && PathPrefix(`/api`)"
            - "traefik.http.routers.riven-api.entrypoints=websecure"
            - "traefik.http.routers.riven-api.tls.certresolver=letsencrypt"
            - "traefik.http.services.riven-api.loadbalancer.server.port=8080"

Security Hardening

Change Default Passwords

# In docker-compose.yml
riven-db:
    environment:
        - POSTGRES_PASSWORD=your_secure_password_here

riven:
    environment:
        - RIVEN_DATABASE_HOST=postgresql+psycopg2://postgres:your_secure_password_here@riven-db/riven

Set Custom API Key

# Generate a secure 32-character key
openssl rand -hex 16

# Add to docker-compose.yml
riven:
  environment:
    - API_KEY=your_generated_api_key_here

Firewall Rules

# Only allow necessary ports
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP (redirect)
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

# Docker containers should NOT expose ports directly
# Use reverse proxy as shown above

Monitoring

Health Checks

Riven includes built-in health checks:

# Check backend health
curl http://localhost:8080/health

# Check database
docker exec riven-db pg_isready -U postgres

Docker Logs

# Follow all logs
docker-compose logs -f

# Specific service
docker logs -f riven

# Last 100 lines
docker logs --tail 100 riven

Resource Usage

# Monitor container resources
docker stats riven riven-db riven-frontend

Updating Riven

Pull Latest Images

# Pull new images
docker compose pull

# Recreate containers
docker compose up -d

# Clean old images
docker image prune -f

Check for Updates

# See what's changed
docker compose pull
docker compose up -d --no-deps riven

Troubleshooting Deployment

See the main Troubleshooting Guide for detailed solutions.

Common issues:

  • FUSE not available → Install fuse3, load kernel module
  • Mount propagation → Follow host mount setup above
  • Database connection → Check credentials, network
  • API key errors → Generate proper 32-character key

On this page