How to Self-Host Kyoo with Docker Compose

What Is Kyoo?

Kyoo is a self-hosted media browser and streaming server for movies, TV shows, and anime. It differentiates from Jellyfin and Plex with dynamic transcoding that supports instant seeking (no buffering while seeking through a video), video preview thumbnails on the timeline, and automatic metadata matching that doesn’t require specific folder structures. Built with a modern stack, it features Meilisearch-powered search and OIDC authentication. Official site.

Updated March 2026: Verified with latest Docker images and configurations.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 2 GB of free disk space for the application
  • 2 GB of RAM minimum (4 GB recommended for transcoding)
  • A TMDB API read access token (get one free)
  • A domain name (optional, for remote access)

Docker Compose Configuration

Create a directory and an .env file first:

mkdir -p ~/kyoo && cd ~/kyoo

Create a .env file with your configuration:

# Media library path on host
LIBRARY_ROOT=./video

# Cache for transcoded segments (auto-cleaned)
CACHE_ROOT=/tmp/kyoo_cache

# Downloaded metadata images
IMAGES_PATH=./images

# Ignore pattern for library scanning
LIBRARY_IGNORE_PATTERN=.*/[dD]ownloads?/.*

# Hardware acceleration: cpu, vaapi, qsv, or nvidia
COMPOSE_PROFILES=cpu

# Transcoding preset: ultrafast, superfast, veryfast, faster, fast, medium
GOCODER_PRESET=fast

# TMDB API token (required — get from themoviedb.org)
THEMOVIEDB_API_ACCESS_TOKEN=your-tmdb-read-access-token

# Public URL for OIDC redirects
PUBLIC_URL=http://localhost:8901

# PostgreSQL credentials
PGUSER=kyoo
PGPASSWORD=change-this-password
PGDATABASE=kyoo
PGHOST=postgres
PGPORT=5432

# Internal API keys (generate with: openssl rand -hex 32)
KEIBI_APIKEY_SCANNER=change-this-to-random-hex

Create the docker-compose.yml:

