SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

Installare Jellyfin su VPS con Docker

Guida all'uso di Jellyfin su VPS tramite Docker. Scopri come gestire i permessi dei file e ottimizzare il transcoding video senza l'uso di una GPU dedicata.

Cosa stai costruendo

Un media server Jellyfin su un VPS: un container, tre volumi e un disco di block-storage per i tuoi film e le tue serie TV, accessibile da qualsiasi browser o app Jellyfin. L'installazione consiste in un file compose di quindici righe. Eventuali problemi successivi derivano da due cause: permessi dei file non leggibili dal container e richieste di transcoding video a un VPS senza GPU. Questa guida si concentra principalmente su questi due aspetti, poiché sono le cause più comuni di errore.

Jellyfin è gratuito e completamente open source, senza account, senza funzioni a pagamento e senza telemetria; per questo motivo compare in quasi ogni lista di cose che vale la pena self-hostare nel 2026. Riproduce i media di tua proprietà. Non include contenuti precaricati e questa guida non serve per acquistarli.

La realtà del transcoding, prima di noleggiare qualsiasi cosa

Leggi prima questo testo, perché cambia il tipo di acquisto da effettuare. Un media server esegue una di due operazioni quando premi play. Il Direct play trasmette il file così com'è: il VPS legge i byte dal disco e li invia sulla rete, consumando quasi zero CPU. Il Transcoding ricodifica il video al volo — con nuova risoluzione, nuovo codec o sottotitoli incorporati — e questo richiede esclusivamente potenza della CPU.

Un tipico VPS non ha una GPU. Di conseguenza, ogni transcodifica avviene sulla CPU con libx264/libx265, e la codifica software è costosa. Una singola transcodifica H.264 a 1080p può saturare diversi vCPU condivisi; una transcodifica 4K o HEVC solitamente non riesce a mantenere il tempo reale, causando interruzioni e buffering continui. Il transcoding hardware — che rende questo processo economico su un computer domestico con una iGPU Intel o una scheda Nvidia — non è disponibile a meno che il provider non offra istanze con GPU in affitto.

La strategia su un VPS è quindi: evitare il transcoding. Mantieni la tua libreria con codec supportati nativamente dai client — video H.264, audio AAC o AC3, in un contenitore MP4 o MKV — e scegli app client che supportino il direct-play: le app native Jellyfin per Android TV, iOS e Roku, oltre a Infuse, Kodi e il Jellyfin Media Player per desktop. Se procedi così, il VPS non utilizzerà mai ffmpeg e un server con soli 2 vCPU potrà trasmettere a più persone contemporaneamente. Se prevedi di fare transcoding, avrai bisogno di un server molto più grande e costoso, e anche in quel caso il 4K è una scelta rischiosa.

Calcola anche il consumo di banda, poiché è l'altra sorpresa. Il direct play invia il file al suo bitrate originale. Un file 1080p compresso richiede 8-12 Mbps; un remux Blu-ray 1080p richiede 20-30 Mbps; un 4K HDR richiede 40-80 Mbps. Tre utenti che utilizzano il direct play con file da 10 Mbps generano un upload sostenuto di 30 Mbps dal tuo VPS. Controlla due parametri nel tuo piano: la velocità della porta (può gestire 30 Mbps in upload?) e il limite di trasferimento mensile. Un film da 10 Mbps della durata di due ore consuma circa 9 GB in uscita; quindi, un limite di 1 TB/mese permette di trasmettere poco più di cento film simili al mese — tre o quattro al giorno — mentre una famiglia che guarda contenuti 4K, con un bitrate da quattro a otto volte superiore, esaurirà la quota molto più velocemente.

Prerequisites

  • Un VPS Ubuntu 24.04 KVM appena installato con privilegi root o sudo, Docker e il plugin Compose installati.
  • Un volume di block-storage per i media, dimensionato in base alla libreria (vedere le indicazioni sotto). I file video non vanno salvati sul disco root limitato fornito con il VPS.
  • Un nome di dominio per l'accesso HTTPS pubblico, oppure un WireGuard VPN sullo stesso VPS per mantenere l'intero sistema privato.
  • Media per i quali si possiede il diritto legale di streaming — i propri rip, le proprie registrazioni o file di proprietà.

Montare prima lo storage a blocchi

Collega il volume tramite il pannello del provider, individualo e montalo. Recupera il nome del dispositivo da lsblk — sarà simile a /dev/sdb o /dev/vdb, mai il disco root.

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

Montalo tramite UUID, non tramite /dev/sdb, poiché i nomi dei dispositivi cambiano dopo il riavvio e si rischia di formattare o montare il disco errato. Aggiungi una riga a /etc/fstab:

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

