Jellyfin sa VPS: I-stream ang Sarili Mong Media
Alamin ang tamang Docker volumes, file permissions, direct play, CPU transcoding, block storage, at ligtas na remote access sa Jellyfin sa isang VPS.
Mga binubuo mo
Isang Jellyfin media server sa isang VPS: isang container, tatlong volume, at isang block-storage disk na naglalaman ng iyong mga pelikula at palabas. Maa-access ito mula sa anumang browser o Jellyfin app. Labinlimang linya lamang ang install sa compose file. Karamihan ng problemang lumilitaw pagkatapos nito ay nagmumula sa dalawang bagay: mga file permission na hindi mabasa ng container, at paghingi sa isang VPS na walang GPU na mag-transcode ng video kahit hindi ito angkop para sa ganoong gawain. Karamihan ng gabay na ito ay nakatuon sa dalawang iyon dahil doon nagmumula ang mga support ticket.
Libre at ganap na open source ang Jellyfin. Wala itong account requirement, walang feature na may bayad, at walang telemetry. Ito ang dahilan kung bakit halos palagi itong kasama sa mga listahan ng mga sulit i-self-host sa 2026. Pinapatugtog nito ang media na pagmamay-ari mo. Wala itong kasamang content, at hindi tungkol sa pagkuha ng content ang gabay na ito.
Ang realidad ng transcoding bago ka mag-rent ng anuman
Basahin muna ito dahil makaaapekto ito sa bibilhin mo. Dalawa ang maaaring gawin ng media server kapag pinindot mo ang play. Sa direct play, sine-stream ang file nang hindi binabago: binabasa ng VPS ang bytes mula sa disk at ipinapadala ang mga ito sa network, kaya halos walang CPU ang ginagamit. Sa transcoding, nire-re-encode ang video habang nagpe-play, gaya ng pagpapalit ng resolution o codec, o pag-burn in ng subtitles. Puro CPU work ito.
Karaniwang walang GPU ang VPS. Kaya bawat transcode ay tumatakbo sa CPU gamit ang libx264/libx265, at mahal sa resources ang software encoding. Maaaring maubos ng isang 1080p H.264 transcode ang ilang shared vCPU; karaniwang hindi makahabol sa real time ang 4K o HEVC transcode, kaya humihinto ang playback at tuloy-tuloy ang buffering. Ang hardware transcoding, na nagpapamura sa prosesong ito sa home box na may Intel iGPU o Nvidia card, ay hindi available sa iyo maliban kung nagrerenta ang provider mo ng GPU instances.
Kaya ang buong strategy sa VPS ay: iwasan ang transcoding. Panatilihin ang library sa mga codec na native na napo-play ng iyong mga client: H.264 video, AAC o AC3 audio, sa MP4 o MKV container. Pumili rin ng client app na gumagamit ng direct play: ang native Jellyfin apps para sa Android TV, iOS at Roku, pati Infuse, Kodi, at desktop Jellyfin Media Player. Kapag ginawa mo ito, hindi kailangang gamitin ng VPS ang ffmpeg, at kayang mag-stream ng isang modest na 2 vCPU box sa ilang tao nang sabay-sabay. Kung plano mong mag-transcode, kailangan mo ng mas malaki at mas mahal na box. Kahit ganoon, hindi magandang piliin ang 4K.
Gawin din ang bandwidth math dahil ito ang isa pang karaniwang sorpresa. Kapag direct play, ipinapadala ang file ayon sa sarili nitong bitrate. Ang compressed na 1080p file ay gumagamit ng 8-12 Mbps; ang 1080p Blu-ray remux ay 20-30 Mbps; at ang 4K HDR ay 40-80 Mbps. Kung tatlong tao ang nagdi-direct play ng mga file na 10 Mbps, 30 Mbps na tuloy-tuloy na upload ang magmumula sa VPS mo. Suriin ang dalawang numero sa plan mo: ang port speed (kaya ba nitong magpadala ng 30 Mbps upstream?) at ang monthly transfer cap. Ang isang dalawang oras na 10 Mbps na pelikula ay humigit-kumulang 9 GB na lumalabas, kaya ang metered na allowance na 1 TB/buwan ay katumbas ng mahigit 100 ganoong pelikula bawat buwan, o tatlo hanggang apat bawat araw. Kapag 4K ang pinapanood ng isang household, na may bitrate na apat hanggang walong beses na mas mataas, mas mabilis maubos ang allowance. Isama sa parehong budget ang anumang iba pang nagpapalabas ng traffic mula sa kaparehong server, kabilang ang isang self-hosted na RustDesk relay, na nagdadala ng buong remote desktop session kapag hindi direktang makakonekta ang dalawang peer.
Mga Kinakailangan
- Isang bagong Ubuntu 24.04 KVM VPS na may root o sudo, at naka-install ang Docker at Compose plugin.
- Isang block-storage volume para sa media, na may sapat na laki para sa iyong library (tingnan ang sizing sa ibaba). Hindi para sa mga pelikula mo ang maliit na root disk na kasama ng VPS.
- Isang domain name kung gusto mo ng public HTTPS access, o isang WireGuard VPN sa parehong VPS kung mas gusto mong panatilihing private ang buong setup.
- Media na legal mong may karapatang i-stream, kabilang ang sarili mong rips, sarili mong recordings, at mga file na pagmamay-ari mo.
I-mount muna ang block storage
I-attach ang volume sa panel ng provider mo, pagkatapos ay hanapin at i-mount ito. Kunin ang device name mula sa lsblk. Magiging parang /dev/sdb o /dev/vdb ito, at hindi kailanman ang 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 deviceI-mount ito gamit ang UUID, hindi gamit ang /dev/sdb, dahil maaaring magbago ang pagkakasunod-sunod ng mga device letter pagkatapos ng reboot. Maaari kang mauwi sa pag-format o pag-mount ng maling disk. Magdagdag ng isang linya sa /etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/mediaMahalaga ang nofail: kung ma-detach ang block volume, hindi magbo-boot ang server at mapupunta ito sa emergency shell. Ang pinakamalaking pagkakamali rito ay ang pagpapatakbo ng mkfs.ext4 sa volume na may laman nang data, dahil mabubura nito ang data. I-format lamang ang mga bagong volume. Kung nasa disk na ang library mo, dumiretso sa fstab line.
Ayusin ang media ayon sa inaasahang layout ng Jellyfin
Tinutugma ng Jellyfin ang metadata batay sa mga pangalan ng folder at file. Kapag mali ang layout, lalabas ang mga pelikula bilang mga file na walang pamagat at poster, o mapupunta ang isang episode sa maling series. May eksaktong tatlong rule: dapat nasa sarili nitong Name (Year) folder ang bawat movie at tugma rito ang filename; dapat Season 01 ang pangalan ng season folders, hindi S01; dapat S01E01 ang format ng episode files; at dapat nasa Season 00 ang mga special.
/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.mkvHindi dekorasyon ang (Year) sa mga movie. Tinutulungan nitong makilala ang mga remake para makuha ng matcher ang tamang title. Panatilihing magkahiwalay na top-level folder ang Movies at Shows dahil ang bawat isa ay magiging Jellyfin library para sa partikular na uri ng content, at nakalilito sa metadata provider kapag pinaghalo ang mga ito. I-i-index din ng Jellyfin ang ikatlong folder ng mga larawan, pero limitado ang experience nito kumpara sa photo server na talagang ginawa para rito. Kaya kung mahalaga ang mga album mo, ilagay ang mga ito sa sarili nilang box na nagpapatakbo ng PhotoPrism o Immich, at gamitin ang server na ito para sa film at TV.
Mga permission: pangunahing dahilan kung bakit walang laman ang mga library
Narito ang maling palagay na maaaring kumain ng isang buong gabi. Hindi ginagamit ng opisyal na jellyfin/jellyfin image ang mga environment variable na PUID at PGID. Para iyon sa LinuxServer.io image (lscr.io/linuxserver/jellyfin). Sa opisyal na image, kinokontrol mo ang user gamit ang key na user: sa compose. Kung aalisin mo ito, tumatakbo ang container bilang root. Anuman ang gamitin mo, pareho ang tuntunin: dapat may kakayahan ang uid/gid na gamit ng container na magbasa at mag-traverse sa bawat media directory.
Gagamit tayo ng uid/gid na 1000, ang unang non-root user sa karaniwang Ubuntu installation. Tiyakin ang iyo at itakda ang ownership:
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 ~/jellyfinKailangan ng mga directory ang execute bit, ang x sa 755, at hindi sapat ang read bit lamang. Kung wala ito, hindi makakapasok ang container sa folder kahit kaya nitong ilista ang pangalan nito. Ang karaniwang problemang nagpapawalang-laman sa buong library ay ang parent directory: kung hindi ma-traverse ng uid ng container ang mismong mount, hindi nito maaabot ang /media/Movies o /media/Shows. Dahil dito, sabay-sabay na nagiging walang laman ang lahat ng library, at lumilitaw ang Access to the path ... is denied sa log. Nila-log at nilalaktawan ang anumang media folder na hindi nito mabasa. Kaya tahimik na nawawala sa library ang batch ng mga file na kinopya bilang root. Ito ang dahilan kung bakit recursive kaming nagcha-chown at nagse-set ng execute bit sa bawat directory sa halip na ayusin lamang ang isang folder.
Ang docker-compose file
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.comIsa-isahin: ang user: "1000:1000" ang aktuwal na nagse-set ng file permissions, ayon sa ownership sa itaas. Nasa /config ang buong server, mga account, library, metadata, at watch state, kaya dapat itong writable at ito ang kailangang i-back up. Ang /cache ay pansamantalang working space. Sadyang read-only ang media mount na :ro: default na nagse-save ang Jellyfin ng artwork at metadata sa /config, kaya hindi nito kailangang magsulat sa library mo. Pinoprotektahan din ng read-only setting ang mga file mo laban sa aksidenteng pag-delete o maling plugin. Sadyang naka-bind ang port sa 127.0.0.1. Plain HTTP ang web login ng Jellyfin, kaya hindi namin inilalantad sa public internet ang port 8096. Ang JELLYFIN_PublishedServerUrl ang address na ina-advertise ng server para sa local autodiscovery, gamit ang LAN UDP broadcast. Dahil dito, hindi ito nakikita ng mga client sa internet at ginagamit nila ang URL na manu-mano mong inilalagay sa app. Itakda ito sa address na dapat ibigay sa mga client, at asahang manu-mano mong ilalagay ang URL na iyon sa mga remote device.
I-start ito mula sa compose directory:
docker compose up -d
docker logs -f jellyfinUnang pag-run: ang setup wizard at ang iyong mga library
Dahil naka-bind ang port sa localhost, gamitin ang SSH tunnel mula sa iyong laptop para ma-access ang wizard sa halip na magbukas ng butas sa firewall:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipPumunta ngayon sa http://localhost:8096. Gagabayan ka ng wizard sa pagpili ng wika, at pagkatapos ay sa paggawa ng admin user na may malakas na password. Ang account na ito ang gagamitin mo sa iyong server, kaya huwag gumamit muli ng pansamantalang password. Idagdag ang iyong unang library: piliin ang content type na Movies, ituro ito sa /media/Movies (ang path sa loob ng container, hindi ang host path), at ulitin gamit ang Shows sa /media/Shows. Tapusin ang setup at magsisimulang mag-scan ang Jellyfin. Ang tamang resulta ay makikita kapag lumitaw ang mga poster at title sa loob ng isa o dalawang minuto para sa maliit na library. Maaari kang magdagdag o mag-edit ng mga library sa Dashboard → Libraries, at puwersahin ang panibagong scan gamit ang Scan All Libraries.
Kung gumagamit ka ng anumang transcoding, buksan ang Dashboard → Playback → Transcoding at itakda ang transcode temp path sa /cache/transcodes para mapunta sa cache volume ang mga pansamantalang file sa halip na lumaki ang /config. Iwanang nakatakda sa None ang hardware acceleration dahil walang GPU na magagamit para dito.
Remote access: TLS reverse proxy, o panatilihin sa VPN
May dalawang ligtas na paraan para ma-access ang Jellyfin mula sa labas, at may isang hindi ligtas na paraang dapat iwasan. Ang hindi ligtas na paraan ay ang direktang pag-publish ng port 8096 sa internet: ipinapadala ang login nang cleartext, at tina-target ng brute-force attacks ang port sa loob lang ng ilang oras.
Option A, TLS reverse proxy. Ilagay ang Jellyfin sa isang subdomain sa likod ng Traefik na may automatic TLS para sa iyong Docker apps, o sa likod ng nginx gamit ang Let's Encrypt certificate na in-issue ng Certbot. Gumagamit ang Jellyfin ng WebSockets para sa real-time updates, kaya dapat ipasa ng proxy ang upgrade headers. Awtomatiko itong ginagawa ng Traefik; sa nginx, kailangang ilista ang mga ito nang tahasan, at kailangang gumamit ng HTTP/1.1 papunta sa upstream. Kung hindi, hindi magaganap ang upgrade:
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";
}Itakda ang JELLYFIN_PublishedServerUrl sa https:// address upang i-advertise ng local autodiscovery ang tamang URL, magamit ng remote apps ang address na ibinibigay mo, at magdagdag ng fail2ban para pabagalin ang mga brute-force attempt laban sa login. Kapag public na ang server, ituro ang Uptime Kuma sa URL upang malaman mo ang downtime bago ito mapansin ng iyong mga viewer.
Option B, panatilihing private sa VPN. Huwag i-publish ang 8096; i-access ang Jellyfin sa pamamagitan lamang ng WireGuard tunnel na nagtatapos sa parehong box. Para sa isang household, ito ang pinakasimpleng secure na opsyon: walang certificate, walang public exposure, at walang brute-force surface. I-bind ang container sa tunnel address o localhost at kumonekta sa pamamagitan ng VPN. Tingnan ang WireGuard VPN setup para sa private VPS para sa mismong tunnel.
Storage sizing at backups
Maglaan ng budget batay sa kalidad, hindi sa bilang ng file. Karaniwang 4-15 GB ang compressed na 1080p film bawat isa; 20-40 GB ang 1080p remux; 15-40 GB ang isang season ng 1080p TV; at 40-100 GB bawat film ang anumang 4K. Ang library na may ilang daang film at ilang palabas ay nangangailangan ng 2-4 TB na volume. Mas mura ring maglaan nang sobra sa block volume nang isang beses kaysa mag-migrate sa ibang pagkakataon.
/config ang kumpletong state ng server, kaya ito ang nag-iisang bagay na kailangan mong i-back up. Gumawa ng snapshot, o ihinto ito at i-tar, pagkatapos ay panatilihin ang kopya sa labas ng server:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -dMaaaring itapon ang /cache at ang transcode folder. I-back up nang hiwalay ang media sa /mnt/media, o tanggapin na maaari itong i-rip muli. Mas pinipili ng karamihan ang huli dahil malaki ang kinakailangang storage. Ang mga upgrade ay docker compose pull && docker compose up -d. Ang tag na :10 sa itaas ay nananatili sa loob ng 10.x major version, kaya ang paglipat sa susunod na major version ay dapat gawin sa pamamagitan ng planadong pag-edit ng tag. Basahin muna nang mabilis ang Jellyfin release notes bago ito gawin, dahil isinasagawa ang library schema migration sa mga major version. Ang pinned tag kasama ang isang naka-back-up na state directory ang buong recipe para sa anumang always-on container. Ito rin ang pattern sa likod ng pagpapanatili ng memory at schedule ng isang self-hosted agent sa pag-reboot.
Mga failure mode at mga string na makikita mo
Walang laman ang library matapos ang scan. Ipinapakita ng log sa Dashboard → Logs (o ~/jellyfin/config/log/log_*.log) ang:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.Hindi mabasa ng uid ng container ang path na iyon. Dahilan: pagmamay-ari ng root ang media o ng uid na iba sa value ng iyong user:, walang execute bit ang isang directory, o hindi madaanan ng uid na iyon ang parent mount mismo. Para ayusin ito: chown -R 1000:1000 /mnt/media, ang mga directory ay 755, at ang mga file ay 644; pagkatapos ay magsagawa ulit ng scan.
Pinupuno ng playback ang CPU at nagbu-buffer. Ipinapakita ng docker stats jellyfin ang CPU na malapit sa 100% na minultiply sa bilang ng iyong core, at inililista ng Dashboard → Playback ang session bilang Transcode na may bilis na mas mababa sa 1.0x. Hindi gumagamit ang client ng direct play, kaya mas mabagal sa real time ang CPU transcoding sa VPS at hindi nito kayang makahabol. Dahilan: hindi suportadong codec o container, subtitle burn-in, o HDR tone-mapping. Para ayusin ito: gumamit ng client na sumusuporta sa direct play, panatilihin ang mga source sa H.264/AAC, gumamit ng text subtitles (SRT) sa halip na image subtitles (PGS/VOBSUB) na nagpipilit ng burn-in, at huwag gumamit ng 4K HDR sa CPU-only na server.
“Walang available na compatible streams.” Karaniwang ganito ang buong mensahe: “Hindi compatible ang client na ito sa media at hindi nagpapadala ang server ng compatible na media format.” Tinanggihan ng client ang source, at hindi rin nagsimulang gumana ang fallback transcode. Dahilan: may sira sa ffmpeg command, hindi mabasa ang file, o hinaharangan ng profile ng user ang video conversion. Para ayusin ito: basahin ang ffmpeg line sa Dashboard → Logs, tiyaking nagpe-play ang file, suriin ang playback permissions ng user kung umaasa ka sa transcoding, at sumubok ng pangalawang client upang matukoy kung browser codec quirks ang problema.
Walang poster ang mga pelikula o mali ang poster. Hindi tumugma ang metadata. Dahilan: wala ang pelikula sa sarili nitong Name (Year) folder, S01 ang pangalan ng season folder sa halip na Season 01, wala sa S01E01 form ang mga episode, o walang year. Para ayusin ito: palitan ang pangalan ayon sa layout sa itaas, pagkatapos ay piliin ang Refresh metadata → Replace all, o gamitin ang Identify sa isang item upang itakda ang tamang TMDB/TVDB entry.
FAQ
Maaari bang mag-transcode ng video ang isang VPS nang walang GPU?
Oo, pero CPU lamang ang gagamitin at magastos ito sa resources. Maaaring maubos ng isang 1080p software transcode ang ilang vCPU, at karaniwang hindi makahabol sa real time ang 4K o HEVC kaya nagbu-buffer ang playback. Mas mabuting iwasan ang transcoding: panatilihin ang library sa H.264/AAC at gumamit ng client apps na sumusuporta sa direct play, para bytes lamang ang i-stream ng VPS. Mag-rent lang ng GPU instance kung talagang kailangan mo ng on-the-fly transcoding.
Bakit walang laman ang Jellyfin library ko pagkatapos ng scan?
Halos palaging permissions ang problema. Tumatakbo ang official jellyfin/jellyfin image bilang user: na itinakda mo (o bilang root), at kung hindi mababasa ng uid na iyon ang mga file, ila-log nito ang Access to the path ... is denied at lalaktawan ang mga ito. Ayusin ang ownership gamit ang chown -R 1000:1000 /mnt/media, idagdag ang execute bit sa mga directory gamit ang 755, at magsagawa ulit ng scan. Suriin din ang parent directory, dahil kung hindi kayang i-traverse ng uid ng container ang /mnt/media mismo, hindi nito maaabot ang mga library folder at mananatiling walang laman ang lahat. Ang ikalawang pinakakaraniwang sanhi ay folder layout na hindi tugma sa inaasahan ng Jellyfin.
Paano ko maa-access ang Jellyfin nang remotely at ligtas?
May dalawang magandang option. Ilagay ito sa likod ng TLS reverse proxy sa isang subdomain para encrypted ang login at stream, at magdagdag ng fail2ban. Huwag kailanman ilantad ang plain port 8096, dahil ipinapadala nito ang password bilang cleartext. O panatilihin itong private at i-access lamang sa VPN; ito ang pinakasimpleng ligtas na option para sa isang household. Ibigay nang direkta sa apps ang public address, dahil ang autodiscovery ay isang local-network broadcast at hindi nito naaabot ang mga client na kumokonekta mula sa internet.
Gaano karaming disk at bandwidth ang kailangan ng isang Jellyfin VPS?
Nakasalalay ang disk sa kalidad: maglaan ng 4-15 GB bawat compressed 1080p film, 20-40 GB bawat remux, at 40-100 GB para sa 4K. Dahil dito, karamihan sa mga library ay nangangailangan ng 2-4 TB block volume. Itinatakda ng direct-play bitrate ang bandwidth: 8-12 Mbps bawat 1080p stream at higit pa para sa 4K. Tiyaking kaya ng port speed mo ang bilang ng sabay-sabay na viewers at subaybayan ang monthly transfer cap. Maglaan ng dagdag na CPU headroom kung plano mong mag-transcode; unahin ang bandwidth kaysa cores kung plano mong gumamit ng direct play.
Legal bang magpatakbo ng Jellyfin sa isang VPS?
Ang Jellyfin mismo ay libre at open-source software, at ganap na legal ang pagpapatakbo nito. Ang mahalaga ay ang content: mag-stream lamang ng media na pagmamay-ari mo o lisensyado kang mag-imbak, gaya ng sarili mong disc rips, recordings, o mga file na may karapatan kang gamitin. Walang media na kasama sa Jellyfin at wala itong paraan upang kumuha ng anumang media; isa itong player para sa library na pagmamay-ari mo na.