services:
  front:
    image: ghcr.io/zoriya/kyoo_front:4.7.1
    container_name: kyoo-front
    restart: unless-stopped
    depends_on:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.front.rule=PathPrefix(`/`)"
      - "traefik.http.routers.front.priority=1"
      - "traefik.http.services.front.loadbalancer.server.port=8901"

  auth:
    image: ghcr.io/zoriya/kyoo_auth:edge
    container_name: kyoo-auth
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - PGUSER=${PGUSER}
      - PGPASSWORD=${PGPASSWORD}
      - PGDATABASE=${PGDATABASE}
      - PGHOST=${PGHOST}
      - PGPORT=${PGPORT}
      - PUBLIC_URL=${PUBLIC_URL}
      - KEIBI_APIKEY_SCANNER=${KEIBI_APIKEY_SCANNER}
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.auth.rule=PathPrefix(`/api/auth`)"
      - "traefik.http.routers.auth.priority=3"
      - "traefik.http.services.auth.loadbalancer.server.port=4568"

  transcoder:
    image: ghcr.io/zoriya/kyoo_transcoder:4.7.1
    container_name: kyoo-transcoder
    restart: unless-stopped
    profiles: ["${COMPOSE_PROFILES:-cpu}"]
    volumes:
      - ${LIBRARY_ROOT:-./video}:/video:ro
      - ${CACHE_ROOT:-/tmp/kyoo_cache}:/cache
      - metadata:/metadata
    environment:
      - GOCODER_PREFIX=/video
      - GOCODER_CACHE_ROOT=/cache
      - GOCODER_METADATA_ROOT=/metadata
      - GOCODER_PRESET=${GOCODER_PRESET:-fast}
      - GOCODER_HWACCEL=${COMPOSE_PROFILES:-cpu}
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.transcoder.rule=PathPrefix(`/api/transcode`)"
      - "traefik.http.routers.transcoder.priority=3"
      - "traefik.http.services.transcoder.loadbalancer.server.port=7666"

  postgres:
    image: postgres:16-alpine
    container_name: kyoo-postgres
    restart: unless-stopped
    volumes:
      - db:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=${PGUSER}
      - POSTGRES_PASSWORD=${PGPASSWORD}
      - POSTGRES_DB=${PGDATABASE}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${PGUSER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  traefik:
    image: traefik:v3.6.11
    container_name: kyoo-traefik
    restart: unless-stopped
    ports:
      - "8901:8901"
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:8901"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

volumes:
  db:
  metadata:

Important: Change PGPASSWORD and KEIBI_APIKEY_SCANNER to strong random values. Set THEMOVIEDB_API_ACCESS_TOKEN to your TMDB read access token.

Start the stack:

docker compose up -d

Initial Setup

  1. Open http://your-server-ip:8901 in your browser
  2. Create the first user account — this account automatically receives admin permissions
  3. Place your media files in the LIBRARY_ROOT directory (default: ./video)
  4. Kyoo automatically scans and matches your media against TMDB metadata
  5. No specific folder structure is required — Kyoo parses filenames to identify content

Configuration

SettingDescription
COMPOSE_PROFILESHardware acceleration: cpu (default), vaapi (Intel/AMD GPU), qsv (Intel Quick Sync), nvidia
GOCODER_PRESETTranscoding speed vs quality tradeoff: ultrafast to medium
LIBRARY_IGNORE_PATTERNRegex pattern to exclude directories from scanning
PUBLIC_URLExternal URL for OIDC authentication redirects
THEMOVIEDB_API_ACCESS_TOKENRequired for metadata matching and artwork

Hardware Acceleration

For GPU-accelerated transcoding, change COMPOSE_PROFILES in your .env:

ProfileGPU TypeNotes
cpuNo GPUDefault, works everywhere
vaapiIntel/AMDMount /dev/dri device
qsvIntel Quick SyncMount /dev/dri device
nvidiaNVIDIARequires nvidia-container-toolkit

For vaapi or qsv, add to the transcoder service:

    devices:
      - /dev/dri:/dev/dri

Reverse Proxy

Kyoo includes Traefik as its internal reverse proxy. To place it behind your own reverse proxy, change the Traefik entrypoint port or remove Traefik and expose services directly.

See Reverse Proxy Setup for external proxy configuration.

Backup

Back up these volumes:

  • db — PostgreSQL database with all metadata, users, and watch history
  • metadata — transcoder metadata cache

Your media files in LIBRARY_ROOT should be backed up separately.

See Backup Strategy for automated approaches.

Troubleshooting

Media Not Being Detected

Symptom: Videos in the library directory don’t appear in Kyoo. Fix: Check that files aren’t matched by LIBRARY_IGNORE_PATTERN. Kyoo parses filenames — ensure they contain the show/movie name. Check scanner logs: docker compose logs auth.

Transcoding Errors or Buffering

Symptom: Video playback fails or buffers constantly. Fix: Try switching to GOCODER_PRESET=ultrafast for faster transcoding at the cost of file size. Verify the CACHE_ROOT directory has sufficient disk space. For hardware acceleration issues, check GPU driver installation and device mounting.

TMDB Metadata Not Loading

Symptom: Shows appear without artwork or descriptions. Fix: Verify your THEMOVIEDB_API_ACCESS_TOKEN is a read access token (not an API key — they’re different). Check network connectivity from the container: docker compose exec auth wget -qO- https://api.themoviedb.org/3/configuration.

Authentication Redirect Loops

Symptom: Login redirects back to the login page repeatedly. Fix: Ensure PUBLIC_URL matches the URL you’re accessing Kyoo from, including the protocol and port.

Resource Requirements

  • RAM: ~500 MB idle, 1-4 GB during transcoding (depends on resolution and codec)
  • CPU: Medium-High during transcoding, low when idle
  • Disk: ~200 MB for application, plus cache space for transcoded segments (auto-cleaned)

Verdict

Kyoo stands out from Jellyfin and Plex with genuinely innovative features: instant seeking during transcoding, video preview thumbnails, and filename-based matching that doesn’t demand a specific folder structure. The trade-off is maturity — Kyoo is newer and has a smaller community. The v5 auth service currently lacks stable tagged releases (using :edge), which means production stability isn’t guaranteed. Choose Kyoo if you value its transcoding features and don’t mind running pre-release software. Choose Jellyfin for a more mature, stable media server with a larger plugin ecosystem.

FAQ

How does Kyoo compare to Jellyfin?

Kyoo’s standout features are instant seeking during transcoding (no buffering when you skip ahead) and video preview thumbnails on the timeline. Jellyfin is more mature, has a larger plugin ecosystem, client apps for every platform, and a bigger community. Choose Kyoo if you value its transcoding innovations and don’t mind running pre-release software. Choose Jellyfin for stability and broader device support.

Does Kyoo require a specific folder structure?

No. Kyoo parses filenames to identify content and matches against TMDB metadata automatically. You don’t need Movie Name (Year)/Movie Name (Year).mkv folder structures like Jellyfin expects. Place your media files in the library root, and Kyoo figures out what they are. This is one of its key advantages.

What hardware acceleration does Kyoo support?

Kyoo supports CPU transcoding (works everywhere), VAAPI (Intel/AMD GPUs), Intel Quick Sync (QSV), and NVIDIA GPUs. Set COMPOSE_PROFILES in your .env file to match your hardware. For VAAPI and QSV, mount /dev/dri as a device. For NVIDIA, install the nvidia-container-toolkit first.

Is Kyoo production-ready?

Partially. The media browsing, transcoding, and metadata features work well. However, the v5 auth service (kyoo_auth) currently uses the :edge tag (no stable release), which means breaking changes can occur without warning. For a personal media server, it’s usable. For shared family use where reliability matters, Jellyfin is the safer choice.

Does Kyoo have mobile apps?

Kyoo’s web interface is responsive and works on mobile browsers. There are no dedicated native mobile apps like Jellyfin or Plex have. The web player handles video playback through the browser, including transcoding for formats the browser can’t play natively.

Why does Kyoo use Traefik internally?

Kyoo is a multi-service architecture (frontend, auth, transcoder) that needs internal routing. Traefik handles path-based routing between these services on a single port. This simplifies the Docker Compose setup — you expose one port (8901) and Traefik routes requests to the correct backend service based on URL path.

Comments