nofail è fondamentale: senza di esso, se il volume a blocchi viene scollegato, il sistema non si avvia e si apre una shell di emergenza. L'errore più comune è eseguire mkfs.ext4 su un volume che contiene già dei dati — questo li cancella. Formatta solo i nuovi volumi; se il disco contiene già la tua libreria, passa direttamente alla riga fstab.

Organizza i media secondo le specifiche di Jellyfin

Jellyfin associa i metadati tramite i nomi delle cartelle e dei file. Se la struttura è errata, i film appariranno senza titolo e senza locandina, oppure un episodio potrebbe essere associato alla serie sbagliata. Esistono esattamente tre regole: ogni film deve trovarsi in una cartella Name (Year) dedicata con un nome file corrispondente; le cartelle delle stagioni devono chiamarsi Season 01 e non S01; i file degli episodi devono usare S01E01; i contenuti speciali vanno 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.mkv

Il (Year) sui film non è un elemento estetico: serve a distinguere i remake in modo che il sistema selezioni il titolo corretto. Mantieni Movies e Shows come cartelle separate di primo livello; ogni cartella diventerà una libreria Jellyfin di un tipo di contenuto specifico. Mescolare i contenuti confonde il provider dei metadati.

Permissions: la causa principale per cui le librerie risultano vuote

Ecco l'errore che fa perdere intere serate agli utenti. L'immagine ufficiale jellyfin/jellyfin non supporta le variabili d'ambiente PUID/PGID; queste appartengono all'immagine LinuxServer.io (lscr.io/linuxserver/jellyfin). Sull'immagine ufficiale, l'utente si gestisce tramite la chiave user: in compose; se viene omessa, il container viene eseguito come root. Indipendentemente dalla scelta, la regola è la stessa: l'uid/gid con cui viene eseguito il container deve avere i permessi di lettura e di attraversamento per ogni directory dei media.

Eseguiremo il processo con uid/gid 1000, il primo utente non-root su un sistema Ubuntu standard. Verifica il tuo e imposta la proprietà:

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

Le directory necessitano del bit di esecuzione (il x in 755), non solo di quello di lettura: senza di esso, il container non può entrare nella cartella anche se riesce a visualizzarne il nome. L'errore che svuota un'intera libreria riguarda la cartella genitore: se l'uid del container non può attraversare il mount, non raggiungerà mai /media/Movies o /media/Shows. Di conseguenza, la libreria risulterà vuota e nei log apparirà l'errore Access to the path ... is denied. Ogni singola cartella media non leggibile viene registrata nei log e saltata; pertanto, un gruppo di file copiati come root scomparirà silenziosamente dalla libreria. Per questo motivo è necessario eseguire un chown ricorsivo e impostare il bit di esecuzione su ogni directory, invece di correggere una singola cartella.

Il file docker-compose

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

Riga per riga: user: "1000:1000" imposta effettivamente i permessi del file, corrispondendo alla proprietà indicata sopra. /config contiene l'intero server — account, librerie, metadati, stato di monitoraggio — pertanto deve essere scrivibile ed è l'elemento da eseguire il backup. /cache è lo spazio di lavoro temporaneo. Il mount dei media è :ro (sola lettura) per scelta: Jellyfin salva di default artwork e metadati sotto /config, quindi non deve mai scrivere nella libreria; la modalità sola lettura protegge i file da cancellazioni accidentali o plugin errati. La porta è associata a 127.0.0.1 deliberatamente — il login web di Jellyfin utilizza HTTP semplice, quindi non pubblichiamo mai la porta 8096 su internet. JELLYFIN_PublishedServerUrl è l'indirizzo che il server comunica per l'autodiscovery locale — un broadcast UDP in LAN, quindi i client su internet non lo vedono e utilizzano semplicemente l'URL digitato nell'app. Impostalo all'indirizzo che deve essere comunicato ai client; l'URL dovrà essere inserito manualmente sui dispositivi remoti.

Avvia il servizio dalla directory compose:

docker compose up -d
docker logs -f jellyfin

Primo avvio: la configurazione guidata e le librerie

Poiché la porta è vincolata a localhost, accedi alla configurazione guidata tramite un tunnel SSH dal tuo laptop invece di aprire una porta nel firewall:

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

