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
- Open
http://your-server-ip:8901in your browser - Create the first user account — this account automatically receives admin permissions
- Place your media files in the
LIBRARY_ROOTdirectory (default:./video) - Kyoo automatically scans and matches your media against TMDB metadata
- No specific folder structure is required — Kyoo parses filenames to identify content
Configuration
| Setting | Description |
|---|---|
COMPOSE_PROFILES | Hardware acceleration: cpu (default), vaapi (Intel/AMD GPU), qsv (Intel Quick Sync), nvidia |
GOCODER_PRESET | Transcoding speed vs quality tradeoff: ultrafast to medium |
LIBRARY_IGNORE_PATTERN | Regex pattern to exclude directories from scanning |
PUBLIC_URL | External URL for OIDC authentication redirects |
THEMOVIEDB_API_ACCESS_TOKEN | Required for metadata matching and artwork |
Hardware Acceleration
For GPU-accelerated transcoding, change COMPOSE_PROFILES in your .env:
| Profile | GPU Type | Notes |
|---|---|---|
cpu | No GPU | Default, works everywhere |
vaapi | Intel/AMD | Mount /dev/dri device |
qsv | Intel Quick Sync | Mount /dev/dri device |
nvidia | NVIDIA | Requires 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 historymetadata— 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.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments