SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-26

Jellyfin su VPS: streaming della tua libreria

Installa Jellyfin in Docker su un VPS: configura block storage e permessi, capisci Direct Play e transcodifica CPU, e proteggi l'accesso remoto.

Cosa stai configurando

Un media server Jellyfin su un VPS: un container, tre volumi e un disco di block storage che contiene film e serie, accessibile da qualsiasi browser o app Jellyfin. L'installazione richiede un file compose di quindici righe. Quasi tutti i problemi successivi dipendono da due cause: permessi sui file che impediscono al container di leggerli e richieste di transcodifica video a un VPS privo di GPU. Questo tutorial dedica la maggior parte dello spazio a questi due aspetti, perché è qui che si concentrano le richieste di supporto.

Jellyfin è gratuito e completamente open source. Non richiede un account, non include funzionalità a pagamento e non raccoglie dati di telemetria. Per questo compare in quasi tutti gli elenchi di progetti che vale la pena ospitare in autonomia nel 2026. Riproduce i contenuti multimediali che possiedi. Non fornisce alcun contenuto e questo tutorial non tratta alcun metodo per procurarsene.

La realtà della transcodifica, prima di noleggiare qualsiasi cosa

Leggi prima questa sezione, perché cambia ciò che devi acquistare. Un media server esegue una di due operazioni quando premi Play. La riproduzione diretta trasmette il file così com’è: il VPS legge i byte dal disco e li invia attraverso la rete, con un consumo di CPU quasi nullo. La transcodifica ricodifica il video in tempo reale, con una nuova risoluzione, un nuovo codec oppure i sottotitoli impressi nell’immagine, e richiede esclusivamente lavoro della CPU.

Un VPS tipico non dispone di una GPU. Ogni transcodifica viene quindi eseguita dalla 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 di solito non riesce a mantenere la velocità in tempo reale, quindi la riproduzione si interrompe e il buffering continua senza fine. La transcodifica hardware, che rende economica questa operazione su un computer domestico con una iGPU Intel o una scheda Nvidia, non è disponibile a meno che il provider non offra istanze GPU.

Su un VPS, la strategia consiste quindi nell’evitare la transcodifica. Mantieni la libreria in codec riprodotti nativamente dai client, con video H.264, audio AAC o AC3 e un contenitore MP4 o MKV, e scegli applicazioni client che supportano la riproduzione diretta: le applicazioni Jellyfin native per Android TV, iOS e Roku, oltre a Infuse, Kodi e Jellyfin Media Player per desktop. In questo modo il VPS non esegue mai ffmpeg e un server con 2 vCPU può trasmettere contenuti a più persone contemporaneamente. Se prevedi di usare la transcodifica, ti serve un server molto più grande e costoso; anche in questo caso, il 4K resta una scelta poco realistica.

Calcola anche la larghezza di banda, perché è l’altra sorpresa. La riproduzione diretta invia il file al relativo bitrate. Un file 1080p compresso richiede 8-12 Mbps; un remux Blu-ray 1080p richiede 20-30 Mbps; il formato 4K HDR richiede 40-80 Mbps. Tre persone che riproducono direttamente file da 10 Mbps generano 30 Mbps di traffico in upload continuo dal tuo VPS. Controlla due valori del tuo piano: la velocità della porta (può trasmettere 30 Mbps in upstream?) e il limite mensile di trasferimento. Un film di due ore a 10 Mbps genera circa 9 GB di traffico in uscita. Quindi, con una quota a consumo di 1 TB/mese, puoi vedere poco più di cento film di questo tipo al mese, cioè tre o quattro al giorno. Un nucleo familiare che guarda contenuti 4K, con un bitrate da quattro a otto volte superiore, esaurisce la quota molto più rapidamente. Considera nel calcolo anche tutto il traffico in uscita generato dallo stesso server, incluso un relay RustDesk self-hosted, che trasporta un’intera sessione di desktop remoto quando due peer non riescono a connettersi direttamente.

Prerequisiti

  • Un VPS KVM Ubuntu 24.04 nuovo, con accesso root o sudo, Docker e il plugin Compose installati.
  • Un volume di block storage per i contenuti multimediali, dimensionato in base alla libreria (vedere la sezione sul dimensionamento). Il piccolo disco root fornito con il VPS non è destinato ai film.
  • Un nome di dominio se si desidera l'accesso HTTPS pubblico, oppure una VPN WireGuard sullo stesso VPS se si preferisce mantenere tutto privato.
  • Contenuti multimediali che si è legalmente autorizzati a trasmettere in streaming: proprie copie, proprie registrazioni e file di cui si detiene la proprietà.

Monta prima lo storage a blocchi

Collega il volume dal pannello del provider, quindi individualo e montalo. Ricava il nome del dispositivo da lsblk: sarà simile a /dev/sdb o /dev/vdb, mai al 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