Ora naviga su http://localhost:8096. La procedura guidata ti permetterà di scegliere la lingua e successivamente di creare un utente admin con una password sicura — questo account gestisce il server, quindi non utilizzare password temporanee. Aggiungi la tua prima libreria: seleziona il tipo di contenuto Movies, punta verso /media/Movies (il percorso interno al container, non il percorso dell'host), e ripeti l'operazione per Shows su /media/Shows. Completa la procedura e Jellyfin avvierà la scansione. Un risultato corretto prevede la comparsa di poster e titoli entro uno o due minuti per librerie di piccole dimensioni. Aggiungi o modifica le librerie in seguito tramite Dashboard → Libraries, e forza una nuova scansione con Scan All Libraries.

Se utilizzi il transcoding, apri Dashboard → Playback → Transcoding e imposta il percorso temporaneo per il transcoding su /cache/transcodes, in modo che i file temporanei vengano salvati sul volume di cache invece di saturare /config. Lascia l'accelerazione hardware impostata su None — non è presente una GPU per l'accelerazione.

Accesso remoto: reverse proxy TLS, oppure mantenere la connessione su VPN

Esistono due metodi sicuri per accedere a Jellyfin dall'esterno e un metodo non sicuro da evitare. Il metodo non sicuro consiste nell'esporre la porta 8096 direttamente su internet: le credenziali di login viaggiano in chiaro e la porta viene soggetta ad attacchi brute-force in poche ore.

Opzione A — reverse proxy TLS. Configura Jellyfin su un sottodominio dietro Traefik con TLS automatico per le app Docker, oppure dietro nginx con un certificato Let's Encrypt emesso da Certbot. Jellyfin utilizza i WebSockets per gli aggiornamenti in tempo reale, pertanto il proxy deve inoltrare gli header di upgrade. Traefik esegue questa operazione automaticamente; nginx richiede la configurazione esplicita degli header e necessita di HTTP/1.1 verso l'upstream affinché l'upgrade avvenga correttamente:

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";
}

Imposta JELLYFIN_PublishedServerUrl con l'indirizzo https:// in modo che l'autodiscovery locale pubblicizzi l'URL corretto — le app remote utilizzano l'indirizzo fornito — e aggiungi fail2ban per limitare i tentativi di brute-force contro il login. Una volta reso il server pubblico, punta Uptime Kuma all'URL per ricevere notifiche di downtime prima degli utenti.

Opzione B — mantenere la connessione privata su VPN. Non pubblicizzare affatto la porta 8096; accedi a Jellyfin solo tramite un tunnel WireGuard terminante sulla stessa macchina. Per un uso domestico questa è la scelta sicura più semplice: nessun certificato, nessuna esposizione pubblica, nessuna superficie di attacco per brute-force. Vincola il container all'indirizzo del tunnel o a localhost e connettiti tramite VPN. Consulta la configurazione VPN WireGuard per un VPS privato per la gestione del tunnel.

Dimensionamento dello storage e backup

Pianifica il budget in base alla qualità, non al numero di file. I film 1080p compressi occupano tra 4 e 15 GB ciascuno; un remux 1080p occupa 20-40 GB; una stagione TV in 1080p occupa 15-40 GB; qualsiasi contenuto 4K occupa tra 40 e 100 GB per film. Una libreria di alcune centinaia di film più alcune serie richiede un volume da 2-4 TB; è più economico sovradimensionare il block volume subito piuttosto che effettuare una migrazione in seguito.

/config contiene l'intero stato del server, quindi è l'unico elemento da backuppare. Esegui uno snapshot o un comando stop-and-tar e conserva la copia su un dispositivo esterno:

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

/cache e la cartella transcode sono eliminabili. I media su /mnt/media vanno backuppati separatamente o considerati ri-rippabili — la maggior parte degli utenti sceglie la seconda opzione a causa delle dimensioni. Gli upgrade sono docker compose pull && docker compose up -d; il tag :10 sopra indicato rimane all'interno della versione major 10.x, quindi il passaggio alla major successiva richiede un'edit manuale del tag — leggi le note di rilascio di Jellyfin prima di procedere, poiché le migrazioni dello schema della libreria avvengono sulle versioni major.

Modalità di errore e stringhe visualizzate

La libreria è vuota dopo una scansione. Il log in Dashboard → Logs (o ~/jellyfin/config/log/log_*.log) mostra:

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

L'uid del container non può leggere quel percorso. Causa: i media sono di proprietà di root o di un uid diverso dal valore user:, una directory manca del bit di esecuzione, o il mount genitore non è attraversabile da quell'uid. Soluzione: chown -R 1000:1000 /mnt/media, directory 755, file 644, quindi avviare una nuova scansione.

