SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-26

Jellyfin installeren op een VPS met Docker

Bouw uw eigen mediaserver met Jellyfin in Docker op een VPS. Leer hoe u bestandsrechten beheert, block storage koppelt en CPU-transcoding voorkomt voor een soepele streamingervaring.

Wat u gaat bouwen

Een Jellyfin-mediaserver op een VPS: één container, drie volumes en een block-storage-schijf voor uw films en series, bereikbaar vanuit elke browser of Jellyfin-app. De installatie bestaat uit een compose-bestand van vijftien regels. Alles wat daarna misgaat, komt voort uit twee oorzaken: bestandsrechten die de container niet kan lezen, en het verzoek aan een VPS zonder GPU om video te transcoderen waarvoor deze niet geschikt is. Deze handleiding besteedt het grootste deel van de tekst aan deze twee punten, omdat daar de meeste supportvragen vandaan komen.

Jellyfin is gratis en volledig open source, zonder account, betaalde functies of telemetrie; dit is de reden waarom het op bijna elke lijst staat van zaken die de moeite waard zijn om in 2026 zelf te hosten. Het speelt media af die u zelf bezit. Het bevat geen content en deze handleiding gaat niet over het verkrijgen daarvan.

De realiteit van transcoding, voordat u iets huurt

Lees dit eerst, want het bepaalt wat u moet aanschaffen. Een mediaserver doet een van de twee volgende dingen wanneer u op afspelen drukt. Direct play streamt het bestand zoals het is: de VPS leest bytes van de schijf en verstuurt deze over het netwerk, wat vrijwel geen CPU-kracht kost. Transcoding codeert de video on-the-fly opnieuw, met een nieuwe resolutie, een nieuwe codec of ingebrande ondertitels, en dat is puur CPU-werk.

Een standaard VPS heeft geen GPU. Elke transcode draait dus op de CPU met libx264/libx265, en software-encoding is kostbaar. Een enkele 1080p H.264-transcode kan meerdere gedeelde vCPU's volledig belasten; een 4K- of HEVC-transcode kan de real-time snelheid meestal niet bijhouden, waardoor het afspelen stopt en blijft bufferen. Hardware-transcoding, de techniek die dit goedkoop maakt op een thuisserver met een Intel iGPU of een Nvidia-kaart, is simpelweg niet beschikbaar tenzij uw provider GPU-instances aanbiedt.

De gehele strategie op een VPS is daarom: vermijd transcoding. Houd uw bibliotheek in codecs die uw clients native afspelen, zoals H.264-video, AAC- of AC3-audio, in een MP4- of MKV-container, en kies client-apps die direct-play ondersteunen: de officiële Jellyfin-apps voor Android TV, iOS en Roku, plus Infuse, Kodi en de desktop Jellyfin Media Player. Als u dat doet, raakt de VPS nooit ffmpeg aan en kan een bescheiden box met 2 vCPU's naar meerdere mensen tegelijk streamen. Als u van plan bent om te transcoderen, heeft u een veel grotere, duurdere box nodig, en zelfs dan is 4K een riskante keuze.

Doe ook de bandbreedteberekening, want dat is de andere verrassing. Bij direct afspelen wordt het bestand met de oorspronkelijke bitrate verzonden. Een gecomprimeerd 1080p-bestand gebruikt 8-12 Mbps; een 1080p Blu-ray-remux 20-30 Mbps; 4K HDR 40-80 Mbps. Als drie mensen bestanden van 10 Mbps rechtstreeks afspelen, komt dat neer op 30 Mbps aan constant uitgaand verkeer vanaf uw VPS. Controleer twee waarden in uw abonnement: de poortsnelheid (kan deze upstream 30 Mbps verwerken?) en de maandelijkse datalimiet. Een film van twee uur met 10 Mbps genereert ongeveer 9 GB aan uitgaand verkeer. Een abonnement met een limiet van 1 TB per maand is daarmee goed voor iets meer dan honderd van zulke films per maand, oftewel drie of vier per dag. Een huishouden dat 4K kijkt, met een vier- tot achtmaal hogere bitrate, verbruikt die limiet veel sneller. Tel al het overige verkeer vanaf dezelfde server mee in datzelfde budget, waaronder een zelfgehoste RustDesk-relay. Die relay draagt een volledige externe desktopsessie wanneer twee peers geen rechtstreekse verbinding kunnen maken.