Monta il volume tramite UUID, non tramite /dev/sdb, perché le lettere dei dispositivi possono cambiare tra i riavvii e potresti 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 è importante: senza questa opzione, se il volume a blocchi viene scollegato, il server non riesce ad avviarsi e passa a una shell di emergenza. L'errore più grave consiste nell'eseguire mkfs.ext4 su un volume che contiene già dati: il comando li cancella. Formatta solo i volumi nuovi; se il disco contiene già la tua libreria, passa direttamente alla riga di fstab.

Organizza i contenuti multimediali nel formato previsto da Jellyfin

Jellyfin associa i metadati ai nomi di cartelle e file. Se la struttura non è corretta, i film vengono importati come file senza titolo e senza locandina oppure un episodio viene associato alla serie sbagliata. Le regole sono esattamente tre: ogni film deve trovarsi nella propria cartella Name (Year) e avere un nome file corrispondente; le cartelle delle stagioni devono chiamarsi Season 01, non S01; i file degli episodi devono usare S01E01; gli speciali devono trovarsi 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) nei film non è un elemento estetico: distingue i remake e consente al motore di matching di selezionare il titolo corretto. Mantieni Movies e Shows come cartelle di primo livello separate, perché ciascuna diventa una libreria Jellyfin di un tipo di contenuti specifico e mescolarle confonde il provider dei metadati. Jellyfin indicizza senza problemi anche una terza cartella di foto, ma l'esperienza è limitata rispetto a quella offerta da un server per foto dedicato. Se gli album sono importanti, assegna loro un server separato con PhotoPrism o Immich e usa questo server per film e serie TV.

Permessi: il motivo principale per cui le librerie risultano vuote

Questo è l'equivoco che può costare un'intera serata. L'immagine ufficiale jellyfin/jellyfin non riconosce le variabili d'ambiente PUID/PGID, che appartengono all'immagine LinuxServer.io (lscr.io/linuxserver/jellyfin). Nell'immagine ufficiale si controlla l'utente con la chiave user: in compose; se la si omette, il container viene eseguito come root. In entrambi i casi, la regola è la stessa: lo uid/gid con cui viene eseguito il container deve poter leggere e attraversare ogni directory dei contenuti multimediali.

Eseguiremo il container con uid/gid 1000, il primo utente non root in un'installazione standard di Ubuntu. Verifica il tuo uid/gid e imposta il proprietario:

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 richiedono il bit execute (il x in 755), non soltanto il permesso di lettura. Senza questo bit, il container non può entrare nella directory, anche se può elencarne il nome. Il problema che svuota un'intera libreria è la directory padre: se lo uid del container non può attraversare il mount stesso, non raggiunge mai /media/Movies o /media/Shows e tutte le librerie risultano immediatamente vuote, con Access to the path ... is denied nel log. Qualsiasi singola directory multimediale che il container non può leggere viene registrata nel log e ignorata. Di conseguenza, un gruppo di file copiati come root scompare dalla libreria senza messaggi evidenti. Per questo modifichiamo ricorsivamente il proprietario e impostiamo il bit execute su ogni directory, invece di correggere una sola 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" è ciò che imposta effettivamente i permessi dei file, in linea con la proprietà definita sopra. /config contiene l'intero stato del server, inclusi account, librerie, metadati e stato di monitoraggio; deve quindi essere scrivibile ed è la directory di cui eseguire il backup. /cache è uno spazio di lavoro temporaneo. Il mount dei contenuti multimediali è :ro (sola lettura) intenzionalmente: per impostazione predefinita, Jellyfin salva immagini e metadati in /config, quindi non deve mai scrivere nella libreria. La modalità di sola lettura protegge inoltre i file da eliminazioni accidentali o da plugin difettosi. La porta è associata intenzionalmente a 127.0.0.1: il login web di Jellyfin usa HTTP in chiaro, quindi non esponiamo mai la porta 8096 su Internet pubblico. JELLYFIN_PublishedServerUrl è l'indirizzo che il server annuncia per l'autodiscovery locale tramite broadcast UDP sulla LAN. I client su Internet non lo vedono e usano semplicemente l'URL immesso nell'app. Impostalo sull'indirizzo che deve essere comunicato ai client e prevedi di dover inserire manualmente quell'URL nei dispositivi remoti.

Avvialo dalla directory del compose:

docker compose up -d
docker logs -f jellyfin

Prima esecuzione: procedura guidata di configurazione e librerie

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

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