La riproduzione satura la CPU e causa buffering. docker stats jellyfin mostra un utilizzo della CPU vicino al 100% moltiplicato per il numero di core, e Dashboard → Playback elenca la sessione come Transcode con una velocità inferiore a 1.0x. Il client non sta effettuando il direct-play; il VPS sta effettuando il transcoding della CPU più lentamente del tempo reale e perde dati. Causa: un codec o un container non supportato, burn-in dei sottotitoli o HDR tone-mapping. Soluzione: passare a un client con supporto direct-play, mantenere i file sorgente in H.264/AAC, usare sottotitoli di tipo text (SRT) invece di sottotitoli di tipo image (PGS/VOBSUB) che forzano il burn-in, e non utilizzare contenuti 4K HDR su sistemi basati esclusivamente su CPU.

"No compatible streams are available." Il messaggio completo è solitamente "This client isn't compatible with the media and the server isn't sending a compatible media format." Il client ha rifiutato la sorgente e il fallback del transcoding non è partito. Causa: un comando ffmpeg errato, un file illeggibile o il profilo utente che blocca la conversione video. Soluzione: leggere la riga ffmpeg in Dashboard → Logs, verificare se il file è riproducibile, controllare i permessi di riproduzione dell'utente se si dipende dal transcoding e provare un secondo client per escludere problemi relativi ai codec del browser.

I film non hanno la locandina o hanno quella errata. I metadati non corrispondono. Causa: un film non si trova nella propria cartella Name (Year), una cartella della stagione è denominata S01 invece di Season 01, gli episodi non sono in formato S01E01, o manca l'anno. Soluzione: rinominare secondo il layout sopra indicato, quindi eseguire Refresh metadata → Replace all, oppure usare Identify su un singolo elemento per assegnare la corretta voce TMDB/TVDB.

FAQ

Un VPS può effettuare il transcoding video senza una GPU?

Sì, ma solo tramite CPU, ed è un'operazione costosa. Un singolo transcoding software in 1080p può saturare diversi vCPU. Il formato 4K o HEVC solitamente non riesce a mantenere il tempo reale, causando il buffering della riproduzione. La soluzione migliore è evitare il transcoding: mantieni la libreria in H.264/AAC e usa app client che supportino il direct-play, in modo che il VPS trasmetta solo i byte. Noleggia un'istanza con GPU solo se il transcoding on-the-fly è strettamente necessario.

Perché la mia libreria Jellyfin è vuota dopo una scansione?

Quasi sempre è un problema di permessi. L'immagine ufficiale jellyfin/jellyfin viene eseguita con l'utente user: configurato (o come root); se i file non sono leggibili da tale uid, la scansione genera log Access to the path ... is denied e li salta. Correggi la proprietà dei file con chown -R 1000:1000 /mnt/media, assegna il bit di esecuzione alle directory (755) e avvia una nuova scansione. Controlla anche la cartella genitore: se l'uid del container non può attraversare /mnt/media, non raggiungerà mai le cartelle della libreria e tutto risulterà vuoto. La seconda causa più comune è una struttura delle cartelle non conforme alle aspettative di Jellyfin.

Come posso accedere a Jellyfin da remoto in modo sicuro?

Esistono due opzioni valide. Utilizza un reverse proxy TLS su un sottodominio per crittografare il login e lo streaming, e installa fail2ban; non esporre mai la porta 8096 in chiaro, poiché invia la password senza crittografia. In alternativa, mantieni il servizio privato e accedi solo tramite VPN, che è la scelta più semplice per una rete domestica. Fornisci alle app l'indirizzo pubblico direttamente: l'autodiscovery utilizza broadcast sulla rete locale e non raggiunge i client che provengono da internet.

Di quanto disco e banda ha bisogno un VPS Jellyfin?

Lo spazio disco dipende dalla qualità: calcola circa 4-15 GB per ogni film compresso in 1080p, 20-40 GB per ogni remux e 40-100 GB per il 4K; pertanto, la maggior parte delle librerie richiede un volume da 2-4 TB. La banda è determinata dal bitrate del direct-play: 8-12 Mbps per uno stream 1080p, molto di più per il 4K. Verifica che la velocità della porta supporti il numero di utenti simultanei e monitora il limite di trasferimento mensile. Aumenta la potenza della CPU se prevedi di fare transcoding; dai priorità alla banda rispetto ai core se prevedi di usare il direct-play.

È legale eseguire Jellyfin su un VPS?

Jellyfin è un software free e open-source, quindi eseguirlo è del tutto legale. Ciò che conta è il contenuto: trasmetti solo media di tua proprietà o per i quali possiedi una licenza (rip dei tuoi dischi, registrazioni o file autorizzati). Jellyfin non include media e non fornisce metodi per ottenerli; è un player per una libreria già in tuo possesso.

#jellyfin#media-server#docker#self-hosting#transcoding