VPSでJellyfinをDocker構築する方法と注意点
VPS上にDockerでJellyfinを構築し、自分専用のメディアサーバーを作る方法を解説します。特に注意すべき、ファイルのpermissionエラーや、GPUなしのVPSで発生するCPU負荷の高いtranscoding問題への対策について、具体的な回避策をまとめました。
作成するもの
VPS上にJellyfinメディアサーバーを構築します。構成は、1つのcontainer、3つのvolume、そして映画や番組を保存するblock-storage diskです。これらは任意のbrowserやJellyfin appからアクセス可能です。インストールは15行のcompose fileで行います。構築後に発生するトラブルは、主に2つの原因に集約されます。1つはcontainerが読み取れないfile permissions、もう1つはGPUのないVPSに不適切なvideo transcodingを要求することです。本ガイドでは、サポートへの問い合わせが最も多いこれら2つの問題に重点を置いて解説します。
Jellyfinは完全なオープンソースのフリーウェアです。アカウント作成、有料機能、telemetryは一切ありません。そのため、2026年にself-hostingすべきもののリストに必ずと言っていいほど含まれています。Jellyfinは所有しているメディアを再生するためのものです。コンテンツ自体は提供しません。本ガイドはコンテンツの入手方法については扱いません。
レンタル前に知っておくべきトランスコードの実態
購入前に必ず読んでください。選択肢が変わります。再生ボタンを押したとき、メディアサーバーは2つのうちどちらかの動作をします。Direct playはファイルをそのままストリームします。VPSはディスクからバイトデータを読み取り、そのまま転送するため、CPU負荷はほとんどありません。Transcodingは、解像度、コーデックの変更、または字幕の焼き付けのために、リアルタイムでビデオを再エンコードします。これには膨大なCPUリソースが必要です。
一般的なVPSにはGPUが搭載されていません。そのため、すべてのトランスコードは libx264/libx265 を使用してCPUで実行され、ソフトウェアエンコーディングは負荷が高くなります。1080p H.264のトランスコード1つで、共有vCPUが複数飽和することがあります。4KやHEVCのトランスコードは、通常リアルタイムでの処理が間に合わず、再生が停止してバッファリングが続きます。Intel iGPUやNvidiaカードを搭載したホームサーバーでは安価に行えるハードウェアトランスコーディングは、プロバイダーがGPUインスタンスを提供していない限り、VPSでは利用できません。
したがって、VPSにおける戦略は transcodingを避けること です。ライブラリは、クライアントがネイティブで再生できるコーデック(H.264ビデオ、AACまたはAC3オーディオ、MP4またはMKVコンテナ)で保存してください。また、Direct playが可能なクライアントアプリ(Android TV、iOS、Roku用のネイティブJellyfinアプリ、およびInfuse、Kodi、デスクトップ版Jellyfin Media Player)を選択してください。これを守れば、VPSがffmpegを使用することはありません。控えめな2 vCPUのサーバーでも、同時に複数人へのストリーミングが可能です。トランスコードを前提に計画を立てると、より大型で高価なサーバーが必要になり、それでも4Kの再生は困難です。
もう一つの注意点は帯域幅の計算です。Direct playはファイル本来のビットレートで送信されます。圧縮された1080pファイルは8-12 Mbps、1080p Blu-ray remuxは20-30 Mbps、4K HDRは40-80 Mbpsです。10 Mbpsのファイルを3人がDirect playで視聴する場合、VPSからは継続的に30 Mbpsのアップロードが発生します。プランの2つの数値を確認してください。ポート速度(30 Mbpsのアップストリームが可能か)と、月間の転送容量制限です。10 Mbpsの2時間の映画は、約9 GBの転送量になります。月間1 TBの制限がある場合、月に約100本(1日あたり3〜4本)の視聴で上限に達します。4Kを視聴する場合、ビットレートが4〜8倍になるため、さらに早く容量を消費します。
前提条件
- root または sudo 権限を持ち、Docker と Compose plugin がインストールされた、新規の Ubuntu 24.04 KVM VPS。
- ライブラリのサイズに合わせたメディア用ブロックストレージボリューム(以下のサイズ計算を参照)。VPS 標準の小さなルートディスクには、動画ファイルを保存しないでください。
- 公開 HTTPS アクセスが必要な場合はドメイン名。プライベート環境を維持したい場合は 同じ VPS 上の WireGuard VPN。
- 法的にストリーミングが許可されているメディア(自身で作成した rip ファイル、録画データ、所有しているファイル)。
最初にブロックストレージをマウントする
プロバイダーのパネルでボリュームをアタッチし、デバイスを特定してマウントします。デバイス名は 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/dev/sdb ではなく UUID でマウントしてください。再起動時にデバイス名が変わるため、誤ったディスクをフォーマットしたりマウントしたりする恐れがあります。/etc/fstab に以下の行を追加します。
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail は重要です。これがないと、ボリュームがデタッチされた際にシステムが起動せず、emergency shell に落ちます。最も多いミスは、データが入っているボリュームに対して mkfs.ext4 を実行することです。データが消去されます。新しいボリュームのみをフォーマットしてください。すでにライブラリが存在する場合は、fstab の行の追加のみを行ってください。
Jellyfinの仕様に合わせたメディアの配置
Jellyfinはフォルダ名とファイル名でメタデータを照合します。配置が正しくないと、映画がタイトルなしのポスター未設定ファイルになったり、エピソードが別のシリーズと誤認されたりします。ルールは以下の3点のみです。各映画は、ファイル名と一致する独自のName (Year)フォルダに格納してください。シーズンフォルダの名前はS01ではなくSeason 01にします。エピソードファイルには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のライブラリになるため、混在させずに独立したトップレベルフォルダとして維持してください。混在させるとメタデータプロバイダーが混乱します。
Permissions: ライブラリが空になる最大の原因
多くの人が陥る誤解があります。公式の jellyfin/jellyfin イメージは PUID/PGID 環境変数を使用しません。これらは LinuxServer.io のイメージ (lscr.io/linuxserver/jellyfin) 用のものです。公式イメージでは、compose 内の user: キーでユーザーを制御します。これを省略すると、コンテナは root として実行されます。どちらのイメージを使用する場合でもルールは同じです。コンテナの uid/gid は、すべてのメディアディレクトリを読み取り、トラバース(通過)できる必要があります。
ここでは、標準的な Ubuntu 環境の最初の非 root ユーザーである uid/gid 1000 として実行します。ご自身の環境を確認し、所有権を設定してください:
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 と表示され、ライブラリが突然空になります。読み取れないメディアフォルダが1つでもあると、そのフォルダはログに記録されてスキップされます。そのため、root 権限でコピーされた大量のファイルが、ライブラリから静かに消えてしまいます。これが、1つのフォルダを修正するのではなく、再帰的に chown を行い、すべてのディレクトリに実行ビットを設定すべき理由です。
The 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.com各行の解説: user: "1000:1000" は、上記の所有権と一致するように、実際のファイル権限を設定します。/config は、アカウント、ライブラリ、メタデータ、監視状態を含むサーバー全体を保持します。そのため、書き込み権限が必要であり、バックアップの対象となります。/cache は、一時的な作業領域です。メディアのマウントは、意図的に :ro (read-only) に設定しています。Jellyfin はデフォルトで /config にアートワークやメタデータを保存するため、ライブラリへの書き込みは不要です。read-only にすることで、誤操作や不具合のあるプラグインによるファイルの削除を防ぎます。ポートは意図的に 127.0.0.1 にバインドしています。Jellyfin の Web ログインは HTTP であるため、8096 をパブリックインターネットに公開することはありません。JELLYFIN_PublishedServerUrl は、ローカルの自動検出に使用されるアドレスです。これは LAN 内の UDP ブロードキャストであるため、インターネット経由のクライアントには表示されません。クライアントは入力された URL を使用します。この値は、クライアントに通知すべきアドレスに設定してください。リモートデバイスでは、その URL を手動で入力する必要があります。
compose ディレクトリから起動します:
docker compose up -d
docker logs -f jellyfin初回実行: セットアップウィザードとライブラリ
ポートが localhost にバインドされているため、ファイアウォールの設定を変更する代わりに、ノートPCから SSH tunnel を使用してウィザードにアクセスしてください。
ssh -L 8096:127.0.0.1:8096 you@your-vps-ip次に http://localhost:8096 にアクセスします。ウィザードに従って言語を選択し、次に強力なパスワードを持つ admin user を作成します。このアカウントはサーバーの管理者権限を持つため、使い捨てのパスワードは使用しないでください。最初のライブラリを追加します。content type に Movies を選択し、/media/Movies(ホストのパスではなく、コンテナ内のパス)を指定します。同様の手順で、Shows を /media/Shows に指定して追加します。完了すると Jellyfin がスキャンを開始します。ライブラリが小さい場合、1〜2分以内にポスターやタイトルが表示されれば成功です。ライブラリの追加や編集は、後で Dashboard → Libraries から行えます。再スキャンが必要な場合は Scan All Libraries を実行してください。
トランスコードを使用する場合は、Dashboard → Playback → Transcoding を開き、transcode temp path を /cache/transcodes に設定してください。これにより、書き込み負荷が /config で膨らむのを防ぎ、cache volume に集約できます。ハードウェアアクセラレーションは None のままにしてください。アクセラレーションに使用する GPU は搭載されていません。
リモートアクセス: TLSリバースプロキシ、またはVPNの利用
外部からJellyfinにアクセスする方法は、安全な方法が2つと、避けるべき危険な方法が1つあります。危険な方法は、port 8096をインターネットに直接公開することです。この場合、ログイン情報がクリアテキストで送信され、数時間以内にポートへのブルートフォース攻撃を受ける可能性があります。
オプションA — TLSリバースプロキシ Dockerアプリに自動TLSを適用できるTraefikのサブドメイン、またはCertbotで発行されたLet's Encrypt証明書を使用したnginxの背後にJellyfinを配置します。Jellyfinはリアルタイム更新にWebSocketsを使用するため、プロキシは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";
}ローカルの自動検出が正しいURLを通知できるように、JELLYFIN_PublishedServerUrlをhttps://のアドレスに設定してください。リモートアプリは指定されたアドレスを使用します。また、ログインに対するブルートフォース攻撃対策としてfail2banを追加してください。サーバーを公開した後は、Uptime KumaをそのURLに向けて、視聴者よりも早くダウンタイムを検知できるようにしてください。
オプションB — VPNを使用してプライベートに維持する 8096を一切公開しないでください。同じマシンで終端するWireGuardトンネル経由でのみJellyfinにアクセスします。家庭環境では、これが最も簡単な安全な選択肢です。証明書も、公開露出も、ブルートフォースの攻撃対象も不要です。コンテナをトンネルのアドレスまたはlocalhostにバインドし、VPN経由で接続してください。トンネルの設定については、プライベートVPS向けのWireGuard VPN設定を参照してください。
ストレージ容量の算出とバックアップ
ファイル数ではなく、品質に基づいて予算を算出してください。圧縮された1080pの映画は1ファイルあたり4-15 GB、1080pのremuxは20-40 GB、1080pのTVシリーズ1シーズンは15-40 GB、4Kコンテンツは1ファイルあたり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 と transcode フォルダは破棄可能です。/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 である、ディレクトリの 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 トランスコードを行っており、再生が追いついていません。原因:サポートされていないコーデックまたはコンテナ、字幕の burn-in、または HDR tone-mapping。解決策:direct-play 対応のクライアントを使用する、ソースを H.264/AAC に保つ、burn-in を強制する image 字幕 (PGS/VOBSUB) ではなく text 字幕 (SRT) を使用する、CPU のみの環境では 4K HDR を一切使用しない。
"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) フォルダに入っていない、シーズンフォルダの名前が Season 01 ではなく S01 である、エピソードが S01E01 形式ではない、または年情報が不足している。解決策:上記のレイアウトに従ってリネームし、Refresh metadata → Replace all を実行する。または、単一のアイテムに対して Identify を使用して正しい TMDB/TVDB エントリを割り当てる。
FAQ
GPUなしでVPSでビデオのトランスコードは可能ですか?
可能です。ただしCPUのみを使用するため、コストが高くなります。1080pのソフトウェアトランスコード1件で複数のvCPUが占有されることがあります。4KやHEVCではリアルタイム処理が追いつかず、再生バッファが発生することが一般的です。回避策として、ライブラリをH.264/AAC形式で保持し、direct-playに対応したクライアントアプリを使用してください。これにより、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にリモートから安全にアクセスする方法は?
2つの推奨される方法があります。1つ目は、サブドメインにTLSリバースプロキシを設置してログインとストリーミングを暗号化し、fail2banを追加する方法です。ポート8096を直接公開しないでください。パスワードがクリアテキストで送信されます。2つ目は、完全にプライベートな環境に保ち、VPN経由でのみアクセスする方法です。家庭内ではこれが最も簡単で安全な選択肢です。アプリにはパブリックアドレスを直接指定してください。autodiscoveryはローカルネットワーク内のブロードキャストであるため、インターネット経由のクライアントには届きません。
JellyfinのVPSに必要なディスク容量と帯域幅は?
ディスク容量は画質に依存します。圧縮された1080pの映画1本につき4-15 GB、remuxは20-40 GB、4Kは40-100 GBを目安にしてください。そのため、多くのライブラリでは2-4 TBのブロックボリュームが必要になります。帯域幅はdirect-playのビットレートによって決まります。1080pストリーム1つにつき8-12 Mbps、4Kはそれよりも大幅に多くなります。ポート速度が同時視聴者数に耐えられるか、また月間の転送量制限を確認してください。トランスコードを行う予定がある場合はCPUに余裕を持たせてください。direct-playを前提とする場合は、コア数よりも帯域幅を優先してください。
VPSでJellyfinを運用することは合法ですか?
Jellyfin自体は無料のオープンソースソフトウェアであり、その運用は完全に合法です。重要なのはコンテンツの内容です。自身が所有しているか、ライセンスを持っているメディアのみをストリーミングしてください。これには、自身のディスクからのリップ、録画、または権利を持つファイルが含まれます。Jellyfin自体はメディアを同梱しておらず、メディアを取得する方法も提供していません。Jellyfinは、ユーザーが既に所有しているライブラリのためのプレイヤーです。