Apri quindi http://localhost:8096 nel browser. La procedura guidata configura prima la lingua, quindi consente di creare un utente amministratore con una password complessa. Questo account dà accesso al server, quindi non riutilizzare una password provvisoria. Aggiungi la prima libreria: seleziona il tipo di contenuto Movies, indica /media/Movies (il percorso all'interno del container, non il percorso dell'host), quindi ripeti la procedura selezionando Shows e indicando /media/Shows. Completa la configurazione e Jellyfin avvierà la scansione. In una libreria di piccole dimensioni, il risultato corretto consiste nella comparsa di poster e titoli entro uno o due minuti. In seguito puoi aggiungere o modificare le librerie in Dashboard → Libraries e forzare una nuova scansione con Scan All Libraries.

Se utilizzi anche una sola funzione di transcodifica, apri Dashboard → Playback → Transcoding e imposta il percorso temporaneo della transcodifica su /cache/transcodes. In questo modo i file temporanei vengono scritti sul volume cache invece di aumentare inutilmente le dimensioni di /config. Lascia l'accelerazione hardware impostata su None, perché non è disponibile alcuna GPU.

Accesso remoto: proxy inverso TLS oppure accesso tramite VPN

Esistono due modi sicuri per raggiungere Jellyfin dall'esterno e un modo non sicuro da evitare. Il modo non sicuro consiste nel pubblicare direttamente su Internet la porta 8096: le credenziali di accesso viaggiano in chiaro e la porta viene sottoposta ad attacchi brute force nel giro di poche ore.

Opzione A, proxy inverso TLS. Pubblica Jellyfin su un sottodominio dietro Traefik con TLS automatico per le applicazioni Docker oppure dietro nginx con un certificato Let's Encrypt emesso da Certbot. Jellyfin usa WebSocket per gli aggiornamenti in tempo reale, quindi il proxy deve inoltrare gli header di upgrade. Traefik lo fa automaticamente; con nginx è necessario specificarli esplicitamente e usare HTTP/1.1 verso l'upstream, altrimenti l'upgrade non viene eseguito:

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 sull'indirizzo https://, in modo che l'autodiscovery locale pubblicizzi l'URL corretto e che le applicazioni remote usino l'indirizzo fornito. Aggiungi inoltre fail2ban per rallentare i tentativi di brute force contro l'accesso. Quando il server è pubblico, configura Uptime Kuma sull'URL, così riceverai una notifica del downtime prima dei tuoi utenti.

Opzione B, mantienilo privato tramite una VPN. Non pubblicare affatto la porta 8096; accedi a Jellyfin solo tramite un tunnel WireGuard terminato sullo stesso host. Per un ambiente domestico è la scelta sicura più semplice: non richiede certificati, non espone il servizio pubblicamente e non offre una superficie per gli attacchi brute force. Associa il container all'indirizzo del tunnel o a localhost e connettiti tramite la VPN. Consulta la configurazione di una VPN WireGuard per un VPS privato per configurare il tunnel.

Dimensionamento dello storage e backup

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

/config contiene l'intero stato del server, quindi è l'unico elemento che devi necessariamente sottoporre a backup. Crea uno snapshot oppure arresta il servizio e crea un archivio tar, quindi conserva la copia fuori dal server:

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

/cache e la cartella di transcodifica sono eliminabili. Esegui il backup separato dei contenuti multimediali presenti in /mnt/media oppure accetta di doverli rippare nuovamente. Date le dimensioni, la maggior parte degli utenti sceglie la seconda opzione. Gli aggiornamenti sono docker compose pull && docker compose up -d; il tag :10 indicato sopra resta all'interno della versione principale 10.x. Il passaggio alla versione principale successiva richiede quindi una modifica intenzionale del tag. Prima di eseguirla, consulta rapidamente le note di rilascio di Jellyfin, perché le migrazioni dello schema della libreria avvengono nelle versioni principali. Un tag bloccato e una sola directory di stato sottoposta a backup costituiscono l'intera procedura per qualsiasi container sempre attivo. È lo stesso modello usato per mantenere attivi memoria e pianificazioni di un agent self-hosted dopo i riavvii.

Modalità di errore e stringhe visualizzate