Vereisten

  • Een nieuwe Ubuntu 24.04 KVM VPS met root- of sudo-toegang, inclusief Docker en de Compose-plugin geïnstalleerd.
  • Een block-storage volume voor de media, met een grootte die past bij uw bibliotheek (zie de dimensionering hieronder). De kleine root-schijf die bij een VPS wordt geleverd, is niet de plek waar uw films moeten staan.
  • Een domeinnaam als u publieke HTTPS-toegang wilt, of een WireGuard VPN op dezelfde VPS als u het geheel liever privé houdt.
  • Media die u legaal mag streamen, uw eigen rips, uw eigen opnames, bestanden waarvan u de eigenaar bent.

Koppel eerst de block storage

Koppel het volume in het configuratiescherm van uw provider, zoek het vervolgens op en monteer het. Achterhaal de apparaatnaam via lsblk; dit is doorgaans iets als /dev/sdb of /dev/vdb, maar nooit de root-schijf.

lsblk
sudo mkfs.ext4 /dev/sdb          # ONLY on a new, empty volume — this ERASES it
sudo mkdir -p /mnt/media
sudo blkid /dev/sdb              # copy the UUID shown for this device

Monteer het volume op basis van de UUID, niet via /dev/sdb. Apparaatletters kunnen na een herstart wijzigen, waardoor u per ongeluk de verkeerde schijf formatteert of koppelt. Voeg één regel toe aan /etc/fstab:

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /mnt/media  ext4  defaults,nofail  0  2
sudo mount -a
df -h /mnt/media

nofail is essentieel: zonder deze optie weigert de server op te starten en valt deze terug naar een emergency shell als het block volume wordt losgekoppeld. De meest gemaakte fout is het uitvoeren van mkfs.ext4 op een volume waar al data op staat; dit wist alle gegevens. Formatteer alleen nieuwe volumes; als de schijf al uw bestanden bevat, ga dan direct door naar de regel voor fstab.

De mediabestanden indelen zoals Jellyfin dit verwacht

Jellyfin koppelt metadata op basis van map- en bestandsnamen. Bij een onjuiste indeling verschijnen films als titelloze bestanden zonder poster, of worden afleveringen aan de verkeerde serie gekoppeld. Er zijn precies drie regels: elke film staat in een eigen Name (Year)-map met een overeenkomende bestandsnaam; seizoensmappen worden benoemd als Season 01, niet S01; afleveringsbestanden gebruiken S01E01; en specials worden in Season 00 geplaatst.

/mnt/media
├── Movies
│   ├── Blade Runner (1982)
│   │   └── Blade Runner (1982).mkv
│   └── Arrival (2016)
│       └── Arrival (2016).mkv
└── Shows
    └── Severance (2022)
        ├── Season 01
        │   ├── Severance - S01E01.mkv
        │   └── Severance - S01E02.mkv
        └── Season 00
            └── Severance - The Lexington Letter.mkv

Het (Year) bij films is geen decoratie, maar dient om remakes te onderscheiden zodat de matcher de juiste titel selecteert. Houd Movies en Shows als afzonderlijke mappen op het hoogste niveau, omdat elke map een Jellyfin-bibliotheek van een specifiek inhoudstype wordt; het mengen hiervan verstoort de metadata-provider. Jellyfin indexeert ook probleemloos een derde map met foto's, maar de functionaliteit is beperkt in vergelijking met een gespecialiseerde fotoserver. Als uw albums belangrijk zijn, kunt u deze beter onderbrengen op een eigen server met PhotoPrism of Immich en deze server reserveren voor films en tv-series.

Rechten: de voornaamste reden voor lege bibliotheken

Dit is het misverstand dat mensen vaak een hele avond kost. De officiële jellyfin/jellyfin-image respecteert de PUID/PGID-omgevingsvariabelen niet; deze horen bij de LinuxServer.io-image (lscr.io/linuxserver/jellyfin). Bij de officiële image beheert u de gebruiker met de user:-sleutel in compose, en als u deze weglaat, draait de container als root. Welke u ook gebruikt, de regel blijft gelijk: de uid/gid waaronder de container draait, moet elk mediapad kunnen lezen en doorlopen.

We draaien als uid/gid 1000, de eerste niet-rootgebruiker op een standaard Ubuntu-systeem. Bevestig uw eigen waarden en stel het eigenaarschap in:

id                                  # confirm your user is uid=1000 gid=1000
sudo chown -R 1000:1000 /mnt/media
sudo find /mnt/media -type d -exec chmod 755 {} \;
sudo find /mnt/media -type f -exec chmod 644 {} \;
mkdir -p ~/jellyfin/config ~/jellyfin/cache
sudo chown -R 1000:1000 ~/jellyfin

