# 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](/apps/jellyfin) and [Plex](/apps/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](https://github.com/zoriya/Kyoo).

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

## Prerequisites

- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed ([guide](/foundations/docker-compose-basics))
- 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](https://www.themoviedb.org/settings/api))
- A domain name (optional, for remote access)

## Docker Compose Configuration

Create a directory and an `.env` file first:

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

Create a `.env` file with your configuration:

```bash
# 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`:

```yaml
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.10
    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:

```bash
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

| 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:

```yaml
    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](/foundations/reverse-proxy-explained) 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](/foundations/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](/apps/jellyfin) and [Plex](/apps/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](/apps/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](/apps/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](/apps/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

- [Best Self-Hosted Media Servers](/best/media-servers)
- [Jellyfin vs Plex](/compare/jellyfin-vs-plex)
- [Jellyfin vs Emby vs Plex](/compare/jellyfin-vs-emby-vs-plex)
- [How to Self-Host Jellyfin](/apps/jellyfin)
- [How to Self-Host Plex](/apps/plex)
- [Self-Hosted Netflix Alternatives](/replace/netflix)
- [Docker Compose Basics](/foundations/docker-compose-basics)
- [Reverse Proxy Setup](/foundations/reverse-proxy-explained)