La libreria è vuota dopo una scansione. Il log disponibile in Dashboard → Logs (oppure ~/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. Cause possibili: i file multimediali appartengono a root o a un uid diverso dal valore user:, una directory non ha il bit di esecuzione oppure il mount padre non è attraversabile da quell'uid. Correzione: chown -R 1000:1000 /mnt/media, directory 755, file 644, quindi eseguire nuovamente la scansione.

La riproduzione porta la CPU al limite e usa il buffering. docker stats jellyfin mostra un utilizzo della CPU vicino al 100% moltiplicato per il numero di core e Dashboard → Playback indica la sessione come Transcode, con una velocità inferiore a 1.0x. Il client non sta eseguendo la riproduzione diretta. Il VPS sta quindi transcodificando tramite CPU a una velocità inferiore al tempo reale e accumula ritardo. Cause possibili: un codec o un container non supportato, il rendering permanente dei sottotitoli oppure il tone mapping HDR. Correzione: passare a un client che supporti la riproduzione diretta, mantenere le sorgenti in H.264/AAC, usare sottotitoli testuali (SRT) invece di sottotitoli immagine (PGS/VOBSUB), che impongono il rendering permanente, e non riprodurre contenuti 4K HDR su un sistema privo di accelerazione hardware e basato soltanto sulla CPU.

"Non sono disponibili stream compatibili." Il messaggio completo è in genere "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 anche la transcodifica di fallback non è stata avviata. Cause possibili: un comando ffmpeg non valido, un file illeggibile oppure il profilo dell'utente impedisce la conversione video. Correzione: leggere la riga ffmpeg in Dashboard → Logs, verificare che il file venga riprodotto, controllare i permessi di riproduzione dell'utente se si dipende dalla transcodifica e provare un secondo client per escludere problemi legati ai codec del browser.

I film non hanno un poster oppure hanno quello errato. I metadati non hanno trovato una corrispondenza. Cause possibili: un film non si trova nella propria directory Name (Year), una directory della stagione è denominata S01 invece di Season 01, gli episodi non sono nel formato S01E01 oppure manca l'anno. Correzione: rinominare gli elementi secondo la struttura precedente, quindi selezionare Refresh metadata → Replace all, oppure usare Identify su un singolo elemento per impostare la voce corretta di TMDB/TVDB.

FAQ

Un VPS può eseguire la transcodifica video senza una GPU?

Sì, ma soltanto sulla CPU, con un costo elevato in termini di risorse. Una singola transcodifica software a 1080p può saturare diverse vCPU, mentre il 4K o HEVC di solito non riesce a mantenere il tempo reale e la riproduzione entra in buffering. La soluzione più efficace è evitare la transcodifica: mantieni la libreria in H.264/AAC e usa applicazioni client che supportano la riproduzione diretta, così il VPS deve soltanto trasferire i dati. Noleggia un'istanza con GPU solo se ti serve realmente la transcodifica durante la riproduzione.

Perché la libreria Jellyfin è vuota dopo una scansione?

Quasi sempre il problema riguarda i permessi. L'immagine ufficiale jellyfin/jellyfin viene eseguita con l'utente impostato in user:, oppure come root. Se quell'uid non può leggere i file, la scansione registra Access to the path ... is denied nei log e li ignora. Correggi il proprietario con chown -R 1000:1000 /mnt/media, assegna alle directory il permesso di esecuzione con 755, quindi avvia una nuova scansione. Controlla anche la directory padre: se l'uid del container non può attraversare /mnt/media, non raggiunge mai le directory della libreria e il risultato è una libreria vuota. La seconda causa più comune è una struttura delle directory non conforme alle aspettative di Jellyfin.

Come posso accedere a Jellyfin da remoto in modo sicuro?

Ci sono due opzioni valide. Pubblicalo dietro un reverse proxy TLS su un sottodominio, in modo da cifrare l'accesso e lo streaming, e aggiungi fail2ban. Non esporre mai la porta non cifrata 8096, perché invia la password in chiaro. In alternativa, mantieni Jellyfin completamente privato e accedivi soltanto tramite una VPN: per una rete domestica è la scelta sicura più semplice. Configura nelle applicazioni direttamente l'indirizzo pubblico. L'autodiscovery è un broadcast sulla rete locale, quindi non raggiunge i client che si collegano da Internet.

Di quanto spazio disco e banda ha bisogno un VPS con Jellyfin?

Lo spazio disco dipende dalla qualità: considera 4-15 GB per un film 1080p compresso, 20-40 GB per un remux e 40-100 GB per un contenuto 4K. Per la maggior parte delle librerie serve quindi un volume a blocchi da 2-4 TB. La banda dipende dal bitrate della riproduzione diretta: 8-12 Mbps per ogni streaming 1080p e molto di più per il 4K. Verifica quindi che la velocità della porta sia sufficiente per il numero di spettatori simultanei e controlla il limite mensile di trasferimento. Aggiungi margine sulla CPU se prevedi di eseguire transcodifiche. Se prevedi di usare la riproduzione diretta, dai priorità alla banda rispetto al numero di core.

È legale eseguire Jellyfin su un VPS?

Jellyfin è un software gratuito e open source, quindi eseguirlo è perfettamente legale. Il punto è il contenuto: trasmetti in streaming soltanto contenuti multimediali che possiedi o per i quali disponi di una licenza, come copie personali dei tuoi dischi, registrazioni o file che hai il diritto di conservare. Jellyfin non include alcun contenuto multimediale e non offre strumenti per ottenerlo. È un riproduttore per una libreria che già possiedi.