Mappen hebben de execute-bit nodig (de x in 755), niet alleen leesrechten. Zonder deze bit kan de container de map niet betreden, ook al kan de naam wel worden weergegeven. De valkuil die een hele bibliotheek leegmaakt, is de bovenliggende map: als de uid van de container de mount zelf niet kan doorlopen, bereikt deze nooit /media/Movies of /media/Shows, en verschijnt elke bibliotheek direct als leeg met Access to the path ... is denied in het logbestand. Elke individuele mediamap die niet gelezen kan worden, wordt gelogd en overgeslagen. Hierdoor verdwijnt een reeks bestanden die als root zijn gekopieerd geruisloos uit de bibliotheek. Daarom voeren we een recursieve chown uit en stellen we de execute-bit in op elke map, in plaats van slechts één map te herstellen.

Het docker-compose bestand

services:
  jellyfin:
    image: jellyfin/jellyfin:10
    container_name: jellyfin
    user: "1000:1000"
    restart: unless-stopped
    ports:
      - "127.0.0.1:8096:8096"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/media:/media:ro
    environment:
      - JELLYFIN_PublishedServerUrl=https://jellyfin.example.com

Regel voor regel: user: "1000:1000" stelt daadwerkelijk de bestandsrechten in, passend bij het bovenstaande eigenaarschap. /config bevat de volledige server, accounts, bibliotheken, metadata en de status van bekeken media; dit moet dus beschrijfbaar zijn en dit is de map die u moet back-uppen. /cache is tijdelijke werkruimte. De media-mount is :ro (alleen-lezen) met een reden: Jellyfin slaat standaard artwork en metadata op onder /config, waardoor het nooit naar uw bibliotheek hoeft te schrijven. De alleen-lezen instelling beschermt uw bestanden tegen per ongeluk verwijderen of een defecte plugin. De poort is bewust gebonden aan 127.0.0.1; de web-login van Jellyfin is standaard HTTP, dus we stellen 8096 nooit bloot aan het openbare internet. JELLYFIN_PublishedServerUrl is het adres dat de server adverteert voor lokale automatische detectie, een LAN UDP-broadcast. Clients via het internet zien dit nooit en gebruiken simpelweg de URL die u in de app invoert. Stel dit in op het adres dat aan clients moet worden doorgegeven en houd er rekening mee dat u op externe apparaten die URL handmatig moet invoeren.

Start de service vanuit de compose-directory:

docker compose up -d
docker logs -f jellyfin

Eerste run: de installatiewizard en uw bibliotheken

Omdat de poort is gebonden aan localhost, bereikt u de wizard via een SSH-tunnel vanaf uw laptop in plaats van een gat in de firewall te openen:

ssh -L 8096:127.0.0.1:8096 you@your-vps-ip

Navigeer nu naar http://localhost:8096. De wizard leidt u door de taalinstellingen en vervolgens door het aanmaken van een admin user met een sterk wachtwoord. Dit account is uw server, dus gebruik geen tijdelijk of hergebruikt wachtwoord. Voeg uw eerste bibliotheek toe: kies het inhoudstype Movies, verwijs naar /media/Movies (het pad binnen de container, niet het hostpad), en herhaal dit met Shows op /media/Shows. Voltooi de stappen en Jellyfin start de scan. Een correct resultaat is dat posters en titels binnen een minuut of twee verschijnen voor een kleine bibliotheek. Voeg later bibliotheken toe of bewerk ze via Dashboard → Libraries en forceer een nieuwe scan met Scan All Libraries.

Als u afhankelijk bent van transcoding, open dan Dashboard → Playback → Transcoding en stel het tijdelijke pad voor transcodering in op /cache/transcodes, zodat de tijdelijke bestanden op het cache-volume terechtkomen in plaats van /config te vervuilen. Laat hardware-acceleratie op None staan; er is geen GPU beschikbaar voor versnelling.

Toegang op afstand: TLS reverse proxy of via VPN

Er zijn twee veilige manieren om Jellyfin van buitenaf te bereiken en één onveilige methode die u moet vermijden. De onveilige methode is het direct openstellen van poort 8096 voor het internet: de inloggegevens worden in leesbare tekst verzonden en de poort wordt binnen enkele uren onderworpen aan brute-force-aanvallen.

