Install

Up and running in five minutes

Vora is one image bundling the API and the web client. It needs PostgreSQL with thevector extension, a volume for its data, and your media mounted read-only. Schema migrations apply themselves on startup.

Quick start

docker compose

The fastest path. Save this as docker-compose.yml, change the two passwords and the JWT secret, adjust the media paths, then run docker compose up -d.

docker-compose.yml
services:
  postgres:
    image: pgvector/pgvector:pg16
    container_name: vora-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: vora
      POSTGRES_USER: vora
      POSTGRES_PASSWORD: change-me-to-a-strong-password
    volumes:
      - /srv/vora/postgres-data:/var/lib/postgresql/data

  vora:
    image: ghcr.io/axufuris/vora-media-server:latest
    container_name: vora
    restart: unless-stopped
    depends_on:
      - postgres
    ports:
      - "8080:8080"
    environment:
      PUID: 99
      PGID: 100
      ASPNETCORE_HTTP_PORTS: 8080
      ConnectionStrings__DefaultConnection: "Host=postgres;Port=5432;Database=vora;Username=vora;Password=change-me-to-a-strong-password"
      Jwt__SecretKey: "REPLACE_WITH_A_LONG_RANDOM_STRING"
      StoragePaths__CustomArtwork: /app/data/custom_artwork
      StoragePaths__OriginalArtworkCache: /app/data/original_artwork_cache
      StoragePaths__EpgCache: /app/data/iptv/epg_cache
      StoragePaths__IptvDvr: /app/data/iptv/dvr
      StoragePaths__UserImages: /app/data/users
      StoragePaths__Plugins: /app/data/plugins
      StoragePaths__Subtitles: /app/data/subtitles
    volumes:
      - /srv/vora/data:/app/data
      - /srv/vora/transcode:/transcode
      - /mnt/media/movies:/media/movies:ro
      - /mnt/media/shows:/media/shows:ro
      - /mnt/media/music:/media/music:ro

Generate a JWT secret

Any long random string works. Keep it somewhere safe — changing it later invalidates every session.

shell
openssl rand -base64 64

Bring the stack up

Vora waits for Postgres, applies its migrations, and starts serving.

shell
docker compose up -d
docker compose logs -f vora

Create the first profile

Open http://your-host:8080. The first profile you create through the setup flow becomes the server admin. Add your libraries from the admin area and let the first scan run.

Image tags

Which tag to pull

Images are published to GitHub Container Registry for linux/amd64.

TagWhat it is
latestThe newest stable release. Recommended.
1.4, 1.4.2Pin a specific release or minor line.
qaPre-release builds from the main branch. Expect breakage.
Configuration

The environment variables that matter

VariablePurpose
ConnectionStrings__DefaultConnectionNpgsql connection string. Use the Postgres container name as the host when both sit on one Docker network.
Jwt__SecretKeyLong random string signing account and profile tokens. Generate once and keep it stable — rotating it signs everyone out.
PUID / PGIDUser and group the container runs as. Data and transcode mounts are chowned to match on startup, so no host-side chown is needed. Defaults are 99 / 100.
StoragePaths__*Where artwork, the EPG cache, DVR recordings, user images, plugins, and subtitles live inside the container. Keep them under one mounted volume.
ASPNETCORE_HTTP_PORTSListening port inside the container. 8080 unless you have a reason.

Seed plugin API keys from compose

Plugin settings can be supplied as environment variables. They are written only when the database row is still empty, so anything you later change in the admin UI wins.

The pattern is Vora__PluginSettings__<pluginId>__<settingKey>. Unknown plugins and unknown keys are skipped with a warning, and values are redacted from the logs.

environment
Vora__PluginSettings__tmdb_metadata__api_key: "YOUR_TMDB_KEY"
Vora__PluginSettings__tvdb_metadata__api_key: "YOUR_TVDB_KEY"
Vora__PluginSettings__fanart_artwork__api_key: "YOUR_FANART_KEY"
Vora__PluginSettings__lastfm_listening__api_key: "YOUR_LASTFM_KEY"
Vora__PluginSettings__opensubtitles_search__api_key: "YOUR_OPENSUBS_KEY"
Vora__PluginSettings__openai_recommendations__is_enabled: "true"
Optional

NVIDIA GPU transcoding

Install the NVIDIA Container Toolkit on the host and reserve a GPU. The image already carries the driver capabilities it needs, so CUDA decode, NVENC encode, and HDR-to-SDR tone mapping work without further configuration.

Do not narrow NVIDIA_DRIVER_CAPABILITIES — dropping libcuda.so.1 is the usual cause of a transcode failing to start.

docker-compose.yml (vora service)
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu, video]
Unraid

Community Applications

Add both containers from a manual template with the Network Type set to the same custom bridge, so Vora can resolve Postgres by container name.

  • Leave Postgres unmapped — keeping the database off the LAN is safer, and Vora reaches it over the internal network.
  • PUID 99 / PGID 100 — Unraid's nobody:users, which already own everything under /mnt/user.
  • Media read-only — set each library path to Access Mode Read Only.
  • For GPU transcoding — install the Nvidia-Driver plugin and set Extra Parameters to --runtime=nvidia. Nothing else is required.

Upgrading

Pull the new image and restart. Vora detects pending migrations and applies them before it serves traffic; your data volume and the Postgres directory persist. If a migration fails the container exits rather than serving a half-migrated schema.

shell
docker compose pull vora
docker compose up -d vora

Something not working?

The README covers every storage path, plugin setting, and deployment quirk in detail. Bugs and questions are welcome on the issue tracker.