VPS 上安裝 Jellyfin 串流自有媒體
學會用 Docker 在 VPS 部署 Jellyfin,設定 block storage 與媒體權限,避開 CPU 轉碼限制,並安全地從瀏覽器或 app 遠端播放自有影片。
建置內容
在 VPS 上建置 Jellyfin 媒體伺服器:使用一個容器、三個 volume,以及存放電影與影集的 block storage 磁碟,讓您可從任何瀏覽器或 Jellyfin app 存取。安裝只需 15 行的 compose 檔案。之後發生的問題大多來自兩個原因:容器無法讀取檔案權限,以及要求沒有 GPU 的 VPS 轉碼不適合由它處理的影片。本指南大部分篇幅都用於這兩個問題,因為支援工單主要都集中在這裡。
Jellyfin 免費且完全開放原始碼,不需要帳戶,沒有付費功能,也不會蒐集遙測資料,因此幾乎每份 2026 年值得自行代管的服務清單都會列出它。它只能播放您擁有的媒體。Jellyfin 不提供任何內容,本指南也不涵蓋取得媒體的方式。
租用任何服務前,先了解轉碼的實際限制
請先閱讀本節,因為它會影響你的選擇。媒體伺服器在你按下播放時,會採用以下其中一種方式。直接播放會原樣串流檔案:VPS 從磁碟讀取位元組,再透過網路傳送,幾乎不消耗 CPU。轉碼則會即時重新編碼影片,例如變更解析度、轉換編解碼器或將字幕燒錄到影片中,這完全依賴 CPU 運算。
一般 VPS 沒有 GPU。因此所有轉碼都會使用 CPU 搭配 libx264/libx265 執行,而軟體編碼的成本很高。單一 1080p H.264 轉碼就可能耗盡數個共用 vCPU;4K 或 HEVC 轉碼通常完全無法跟上即時播放速度,導致播放停頓並持續緩衝。Intel iGPU 或 Nvidia 顯示卡上的硬體轉碼,能讓家用設備以低成本處理這些工作;但除非服務供應商提供 GPU 執行個體,否則 VPS 無法使用這項功能。
因此,在 VPS 上採用的整體策略是:避免轉碼。 將媒體庫維持在用戶端原生支援的編解碼格式,例如 H.264 影片、AAC 或 AC3 音訊,並使用 MP4 或 MKV 容器。同時選擇支援直接播放的用戶端應用程式,例如 Android TV、iOS 和 Roku 的原生 Jellyfin 應用程式,以及 Infuse、Kodi 和桌面版 Jellyfin Media Player。這樣一來,VPS 就不需要執行 ffmpeg;一台配置適中的 2 vCPU VPS 也能同時為數名使用者串流。若計畫進行轉碼,就需要更大且更昂貴的 VPS,即使如此,4K 仍然不是理想選擇。
也要計算頻寬,因為這是另一個容易忽略的問題。Direct play 會以檔案本身的位元率傳送資料。壓縮後的 1080p 檔案約為 8-12 Mbps;1080p Blu-ray remux 約為 20-30 Mbps;4K HDR 則為 40-80 Mbps。3 個人同時 direct play 10 Mbps 的檔案,會持續佔用 VPS 30 Mbps 的上傳頻寬。請查看方案中的 2 個數值:連接埠速度(上行是否能推送 30 Mbps?)以及每月傳輸量上限。1 部 2 小時、10 Mbps 的影片,輸出流量約為 9 GB。因此,按流量計費且每月提供 1 TB 的方案,每月只能支援略多於 100 部這類影片,也就是每天 3 或 4 部;如果家中觀看 4K 內容,其位元率是上述的 4 到 8 倍,流量會消耗得快得多。同一台主機上任何其他會產生輸出流量的服務,也要計入相同的流量預算,包括 自架的 RustDesk relay;當 2 個對等端無法直接連線時,該服務會承載完整的遠端桌面工作階段。
先決條件
- 全新的 Ubuntu 24.04 KVM VPS,具備 root 或 sudo 權限,並已安裝 Docker 與 Compose plugin。
- 用於媒體檔案的 block storage volume,容量應符合您的媒體庫需求(請參閱下方的容量規劃)。VPS 隨附的小型 root disk 不適合存放影片。
- 如果需要公開的 HTTPS 存取,請準備網域名稱;如果希望整體維持私有,則可在同一台 VPS 上使用 WireGuard VPN。
- 您依法有權串流的媒體,包括您自行擷取的內容、自行錄製的內容,以及您擁有的檔案。
先掛載 block storage
先在供應商的控制台中連接 volume,再找出該磁碟並掛載。從 lsblk 取得裝置名稱,結果會類似 /dev/sdb 或 /dev/vdb,不會是 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 device請使用 UUID 掛載,不要使用 /dev/sdb。因為裝置代號可能在重新開機後重新排序,導致你格式化或掛載錯誤的磁碟。在 /etc/fstab 中加入一行:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail 很重要:如果 block volume 被卸離,沒有這項設定時,主機會無法開機並進入 emergency shell。最常見且影響最大的錯誤,是對已經包含資料的 volume 執行 mkfs.ext4,這會清除其中的資料。只有新的 volume 才需要格式化;如果磁碟中已經有你的資料庫,請直接跳到 fstab 那一行。
依 Jellyfin 預期的方式配置媒體
Jellyfin 會依資料夾與檔案名稱比對中繼資料。配置錯誤時,電影可能會以沒有標題且沒有海報的檔案出現,或讓劇集分集對應到錯誤的影集。規則只有三項:每部電影都必須放在獨立的 Name (Year) 資料夾中,且檔名必須一致;季資料夾應命名為 Season 01,不可使用 S01;分集檔案應使用 S01E01;特別篇則放在 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電影的 (Year) 不是裝飾,而是用來區分翻拍版本,讓比對器取得正確的片名。請將 Movies 與 Shows 保持為不同的頂層資料夾,因為兩者會分別成為 Jellyfin 中特定內容類型的媒體庫;混放會讓中繼資料提供者產生混淆。Jellyfin 也能正常索引第三個相片資料夾,但與專用相片伺服器相比,功能較為有限。因此,如果相簿很重要,請讓它們使用獨立主機執行 PhotoPrism 或 Immich,並讓這台主機專門處理電影與電視節目。
權限:媒體庫顯示空白的首要原因
以下是最容易讓人浪費一個晚上的誤解。官方的 jellyfin/jellyfin image 不會使用 PUID/PGID 環境變數;這些變數屬於 LinuxServer.io image(lscr.io/linuxserver/jellyfin)。在官方 image 中,您可在 compose 內使用 user: 欄位指定使用者;若省略此欄位,container 會以 root 身分執行。無論採用哪種方式,規則都相同:container 執行時使用的 uid/gid 必須能讀取並進入每個媒體目錄。
我們會使用 uid/gid 1000,這是標準 Ubuntu 系統上的第一個非 root 使用者。請確認您的 uid/gid,並設定擁有權:
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目錄需要 execute 位元(即 755 中的 x),不能只有讀取權限。缺少此位元時,container 即使能列出目錄名稱,也無法進入該目錄。導致整個媒體庫變空的陷阱在於父目錄:如果 container 的 uid 無法進入掛載點本身,就永遠到不了 /media/Movies 或 /media/Shows,而且每個媒體庫都會同時變成空白,log 中則會出現 Access to the path ... is denied。只要有任何媒體資料夾無法讀取,系統就會記錄錯誤並略過該資料夾。因此,以 root 複製進去的一批檔案可能會無聲無息地從媒體庫消失。這就是我們要遞迴執行 chown,並為每個目錄設定 execute 位元,而不是只修正單一資料夾的原因。
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逐行說明:user: "1000:1000" 實際設定檔案權限,並與上述擁有者一致。/config 存放整個伺服器、帳號、函式庫、metadata 及監看狀態,因此必須可寫入,也是需要備份的內容。/cache 是可捨棄的工作空間。媒體掛載點刻意設為 :ro(唯讀):Jellyfin 預設會將 artwork 與 metadata 存放在 /config 下,因此不需要寫入您的媒體庫;唯讀設定也能防止誤刪檔案或有問題的 plugin 造成損害。連接埠刻意繫結至 127.0.0.1,因為 Jellyfin 的 Web 登入使用純 HTTP,因此不會將 8096 公開至網際網路。JELLYFIN_PublishedServerUrl 是伺服器用於本機自動探索的廣播位址,屬於 LAN UDP 廣播,因此網際網路上的用戶端看不到這個位址,只會使用您在應用程式中輸入的 URL。請將它設為應告知用戶端的位址,並預期需要在遠端裝置上手動輸入該 URL。
從 compose 目錄啟動:
docker compose up -d
docker logs -f jellyfin首次執行:設定精靈與媒體庫
由於連接埠綁定至 localhost,請從筆記型電腦建立 SSH tunnel 來開啟設定精靈,不要為此開放防火牆連接埠:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ip現在瀏覽至 http://localhost:8096。設定精靈會先引導您選擇語言,接著建立具有強密碼的 admin 使用者。此帳號可管理您的伺服器,因此不要重複使用臨時密碼。新增第一個媒體庫:將內容類型選為 Movies,指定 /media/Movies(這是容器內的路徑,不是主機路徑),再以 Shows 指定 /media/Shows。完成後,Jellyfin 會開始掃描。若媒體庫較小,正常結果應是在 1 或 2 分鐘內顯示海報與標題。之後可在 Dashboard → Libraries 新增或編輯媒體庫,並使用 Scan All Libraries 強制重新掃描。
只要有使用任何轉碼功能,請開啟 Dashboard → Playback → Transcoding,並將轉碼暫存路徑設為 /cache/transcodes,讓大量暫存資料寫入 cache volume,而不是讓 /config 持續膨脹。硬體加速請維持 None,因為沒有 GPU 可供加速。
遠端存取:TLS 反向代理,或維持在 VPN 內
從外部存取 Jellyfin 有兩種安全方式,另有一種不安全方式應避免。不要直接將 8096 埠發布到網際網路:登入資料會以明文傳送,該埠也會在數小時內遭到暴力破解。
選項 A:TLS 反向代理。 將 Jellyfin 放在子網域後方,使用 為 Docker 應用程式自動設定 TLS 的 Traefik,或使用 nginx 搭配 由 Certbot 核發的 Let's Encrypt 憑證。Jellyfin 使用 WebSockets 傳送即時更新,因此代理必須轉送 upgrade 標頭。Traefik 會自動處理;nginx 則需要明確設定這些標頭,並對 upstream 使用 HTTP/1.1,否則 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";
}將 JELLYFIN_PublishedServerUrl 設為 https:// 位址,讓本機自動探索功能發布正確的 URL,並讓遠端應用程式使用你提供的位址;另外加入 fail2ban 以減緩針對登入的暴力破解嘗試。伺服器公開後,將 Uptime Kuma 指向該 URL,這樣你會在觀眾發現服務中斷前收到通知。
選項 B:透過 VPN 維持私有。 完全不要發布 8096;僅透過終止於同一台主機上的 WireGuard tunnel 存取 Jellyfin。對家庭使用情境而言,這是最簡單的安全選擇,不需要憑證,也不會對外公開或形成暴力破解攻擊面。將容器繫結至 tunnel 位址或 localhost,並透過 VPN 連線。tunnel 本身請參閱 用於私有 VPS 的 WireGuard VPN 設定。
儲存空間規劃與備份
應依畫質而非檔案數量編列容量。壓縮後的 1080p 電影每部約 4-15 GB;1080p remux 每部約 20-40 GB;1080p 電視節目每季約 15-40 GB;任何 4K 電影每部約 40-100 GB。數百部電影加上部分電視節目的媒體庫,需要 2-4 TB 的 volume。一次預留較大的 block volume,通常比日後遷移更便宜。
/config 包含整台伺服器的狀態,因此這是必須備份的項目。建立 snapshot,或停止服務後使用 tar 封存,並將副本存放在該主機之外:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache 與 transcode 資料夾都可捨棄。/mnt/media 上的媒體可另外備份,或接受日後重新擷取;考量儲存容量,多數人會選擇後者。升級使用 docker compose pull && docker compose up -d;上方的 :10 tag 會維持在 10.x major 版本內,因此移至下一個 major 版本時,需刻意修改 tag。執行前先查看 Jellyfin release notes,因為 major 版本可能會執行 library schema migration。固定 tag 加上一個已備份的 state directory,就是任何常駐 container 的完整做法;這也是 讓自架 agent 的記憶與排程在重新開機後持續存在 所採用的相同模式。
失敗模式與你會看到的訊息
掃描後媒體庫是空的。 Dashboard → Logs(或 ~/jellyfin/config/log/log_*.log)中的日誌顯示:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.容器的 uid 無法讀取該路徑。原因可能是媒體檔案由 root 或不同於 user: 值的 uid 擁有、目錄缺少 execute 位元,或父層掛載點本身無法由該 uid 遍歷。修正方式:chown -R 1000:1000 /mnt/media、目錄 755、檔案 644,然後重新掃描。
播放時 CPU 使用率飆高並持續緩衝。 docker stats jellyfin 顯示 CPU 使用率接近核心數乘以 100%,而 Dashboard → Playback 將工作階段列為 Transcode,速度低於 1.0x。用戶端未進行 direct-play,因此 VPS 正在以低於即時速度進行 CPU 轉碼,播放進度會持續落後。原因可能是不支援的編解碼器或容器格式、字幕燒錄,或 HDR 色調映射。修正方式:改用支援 direct-play 的用戶端,將來源維持為 H.264/AAC,使用文字字幕(SRT),不要使用會強制燒錄的影像字幕(PGS/VOBSUB),並避免在僅使用 CPU 的主機上播放 4K HDR。
「沒有相容的串流可用。」 完整訊息通常是 「此用戶端不相容於該媒體,且伺服器未傳送相容的媒體格式。」 用戶端拒絕了來源,而備援轉碼也未能啟動。原因可能是 ffmpeg 命令錯誤、檔案無法讀取,或使用者的設定檔禁止影片轉換。修正方式:讀取 Dashboard → Logs 中的 ffmpeg 行,確認檔案本身可以播放;如果依賴轉碼,請檢查使用者的播放權限;並嘗試第二個用戶端,以排除瀏覽器編解碼器差異。
電影沒有海報或海報錯誤。 Metadata 比對失敗。原因可能是電影未放在自己的 Name (Year) 資料夾中、季資料夾命名為 S01 而非 Season 01、集數未採用 S01E01 格式,或缺少年份。修正方式:依照上述配置重新命名,然後選取 Refresh metadata → Replace all;或者對單一項目使用 Identify,指定正確的 TMDB/TVDB 項目。
FAQ
VPS 可以在沒有 GPU 的情況下轉碼影片嗎?
可以,但只能使用 CPU,而且成本很高。單一 1080p 軟體轉碼就可能耗盡數個 vCPU;4K 或 HEVC 通常無法跟上即時播放速度,因此播放會緩衝。最有效的方法是避免轉碼:將媒體庫維持為 H.264/AAC,並使用支援 direct-play 的用戶端應用程式,讓 VPS 只傳輸位元組。只有在確實需要即時轉碼時,才租用 GPU instance。
為什麼 Jellyfin 掃描後媒體庫是空的?
幾乎都是權限問題。官方 jellyfin/jellyfin image 會以你設定的 user:(或 root)執行;如果該 uid 無法讀取檔案,掃描日誌會記錄 Access to the path ... is denied 並略過這些檔案。使用 chown -R 1000:1000 /mnt/media 修正擁有者,為目錄加入執行權限位元(755),然後重新掃描。同時檢查上層目錄,因為如果容器的 uid 無法遍歷 /mnt/media 本身,就無法到達媒體庫目錄,結果會完全顯示為空。第二常見的原因是目錄結構不符合 Jellyfin 的預期。
如何安全地從遠端存取 Jellyfin?
有兩個可行方法。將它放在子網域上的 TLS reverse proxy 後方,讓登入資料與串流內容都經過加密,並加入 fail2ban。絕對不要公開 plain port 8096,因為這會以明文傳送密碼。另一種方法是完全維持私有,只透過 VPN 存取;對家庭使用情境而言,這是最簡單且安全的選擇。請直接將公開位址提供給應用程式,因為 autodiscovery 是區域網路廣播,無法到達從網際網路連入的用戶端。
Jellyfin VPS 需要多少磁碟空間與頻寬?
磁碟需求取決於畫質:每部壓縮的 1080p 電影預留 4-15 GB,每部 remux 預留 20-40 GB,4K 則預留 40-100 GB。因此,多數媒體庫需要 2-4 TB 的 block volume。頻寬取決於 direct-play bitrate;每個 1080p 串流約需 8-12 Mbps,4K 需要更多。請確認連接埠速度足以應付同時觀看的人數,並留意每月傳輸量上限。若計畫轉碼,請預留 CPU 效能;若計畫使用 direct-play,應優先配置頻寬,而不是增加核心數。
在 VPS 上執行 Jellyfin 合法嗎?
Jellyfin 本身是免費的 open-source software,執行它完全合法。關鍵在於內容:只能串流你擁有或取得授權持有的媒體,例如自己的光碟轉檔、錄製內容,或你有權使用的檔案。Jellyfin 不提供任何媒體,也不提供取得媒體的方式;它只是播放你已擁有之媒體庫的播放器。