Optie A, TLS reverse proxy. Plaats Jellyfin op een subdomein achter Traefik met automatische TLS voor uw Docker-applicaties, of achter nginx met een Let's Encrypt-certificaat uitgegeven door Certbot. Jellyfin gebruikt WebSockets voor real-time updates, dus de proxy moet de upgrade-headers doorsturen. Traefik doet dit automatisch; nginx vereist dat deze expliciet worden gedefinieerd en heeft HTTP/1.1 nodig naar de upstream, anders vindt de upgrade nooit plaats:

location / {
    proxy_pass http://127.0.0.1:8096;
    proxy_http_version 1.1;
    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;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Stel JELLYFIN_PublishedServerUrl in op het https://-adres zodat lokale autodiscovery de juiste URL adverteert, externe apps het adres gebruiken dat u opgeeft, en voeg fail2ban toe om brute-force-pogingen op de inlogpagina te vertragen. Zodra de server publiek toegankelijk is, kunt u Uptime Kuma naar de URL laten wijzen, zodat u op de hoogte bent van downtime voordat uw gebruikers dat zijn.

Optie B, houd het privé via een VPN. Stel poort 8096 in het geheel niet open; bereik Jellyfin uitsluitend via een WireGuard-tunnel die op dezelfde machine eindigt. Voor een huishouden is dit de eenvoudigste veilige keuze: geen certificaat, geen publieke blootstelling en geen aanvalsoppervlak voor brute-force. Koppel de container aan het tunneladres of localhost en maak verbinding via de VPN. Zie de WireGuard VPN-configuratie voor een privé VPS voor het opzetten van de tunnel zelf.

Opslagcapaciteit en back-ups

Baseer uw budget op kwaliteit, niet op het aantal bestanden. Gecomprimeerde 1080p-films nemen elk 4-15 GB in beslag; een 1080p-remux 20-40 GB; een seizoen van een 1080p-serie 15-40 GB; alles in 4K is 40-100 GB per film. Een bibliotheek van enkele honderden films plus wat series vereist een volume van 2-4 TB. Het is goedkoper om het blokvolume eenmalig ruim te dimensioneren dan later te moeten migreren.

/config bevat de volledige serverstatus; dit is het enige onderdeel waarvan u een back-up moet maken. Maak een snapshot of stop de container en maak een tar-archief, en bewaar de kopie buiten de server:

docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d

/cache en de transcode-map zijn vervangbaar. De media op /mnt/media back-upt u afzonderlijk of u accepteert dat deze opnieuw geript kunnen worden; de meeste gebruikers kiezen voor het laatste vanwege de omvang. Upgrades verlopen via docker compose pull && docker compose up -d; de :10-tag hierboven blijft binnen de 10.x-majorversie. Overstappen naar de volgende majorversie is een bewuste wijziging van de tag. Lees de Jellyfin-release notes door voordat u dit doet, aangezien er bij majorversies vaak migraties van het bibliotheekschema plaatsvinden. Een vastgezette tag plus één back-up van de statusdirectory vormt het volledige recept voor elke altijd actieve container. Dit is hetzelfde patroon als bij het behouden van het geheugen en de planningen van een self-hosted agent na een herstart.

Foutmodi en de bijbehorende meldingen

Bibliotheek is leeg na een scan. Het logboek onder Dashboard → Logs (of ~/jellyfin/config/log/log_*.log) toont:

System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.

De uid van de container kan het pad niet lezen. Oorzaak: media is eigendom van root of een andere uid dan uw user:-waarde, een map mist het execute-bit, of het koppelpunt zelf is niet toegankelijk voor die uid. Oplossing: chown -R 1000:1000 /mnt/media, mappen 755, bestanden 644, en voer daarna een nieuwe scan uit.

Afspelen belast de CPU volledig en buffert. docker stats jellyfin toont een CPU-gebruik van bijna 100% vermenigvuldigd met het aantal cores, en Dashboard → Playback vermeldt de sessie als Transcode met een snelheid lager dan 1.0x. De client maakt geen gebruik van direct-play, waardoor de VPS trager dan real-time transcodeert en achterblijft. Oorzaak: een niet-ondersteunde codec of container, het inbranden van ondertitels, of HDR tone-mapping. Oplossing: schakel over naar een client die direct-play ondersteunt, bewaar bronbestanden in H.264/AAC, gebruik tekst-ondertitels (SRT) in plaats van afbeeldings-ondertitels (PGS/VOBSUB) die een inbrand-proces forceren, en vermijd 4K HDR volledig op een server zonder hardware-transcodering.

"No compatible streams are available." De volledige melding luidt meestal "This client isn't compatible with the media and the server isn't sending a compatible media format." De client wees de bron af en de fallback-transcodering kon eveneens niet starten. Oorzaak: een defect ffmpeg-commando, een onleesbaar bestand, of het gebruikersprofiel blokkeert videoconversie. Oplossing: lees de ffmpeg-regel in Dashboard → Logs, controleer of het bestand überhaupt afspeelbaar is, controleer de afspeelrechten van de gebruiker als u afhankelijk bent van transcodering, en probeer een tweede client om browser-specifieke codec-problemen uit te sluiten.

Films hebben geen of de verkeerde poster. De metadata kwam niet overeen. Oorzaak: een film staat niet in een eigen Name (Year)-map, een seizoensmap is benoemd als S01 in plaats van Season 01, afleveringen staan niet in de vorm S01E01, of het jaartal ontbreekt. Oplossing: hernoem naar de bovenstaande structuur en kies daarna Refresh metadata → Replace all, of gebruik Identify op een enkel item om de juiste TMDB/TVDB-vermelding te koppelen.

FAQ

Kan een VPS video transcoderen zonder GPU?

Ja, maar dit gebeurt uitsluitend via de CPU en is rekenintensief. Eén 1080p software-transcode kan meerdere vCPU's volledig belasten. Bij 4K of HEVC is real-time verwerking vaak niet haalbaar, wat leidt tot bufferen tijdens het afspelen. De beste aanpak is om transcodering te vermijden: bewaar uw bibliotheek in H.264/AAC en gebruik client-applicaties die direct-play ondersteunen, zodat de VPS enkel bytes streamt. Huur alleen een GPU-instantie als u daadwerkelijk on-the-fly transcodering nodig heeft.

Waarom is mijn Jellyfin-bibliotheek leeg na een scan?

Dit wordt bijna altijd veroorzaakt door toegangsrechten. De officiële jellyfin/jellyfin-image draait als de user: die u instelt (of root). Als de bestanden niet leesbaar zijn voor die uid, logt de scan Access to the path ... is denied en slaat deze bestanden over. Herstel het eigenaarschap met chown -R 1000:1000 /mnt/media, geef mappen het execute-bit (755) en voer de scan opnieuw uit. Controleer ook de bovenliggende mappen; als de uid van de container /mnt/media zelf niet kan doorlopen, bereikt deze nooit de bibliotheekmappen en blijft alles leeg. De op één na meest voorkomende oorzaak is een mappenstructuur die niet overeenkomt met wat Jellyfin verwacht.

Hoe krijg ik veilig op afstand toegang tot Jellyfin?

Er zijn twee goede opties. Plaats de service achter een TLS-reverse proxy op een subdomein zodat de inloggegevens en de stream versleuteld zijn, en voeg fail2ban toe. Stel nooit poort 8096 direct bloot aan het internet, aangezien wachtwoorden dan in leesbare tekst worden verzonden. Of houd de service volledig privé en benader deze enkel via een VPN; dit is de eenvoudigste veilige keuze voor thuisgebruik. Voer het publieke adres direct in de apps in, aangezien autodiscovery een lokaal netwerk-broadcast is die clients via het internet niet bereikt.

Hoeveel schijfruimte en bandbreedte heeft een Jellyfin-VPS nodig?

De benodigde schijfruimte hangt af van de kwaliteit: reken op 4-15 GB per gecomprimeerde 1080p-film, 20-40 GB per remux en 40-100 GB voor 4K. De meeste bibliotheken vereisen daarom een block volume van 2-4 TB. De bandbreedte wordt bepaald door de direct-play bitrate: 8-12 Mbps per 1080p-stream en aanzienlijk meer voor 4K. Controleer of uw poortsnelheid het aantal gelijktijdige kijkers aankan en houd rekening met de maandelijkse datalimiet. Zorg voor extra CPU-capaciteit als u van plan bent te transcoderen; geef prioriteit aan bandbreedte boven CPU-cores als u direct-play gebruikt.

Is het legaal om Jellyfin op een VPS te draaien?

Jellyfin zelf is vrije, open-source software en het gebruik ervan is volledig legaal. Waar het om gaat is de inhoud: stream alleen media die u bezit of waarvoor u een licentie heeft, zoals uw eigen disc-rips, opnames of bestanden waarvoor u de rechten heeft. Jellyfin levert geen media mee en biedt geen middelen om deze te verkrijgen; het is een speler voor een bibliotheek die u reeds in uw bezit heeft.