Jellyfin op een VPS installeren met Docker
Leer Jellyfin in Docker op een VPS draaien. Wij bespreken block-storage, media permissions en de impact van CPU transcoding zonder GPU op uw server.
Wat u bouwt
Een Jellyfin media server op een VPS: één container, drie volumes en een block-storage disk met uw films en series, bereikbaar via elke browser of Jellyfin app. De installatie bestaat uit een compose file van vijftien regels. Eventuele problemen die daarna ontstaan, hebben meestal twee oorzaken: bestandsrechten die de container niet kan lezen, of een VPS zonder GPU die video probeert te transcoderen. Deze gids richt zich voornamelijk op deze twee punten, omdat hier de meeste vragen over ontstaan.
Jellyfin is gratis en volledig open source, zonder accounts, zonder betaalde functies en zonder telemetrie — de reden dat het op bijna elke lijst staat van dingen die het waard zijn om zelf te hosten in 2026. Het speelt media af die u bezit. Het bevat geen eigen content, en deze gids gaat niet over het verkrijgen van content.
De realiteit van transcoding, voordat u iets huurt
Lees dit eerst, omdat dit bepaalt wat u koopt. Een mediaserver voert een van de volgende twee acties uit wanneer u op play drukt. Direct play streamt het bestand zoals het is: de VPS leest bytes van de schijf en stuurt deze door het netwerk; dit kost bijna geen CPU. Transcoding hercodeert de video tijdens het afspelen — voor een nieuwe resolutie, een nieuwe codec, of ingebakken ondertiteling — en dit is puur CPU-werk.
Een typische VPS heeft geen GPU. Elke transcode draait daarom op de CPU met libx264/libx265, en softwarematige codering is zwaar voor de processor. Eén enkele 1080p H.264 transcode kan meerdere gedeelde vCPUs volledig belasten; een 4K of HEVC transcode kan de real-time verwerking meestal niet bijhouden, waardoor de weergave stopt en blijft bufferen. Hardware transcoding — de methode die dit goedkoop maakt op een thuistoestel met een Intel iGPU of een Nvidia-kaart — is niet beschikbaar, tenzij uw provider GPU-instances verhuurt.
De strategie voor een VPS is daarom: vermijd transcoding. Bewaar uw bibliotheek in codecs die uw clients native kunnen afspelen — H.264 video, AAC of AC3 audio, in een MP4 of MKV container — en kies client-apps die direct-play ondersteunen: de native Jellyfin apps voor Android TV, iOS en Roku, plus Infuse, Kodi, en de desktop Jellyfin Media Player. Als u dit doet, gebruikt de VPS nooit ffmpeg, en kan een bescheiden systeem met 2 vCPU tegelijkertijd streamen naar meerdere personen. Als u plant om te transcoderen, heeft u een veel grotere en duurdere machine nodig, en zelfs dan is 4K een onzekere keuze.
Bereken ook de bandbreedte, want dat is de andere verrassing. Direct play verzendt het bestand op de eigen bitrate. Een gecomprimeerd 1080p bestand gebruikt 8-12 Mbps; een 1080p Blu-ray remux gebruikt 20-30 Mbps; 4K HDR gebruikt 40-80 Mbps. Drie personen die 10 Mbps bestanden via direct-play bekijken, vereisen een constante upload van 30 Mbps vanuit uw VPS. Controleer twee gegevens in uw abonnement: de poetsnelheid (kan deze 30 Mbps upstream versturen?) en de maandelijkse datalimiet. Eén film van twee uur met 10 Mbps verbruikt ongeveer 9 GB aan data; een limiet van 1 TB per maand is dus iets meer dan honderd van dit soort films per maand — drie of vier per dag. Een huishouden dat 4K bekijkt, met een bitrate die vier tot acht keer hoger ligt, verbruikt dit limiet veel sneller.
Vereisten
- Een nieuwe Ubuntu 24.04 KVM VPS met root- of sudo-rechten, en Docker met de Compose plugin geïnstalleerd.
- Een block-storage volume voor de media, met een grootte die past bij uw bibliotheek (zie de details hieronder). Gebruik niet de kleine root-disk die standaard bij een VPS wordt geleverd voor uw films.
- Een domeinnaam voor publieke HTTPS-toegang, of een WireGuard VPN op dezelfde VPS als u alles privé wilt houden.
- Media waarvoor u wettelijk de rechten heeft om te streamen — uw eigen rips, uw eigen opnames, of bestanden die u bezit.
Mount eerst de block storage
Koppel het volume via het paneel van uw provider, zoek het op en monteer het. Haal de device naam op via lsblk — dit is iets als /dev/sdb of /dev/vdb, nooit de root disk.
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 deviceMonteer het via UUID, niet via /dev/sdb. Device letters kunnen bij een reboot veranderen. Dit kan ertoe leiden dat u de verkeerde disk formatteert of monteert. Voeg één regel toe aan /etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail is van belang: zonder deze instelling kan de server niet opstarten als het block volume wordt losgekoppeld. De server komt dan in een emergency shell terecht. De grootste 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 disk uw library al bevat, sla de formatteerstap dan over en ga direct naar de fstab-regel.
Richt de media in zoals Jellyfin dit verwacht
Jellyfin koppelt metadata aan de namen van mappen en bestanden. Een incorrecte mappenstructuur zorgt ervoor dat films als naamloze bestanden zonder poster verschijnen, of dat een aflevering aan de verkeerde serie wordt gekoppeld. Er gelden precies drie regels: elke film staat in een eigen Name (Year)-map met een bijpassende bestandsnaam; seizoensmappen heten Season 01 en niet S01; afleveringsbestanden gebruiken S01E01; en specials staan in Season 00.
/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.mkvDe (Year) op films is niet decoratief — het dient om remakes te onderscheiden, zodat de matcher de juiste titel vindt. Houd Movies en Shows als aparte mappen op het hoogste niveau, omdat elke map een Jellyfin-bibliotheek van een specifiek type content wordt. Het mengen van deze mappen zorgt voor fouten bij de metadata-provider.
Rechten: de belangrijkste reden waarom bibliotheken leeg blijven
Dit is de misvatting die gebruikers veel tijd kost. De officiële jellyfin/jellyfin image houdt geen rekening met de PUID/PGID omgevingsvariabelen — deze horen bij de LinuxServer.io image (lscr.io/linuxserver/jellyfin). Bij de officiële image beheert u de gebruiker met de user: key in compose. Als u deze weglaat, draait de container als root. Ongeacht welke methode u gebruikt, de regel blijft gelijk: de uid/gid waarmee de container draait, moet alle mediadirectories kunnen lezen en doorlopen.
We draaien als uid/gid 1000, de eerste niet-root gebruiker op een standaard Ubuntu-systeem. Controleer uw eigen gegevens en stel de eigendom 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 ~/jellyfinDirectories hebben het execute bit nodig (het x in 755), niet alleen read. Zonder dit bit kan de container de map niet betreden, ook al kan de mapnaam wel worden opgegeven. De fout die een volledige bibliotheek leegmaakt, zit in de parent-map: als de uid van de container de mount zelf niet kan doorlopen, bereikt deze nooit /media/Movies of /media/Shows. Hierdoor blijft de bibliotheek direct leeg en verschijnt er Access to the path ... is denied in het logbestand. Elke mediamap die niet gelezen kan worden, wordt gelogd en overgeslagen. Hierdoor verdwijnt een groep bestanden die als root is gekopieerd geruisloos uit de bibliotheek. Daarom gebruiken we chown recursief en stellen we het execute bit in op elke directory, in plaats van slechts één map te repareren.
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.comReg voor reg: user: "1000:1000" bepaalt de bestandsrechten en komt overeen met de eigenaar hierboven. /config bevat de volledige server — accounts, bibliotheken, metadata en de watch-status — dus dit moet beschrijfbaar zijn; dit is wat u backupt. /cache is tijdelijke werkruimte. De media-mount is met opzet :ro (alleen-lezen): Jellyfin slaat standaard artwork en metadata op onder /config, waardoor er nooit naar uw bibliotheek geschreven hoeft te worden. Alleen-lezen 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 gebruikt HTTP, dus we publiceren poort 8096 nooit op het publieke internet. JELLYFIN_PublishedServerUrl is het adres dat de server gebruikt voor lokale autodiscovery via een LAN UDP-broadcast; hierdoor zien clients op het internet dit niet en gebruiken zij simpelweg de URL die u in de app typt. Stel dit in op het adres dat de clients moeten gebruiken; houd er rekening mee dat u deze URL handmatig moet invoeren op externe apparaten.
Start de container vanuit de compose-map:
docker compose up -d
docker logs -f jellyfinEerste run: de setup wizard en uw bibliotheken
Omdat de poort aan localhost is gekoppeld, bereikt u de wizard via een SSH-tunnel vanaf uw laptop in plaats van een poort in de firewall te openen:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipNavigeer nu naar http://localhost:8096. De wizard begeleidt u bij de taalinstellingen en het aanmaken van een admin user met een sterk wachtwoord — dit account is uw server, gebruik daarom geen tijdelijk wachtwoord. Voeg uw eerste bibliotheek toe: kies content type Movies, wijs dit toe aan /media/Movies (het pad binnen de container, niet het pad op de host), en herhaal dit voor Shows op /media/Shows. Voltooi de wizard en Jellyfin start de scan. Bij een kleine bibliotheek zijn posters en titels binnen een of twee minuten zichtbaar. Voeg bibliotheken later toe of wijzig deze via Dashboard → Libraries, en dwing een nieuwe scan af met Scan All Libraries.
Als u gebruikmaakt van transcoding, open dan Dashboard → Playback → Transcoding en stel het transcode temp path in op /cache/transcodes. Hierdoor wordt de tijdelijke data op het cache volume opgeslagen in plaats van dat /config volloopt. Laat hardware acceleration op None staan — er is geen GPU beschikbaar voor versnelling.
Remote access: TLS reverse proxy, of gebruik een VPN
U heeft twee veilige manieren om Jellyfin van buitenaf te bereiken. Eén methode is onveilig en moet worden vermeden. De onveilige methode is het direct publiceren van port 8096 op het internet: inloggegevens worden in cleartext verzonden en de port wordt binnen enkele uren onderworpen aan brute-force aanvallen.
Optie A — TLS reverse proxy. Plaats Jellyfin op een subdomain achter Traefik met automatische TLS voor uw Docker apps, 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 expliciete configuratie en heeft HTTP/1.1 naar de upstream nodig, anders vindt de upgrade niet 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 gebruiken het adres dat u hen opgeeft — en voeg fail2ban toe om brute-force pogingen te vertragen tegen het inlogproces toe. Zodra de server publiek is, kunt u Uptime Kuma richten op de URL, zodat u op de hoogte bent van downtime voordat uw kijkers dat zijn.
Optie B — houd het privé via een VPN. Publiceer port 8096 niet. 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 tunnel-adres of localhost en maak verbinding via de VPN. Raadpleeg de WireGuard VPN setup voor een private VPS voor de configuratie van de tunnel zelf.
Opslagcapaciteit en backups
Maak een budget op basis van kwaliteit, niet op basis van het aantal bestanden. Gecomprimeerde 1080p films zijn 4-15 GB per stuk; een 1080p remux is 20-40 GB; een seizoen van 1080p TV is 15-40 GB; alles in 4K is 40-100 GB per film. Een bibliotheek van enkele honderden films plus enkele series vereist een volume van 2-4 TB. Het is goedkoper om het block volume direct ruim in te plannen dan om later te migreren.
/config bevat de volledige serverstatus, dus dit is het enige onderdeel dat u moet backuppen. Maak een snapshot of gebruik een stop-and-tar methode 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 folder zijn vervangbaar. De media op /mnt/media backupt u apart of u accepteert dat deze opnieuw geript moeten worden — de meeste gebruikers kiezen voor het laatste vanwege de omvang. Upgrades zijn docker compose pull && docker compose up -d; de :10 tag hierboven blijft binnen de 10.x major versie. Een overstap naar de volgende major versie vereist een bewuste aanpassing van de tag. Lees de Jellyfin release notes voordat u dit doet, omdat schema-migraties van de bibliotheek plaatsvinden bij major versies.
Foutmodi, met de tekst die u zult zien
De bibliotheek is leeg na een scan. Het logboek bij 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 dit pad niet lezen. Oorzaak: media is eigendom van root of een uid die afwijkt van uw user: waarde, een directory mist het execute-bit, of de parent mount is niet toegankelijk voor die uid. Oplossing: chown -R 1000:1000 /mnt/media, directories 755, files 644, en daarna opnieuw scannen.
Playback belast de CPU maximaal en buffert. docker stats jellyfin laat een CPU-belasting zien 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 gebruikt geen direct-play, waardoor de VPS CPU-transcodeert op een snelheid lager dan real-time en de stream verliest. Oorzaak: een niet-ondersteunde codec of container, subtitle burn-in, of HDR tone-mapping. Oplossing: schakel over naar een direct-play client, houd bronbestanden in H.264/AAC, gebruik text subtitles (SRT) in plaats van image subtitles (PGS/VOBSUB) omdat deze burn-in forceren, en gebruik geen 4K HDR op een systeem dat alleen een CPU heeft.
"No compatible streams are available." De volledige melding is meestal "This client isn't compatible with the media and the server isn't sending a compatible media format." De client heeft de bron geweigerd en de fallback transcode kon ook niet starten. Oorzaak: een defect ffmpeg-commando, een onleesbaar bestand, of een gebruikersprofiel dat videoconversie blokkeert. Oplossing: lees de ffmpeg-regel in Dashboard → Logs, controleer of het bestand überhaupt afspeelt, controleer de playback-rechten van de gebruiker als u afhankelijk bent van transcoding, en probeer een andere client om browser-codecproblemen uit te sluiten.
Films hebben geen poster of de verkeerde poster. Metadata kwam niet overeen. Oorzaak: een film staat niet in de eigen Name (Year) folder, een season folder is S01 genoemd in plaats van Season 01, episodes staan niet in S01E01 vorm, of een ontbrekend jaartal. Oplossing: hernoem naar de bovenstaande lay-out, gebruik 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 op de CPU en dit is kostbaar. Eén enkele 1080p software-transcode kan meerdere vCPUs verzadigen. 4K of HEVC kan meestal niet in real-time verwerken, waardoor de playback buffert. De beste oplossing is om transcoderen te vermijden: houd uw bibliotheek in H.264/AAC en gebruik client-apps die direct-play ondersteunen. De VPS streamt dan alleen bytes. Huur alleen een GPU-instance als u daadwerkelijk on-the-fly transcoding nodig heeft.
Waarom is mijn Jellyfin-bibliotheek leeg na een scan?
Meestal ligt dit aan de permissies. De officiële jellyfin/jellyfin image draait onder de door u ingestelde user: (of root). Als de bestanden niet leesbaar zijn voor die uid, slaat de scan de bestanden over in de Access to the path ... is denied logs. Herstel de eigendom met chown -R 1000:1000 /mnt/media, geef mappen het execute-bit (755) en scan opnieuw. Controleer ook de bovenliggende mappen; als de uid van de container /mnt/media zelf niet kan doorkruisen, bereikt deze de bibliotheekmappen nooit en blijft alles leeg. De tweede veelvoorkomende oorzaak is een mappenstructuur die niet voldoet aan de verwachtingen van Jellyfin.
Hoe krijg ik op een veilige manier op afstand toegang tot Jellyfin?
Er zijn twee goede opties. Gebruik een TLS reverse proxy op een subdomain zodat de login en de stream versleuteld zijn, en voeg fail2ban toe. Gebruik nooit poort 8096 zonder versleuteling, omdat uw wachtwoord dan in cleartext wordt verzonden. De andere optie is om het volledig privé te houden en alleen via een VPN toegang te krijgen; dit is de eenvoudigste veilige keuze voor een huishouden. Geef de apps direct het publieke adres; autodiscovery is een broadcast op het lokale netwerk en werkt niet voor clients die via het internet binnenkomen.
Hoeveel schijfruimte en bandbreedte heeft een Jellyfin VPS nodig?
De 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 bitrate van direct-play: 8-12 Mbps per 1080p stream, en veel meer voor 4K. Controleer of uw poetsnelheid voldoende is voor het aantal gelijktijdige kijkers en let op de maandelijkse transferlimiet. Voeg extra CPU-capaciteit toe als u van plan bent te transcoderen; geef prioriteit aan bandbreedte boven cores als u direct-play gebruikt.
Is het legaal om Jellyfin op een VPS te draaien?
Jellyfin zelf is gratis open-source software en het draaien ervan is volledig legaal. De inhoud is bepalend: stream alleen media die u bezit of waarvoor u een licentie heeft, zoals eigen disc-rips, opnames of bestanden waar u het recht op heeft. Jellyfin levert geen media en biedt geen manier om media te verkrijgen; het is een speler voor een bibliotheek die u al bezit.