在 VPS 上搭建 Jellyfin:串流您自己的媒体库
用 Docker 在 VPS 上运行 Jellyfin,串流您自己的媒体库:块存储、媒体权限、直接播放与 CPU 转码的取舍,以及安全的远程访问。
您将搭建什么
一台运行在 VPS 上的 Jellyfin 媒体服务器:一个容器、三个卷,加上一块存放电影和剧集的块存储磁盘,任何浏览器或 Jellyfin 应用都能访问。安装本身只是一个十五行的 compose 文件。装好之后所有出问题的地方都来自两处——容器读不到的文件权限,以及让一台没有 GPU 的 VPS 去转码它本不该转码的视频。本指南的大部分篇幅都花在这两点上,因为这里才是工单集中的地方。
Jellyfin 免费且完全开源,没有账户、没有付费墙功能、也没有遥测——这正是它几乎出现在每一份 2026 年值得自托管的清单 上的原因。它播放您拥有的媒体。它不附带任何内容,本指南也与获取任何内容无关。
在租任何东西之前,先认清转码的现实
请先读这一段,因为它会改变您的采购决定。当您按下播放,媒体服务器只做两件事之一。直接播放(direct play)原样串流文件:VPS 从磁盘读取字节并推送到网络上,几乎不消耗 CPU。转码(transcoding)则实时重新编码视频——换分辨率、换编解码器,或把字幕烧进画面——这是纯粹的 CPU 工作。
典型的 VPS 没有 GPU。所以每一次转码都用 libx264/libx265 在 CPU 上进行,而软件编码代价高昂。单个 1080p H.264 转码就能占满好几个共享 vCPU;4K 或 HEVC 转码通常根本跟不上实时速度,于是播放卡顿、无休止地缓冲。硬件转码——就是让家用机器凭借 Intel 核显或 Nvidia 显卡把这件事做得很便宜的那个东西——除非您的服务商租用 GPU 实例,否则您根本用不上。
因此在 VPS 上的整体策略就是:避免转码。 让您的媒体库保持在客户端能原生播放的编解码器里——H.264 视频、AAC 或 AC3 音频,装在 MP4 或 MKV 容器中——并选择能直接播放的客户端应用:Android TV、iOS 和 Roku 上的原生 Jellyfin 应用,加上 Infuse、Kodi 和桌面版 Jellyfin Media Player。这样做,VPS 就永远不会碰 ffmpeg,一台普通的 2 vCPU 机器就能同时串流给好几个人。若打算依赖转码,您就得换一台大得多、贵得多的机器,即便如此,4K 仍然是个糟糕的赌注。
带宽也要算一算,因为这是另一个意外。直接播放按文件自身的码率发送。压缩过的 1080p 文件约为 8-12 Mbps;1080p 蓝光 remux 为 20-30 Mbps;4K HDR 为 40-80 Mbps。三个人同时直接播放 10 Mbps 的文件,就是从您的 VPS 持续上传 30 Mbps。请核对套餐上的两个数字:端口速率(能不能上行推出 30 Mbps?)和每月流量上限。一部两小时、10 Mbps 的电影出站约 9 GB,所以计量为 1 TB/月的额度大约只够一百部出头这样的电影——每天三四部——而一家人看 4K,码率是它的四到八倍,会把额度消耗得快得多。
前提条件
- 一台全新的 Ubuntu 24.04 KVM VPS,具备 root 或 sudo 权限,并已安装 Docker 及 Compose 插件。
- 一块用于存放媒体的块存储卷,容量按您的媒体库规划(见下方容量规划)。VPS 自带的小容量根磁盘不是放电影的地方。
- 若想要公网 HTTPS 访问,需要一个域名;或者,如果您更愿意把整套东西保持私有,可在同一台 VPS 上部署 同一台 VPS 上的 WireGuard VPN。
- 您在法律上有权串流的媒体——您自己的转录、您自己的录制、您拥有的文件。
先挂载块存储
在服务商面板里挂接卷,然后找到并挂载它。用 lsblk 获取设备名——它会是类似 /dev/sdb 或 /dev/vdb 的东西,绝不会是根磁盘。
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 很重要:没有它,一旦块存储卷被卸下,机器就会拒绝启动并掉进应急 shell。这里最大的错误就是对一块已经存有数据的卷运行 mkfs.ext4——那会把它抹掉。只格式化新卷;如果磁盘上已经放着您的媒体库,请直接跳到 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/jellyfin 镜像 不 认 PUID/PGID 环境变量——那两个属于 LinuxServer.io 的镜像(lscr.io/linuxserver/jellyfin)。在官方镜像上,您通过 compose 里的 user: 键来控制运行用户,若省略它,容器就以 root 运行。无论用哪个,规则都一样:容器所运行的 uid/gid 必须能读取并进入每一个媒体目录。
我们将以 uid/gid 1000 运行,也就是标准 Ubuntu 机器上的第一个非 root 用户。确认您自己的值并设置属主:
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目录需要 执行 位(755 里的 x),而不只是读——没有它,容器即使能列出文件夹名,也无法进入该文件夹。会掏空整个库的陷阱在于父目录:如果容器的 uid 无法进入挂载点本身,它就永远到不了 /media/Movies 或 /media/Shows,于是每一个库同时空掉,日志里出现 Access to the path ... is denied。任何一个它读不了的单独媒体文件夹都会被记录并跳过,所以以 root 身份拷进来的一批文件会从库里悄无声息地消失。这正是我们要递归 chown 并给每一个目录都设置执行位,而不是只修一个文件夹的原因。
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 存放整台服务器——账户、库、元数据、观看进度——所以它必须可写,也正是您要备份的对象。/cache 是可丢弃的工作空间。媒体挂载特意设为 :ro(只读):Jellyfin 默认把封面图和元数据存在 /config 下,所以它从不需要写入您的媒体库,而只读还能保护您的文件免遭误删或坏插件破坏。端口被刻意绑定到 127.0.0.1——Jellyfin 的网页登录是明文 HTTP,所以我们绝不把 8096 发布到公网。JELLYFIN_PublishedServerUrl 是服务器为本地自动发现所公布的地址——一个局域网 UDP 广播,所以互联网另一端的客户端根本看不到它,只会使用您在应用里输入的 URL。把它设成应该告知客户端的地址,并预期在远程设备上手动输入该 URL。
在 compose 目录里把它启动起来:
docker compose up -d
docker logs -f jellyfin首次运行:设置向导与您的库
由于端口绑定在本地回环,请通过从笔记本发起的 SSH 隧道来访问向导,而不是在防火墙上开一个洞:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ip现在浏览到 http://localhost:8096。向导会带您走过语言选择,然后创建一个 带强密码的管理员用户——这个账户就是您的服务器,所以不要复用一个随手设的密码。添加您的第一个库:内容类型选 Movies,指向 /media/Movies(容器 内部 的路径,不是宿主机路径),再对 Shows 重复一遍,指向 /media/Shows。完成,Jellyfin 就开始扫描。对一个小库而言,正确的结果是一两分钟内海报和标题陆续填好。之后可在 Dashboard → Libraries 里添加或编辑库,并用 Scan All Libraries 强制重新扫描。
如果您确实要用到任何转码,请打开 Dashboard → Playback → Transcoding,把转码临时路径设为 /cache/transcodes,好让这些反复读写落在缓存卷上,而不是把 /config 撑大。把硬件加速保持为 None——没有 GPU 可供加速。
远程访问:TLS 反向代理,或把它留在 VPN 里
从外部访问 Jellyfin 有两种安全的方式,还有一种要避开的不安全方式。不安全的方式就是把 8096 端口直接发布到互联网:登录信息以明文传输,端口几小时内就会被暴力破解。
方案 A——TLS 反向代理。 把 Jellyfin 放在一个子域名后面,交给 带自动 TLS 的 Traefik 来托管您的 Docker 应用,或者放在 nginx 后面配上 由 Certbot 签发的 Let's Encrypt 证书。Jellyfin 用 WebSocket 做实时更新,所以反向代理必须转发升级(upgrade)头。Traefik 会自动处理;nginx 则需要明确写出来,而且到上游必须用 HTTP/1.1,否则升级永远不会发生:
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 隧道访问 Jellyfin。对一家人来说这是最简单的安全选择——没有证书、没有公网暴露、没有暴力破解面。把容器绑定到隧道地址或本地回环,然后通过 VPN 连接。隧道本身请参见 私有 VPS 的 WireGuard VPN 搭建。
容量规划与备份
按画质而不是文件数量来预估。压缩过的 1080p 电影每部约 4-15 GB;1080p remux 每部 20-40 GB;一季 1080p 剧集 15-40 GB;4K 的任何东西每部 40-100 GB。一个几百部电影加一些剧集的库需要一块 2-4 TB 的卷,而且一次把块存储卷买大一点,比日后迁移更划算。
/config 是整台服务器的状态,所以它是您必须备份的唯一一样东西。对它做快照,或停机后打成 tar 包,并把副本保存在机器之外:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache 和转码文件夹是可丢弃的。/mnt/media 上的媒体请单独备份,或者接受它可以重新转录——考虑到体量,大多数人选择后者。升级就是 docker compose pull && docker compose up -d;上面的 :10 标签保持在 10.x 大版本内,所以迁到下一个大版本是一次刻意的标签修改——动手前请浏览一遍 Jellyfin 的发行说明,因为库的架构迁移发生在大版本上。
故障模式,以及您会看到的字样
扫描后库是空的。 Dashboard → Logs(或 ~/jellyfin/config/log/log_*.log)中的日志显示:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.容器的 uid 读不了那个路径。原因:媒体的属主是 root 或某个不同于您 user: 值的 uid、某个目录缺少执行位,或父挂载点本身对该 uid 不可进入。修法:chown -R 1000:1000 /mnt/media,目录设 755,文件设 644,然后重新扫描。
播放把 CPU 顶满并缓冲。 docker stats jellyfin 显示 CPU 接近 100% 乘以核心数,而 Dashboard → Playback 把该会话列为 Transcode,速度低于 1.0x。客户端没在直接播放,所以 VPS 在以慢于实时的速度做 CPU 转码,节节落后。原因:不受支持的编解码器或容器、字幕烧录,或 HDR 色调映射。修法:换用一个直接播放的客户端、把源保持在 H.264/AAC、使用 文本 字幕(SRT)而不是会强制烧录的 图像 字幕(PGS/VOBSUB),并把 4K HDR 彻底挡在纯 CPU 机器之外。
"No compatible streams are available." 完整信息通常是 "This client isn't compatible with the media and the server isn't sending a compatible media format."。客户端拒绝了源,回退的转码也没能启动。原因:一条出错的 ffmpeg 命令、一个读不了的文件,或用户的配置禁止了视频转换。修法:读 Dashboard → Logs 里的 ffmpeg 那一行、确认文件本身能不能播、如果您依赖转码就检查用户的播放权限,并换第二个客户端来排除浏览器编解码器的怪癖。
电影没有海报或海报错了。 元数据没匹配上。原因:某部电影没放在自己的 Name (Year) 文件夹里、某个季文件夹叫 S01 而不是 Season 01、剧集没用 S01E01 形式,或者少了年份。修法:按上面的布局重命名,然后 Refresh metadata → Replace all,或对单个条目用 Identify 来钉住正确的 TMDB/TVDB 条目。
FAQ
没有 GPU 的 VPS 能转码视频吗?
能,但只能用 CPU,而且代价高。单个 1080p 软件转码就能占满好几个 vCPU,而 4K 或 HEVC 通常跟不上实时速度,于是播放缓冲。取胜的办法是避免转码:把库保持在 H.264/AAC,并使用能直接播放的客户端应用,这样 VPS 只是串流字节而已。只有当您确实需要实时转码时,才去租 GPU 实例。
为什么我的 Jellyfin 库扫描后是空的?
几乎总是权限问题。官方的 jellyfin/jellyfin 镜像以您设定的 user:(或 root)身份运行,如果文件对那个 uid 不可读,扫描就会记录 Access to the path ... is denied 并跳过它们。用 chown -R 1000:1000 /mnt/media 修好属主、给目录加上执行位(755),然后重新扫描——同时也要检查父目录,因为如果容器的 uid 连 /mnt/media 本身都进不去,它就永远到不了库文件夹,一切都会空掉。第二常见的原因是文件夹布局不符合 Jellyfin 的期望。
我该如何安全地远程访问 Jellyfin?
有两个好选择。把它放在子域名上的 TLS 反向代理后面,让登录和串流都加密,并加上 fail2ban——绝不要暴露明文的 8096 端口,那会以明文发送您的密码。或者把它保持完全私有,只通过 VPN 访问,这对一家人来说是最简单的安全选择。把公网地址直接给应用——自动发现是局域网内的广播,所以它到不了从互联网接入的客户端。
Jellyfin VPS 需要多少磁盘和带宽?
磁盘取决于画质:每部压缩过的 1080p 电影预算 4-15 GB、每部 remux 20-40 GB、4K 40-100 GB,所以大多数库需要一块 2-4 TB 的块存储卷。带宽由直接播放的码率决定——每路 1080p 串流 8-12 Mbps,4K 要高得多——所以请确认您的端口速率能承载同时观看的人数,并留意每月流量上限。如果打算转码,就多留些 CPU 余量;如果打算直接播放,就把带宽的优先级放在核心数之上。
在 VPS 上运行 Jellyfin 合法吗?
Jellyfin 本身是免费、开源的软件,运行它完全合法。要紧的是内容:只串流您拥有或获得授权持有的媒体——您自己的碟片转录、录制,或您有权持有的文件。Jellyfin 不附带任何媒体,也不提供任何获取途径;它只是一个用来播放您已经拥有的媒体库的播放器。