SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

VPSでJellyfinを構築して自分の動画を配信する方法

VPSでJellyfinをDocker運用する手順です。block storage、ファイル権限、ダイレクトプレイとCPUトランスコード、外部公開の安全対策を解説します。

構築するもの

VPS 上で Jellyfin メディアサーバーを構築します。コンテナ 1 つ、ボリューム 3 つ、映画や番組を保存する block storage ディスク 1 台を使用し、任意のブラウザーまたは Jellyfin アプリからアクセスできる構成です。インストールには 15 行の compose ファイルを使います。その後に発生する問題の大半は、コンテナが読み取れないファイル権限と、GPU を搭載していない VPS に本来処理できない動画のトランスコードを要求することの 2 つが原因です。このガイドの大部分をこの 2 点に割くのは、サポートチケットの多くがここに集中するためです。

Jellyfin は無料で完全なオープンソースです。アカウントは不要で、有料機能もテレメトリもありません。そのため、2026 年に自分でホスティングする価値のあるもののほぼすべてのリストに掲載されています。自分が所有するメディアを再生します。コンテンツは提供されません。このガイドも、コンテンツの入手方法を扱うものではありません。

トランスコードの実情を知ってから、契約を決める

最初にこれを読んでください。契約すべきサービスが変わる可能性があります。メディアサーバーが再生開始時に行う処理は、主に 2 つです。ダイレクトプレイでは、ファイルをそのままストリーミングします。VPS はディスクからバイト列を読み出してネットワークへ送るだけなので、CPU 使用率はほとんど上がりません。トランスコードでは、動画を再生中に再エンコードします。解像度やコーデックを変更したり、字幕を映像に焼き込んだりする処理であり、CPU に大きな負荷がかかります。

一般的な VPS には GPU がありません。そのため、すべてのトランスコードを CPU と libx264/libx265 で実行することになり、ソフトウェアエンコードには大きな負荷がかかります。1 本の 1080p H.264 トランスコードだけで、共有 vCPU を複数使い切ることがあります。4K や HEVC のトランスコードでは、通常リアルタイム再生に追従できません。その結果、再生が停止し、バッファリングが続きます。Intel iGPU や Nvidia のカードを搭載した自宅のマシンでコストを抑えられるハードウェアトランスコードも、プロバイダーが GPU インスタンスを提供していない限り利用できません。

したがって、VPS での基本方針は トランスコードを避けること です。クライアントがそのまま再生できるコーデックでライブラリを用意します。動画は H.264、音声は AAC または AC3、コンテナは MP4 または MKV が適しています。また、ダイレクトプレイに対応するクライアントアプリを選びます。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倍になるため、上限をはるかに速く消費します。同じサーバーから外向きに送信される他の通信も、同じ上限に算入してください。自分でホストする RustDesk リレーも含まれます。2つのピアが直接接続できない場合、リモートデスクトップセッション全体を転送するためです。

前提条件

  • root または sudo を利用でき、Docker と Compose plugin がインストールされた新しい Ubuntu 24.04 KVM VPS。
  • メディア用の block storage volume。容量はライブラリのサイズに合わせます(以下の容量設計を参照してください)。VPS に付属する小容量の root disk は、映画の保存先には適していません。
  • 公開 HTTPS アクセスを使用する場合は domain name。すべてを非公開にする場合は、同じ VPS 上の WireGuard VPN を使用します。
  • ストリーミングする法的権利を持つメディア、自分でリッピングしたファイル、自分で録画したファイル、または自分が所有するファイル。

ブロックストレージを最初にマウントする

プロバイダーのパネルでボリュームをアタッチし、その後でデバイスを確認してマウントします。lsblk でデバイス名を取得します。/dev/sdb/dev/vdb のような名前ですが、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

/dev/sdb ではなく UUID を使用してマウントします。再起動するとデバイス名の割り当てが変わるためです。これにより、誤ったディスクをフォーマットしたり、マウントしたりする事態を防げます。/etc/fstab に次の 1 行を追加します。

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /mnt/media  ext4  defaults,nofail  0  2
sudo mount -a
df -h /mnt/media

nofail は重要です。これがないと、ブロックボリュームが切り離された場合にサーバーが起動を拒否し、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) は飾りではありません。リメイク作品を区別し、照合処理で正しい作品を取得するために必要です。MoviesShows は別々のトップレベルフォルダーにしてください。それぞれが特定のコンテンツ種別の Jellyfin ライブラリになるため、混在させるとメタデータプロバイダーが正しく処理できません。Jellyfin は写真用の 3 つ目のフォルダーも問題なくインデックス化できます。ただし、専用の写真サーバーと比べると機能は限定的です。アルバムを重視する場合は、PhotoPrism または Immich を実行する専用サーバーを用意し、このサーバーは映画と TV 用にしてください。

権限: ライブラリが空になる最大の原因

多くの人が数時間を費やす原因は、この思い込みです。公式の jellyfin/jellyfin イメージは PUID/PGID 環境変数に対応していません。これらは LinuxServer.io のイメージ(lscr.io/linuxserver/jellyfin)で使用するものです。公式イメージでは、compose の user: キーでユーザーを指定します。省略すると、コンテナは root として実行されます。どちらを使用する場合も、ルールは同じです。コンテナが実行される 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

ディレクトリには、読み取り権限だけでなく 実行 ビット(755x)も必要です。実行ビットがないと、コンテナは名前を一覧表示できても、そのディレクトリに入れません。ライブラリ全体を空にする落とし穴は親ディレクトリです。コンテナの uid にマウントポイント自体を通過する権限がないと、/media/Movies/media/Shows に到達できません。その結果、すべてのライブラリが一度に空になり、ログには Access to the path ... is denied が記録されます。読み取れないメディアフォルダーが1つでもあると、そのフォルダーはログに記録されてスキップされます。そのため、root としてコピーしたファイル群がライブラリから何も表示されないまま消えたように見えます。これが、1つのフォルダーだけを修正するのではなく、再帰的に所有権を変更し、すべてのディレクトリに実行ビットを設定する理由です。

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 の Web ログインは平文の HTTP であるため、8096 番ポートをパブリックインターネットへ公開しません。JELLYFIN_PublishedServerUrl は、サーバーがローカルの自動検出用に通知するアドレスです。これは LAN 内の UDP ブロードキャストであるため、インターネット上のクライアントからは見えません。リモートのクライアントは、アプリに入力した URL を使用します。クライアントに通知するアドレスを設定し、リモートデバイスではその URL を手動で入力してください。

compose ディレクトリから起動します。

docker compose up -d
docker logs -f jellyfin

初回起動: セットアップウィザードとライブラリ

ポートは localhost にバインドされているため、ファイアウォールに穴を開けるのではなく、ノート PC から SSH トンネル経由でウィザードにアクセスします。

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

次に http://localhost:8096 を開きます。ウィザードの指示に従って言語を選択し、強力なパスワードを設定した管理者ユーザーを作成します。このアカウントはサーバーの管理に使用するため、使い捨てのパスワードを再利用しないでください。最初のライブラリを追加します。コンテンツタイプに Movies を選び、/media/Movies を指定します。これはホスト側のパスではなく、コンテナ内のパスです。続けて Shows を選び、/media/Shows を指定します。完了すると、Jellyfin がスキャンを開始します。小規模なライブラリであれば、1〜2 分以内にポスターとタイトルが表示されれば正常です。ライブラリは後から Dashboard → Libraries で追加または編集できます。Scan All Libraries を実行すると、すべてのライブラリを再スキャンできます。

トランスコードを少しでも使用する場合は、Dashboard → Playback → Transcoding を開き、トランスコード一時パスを /cache/transcodes に設定します。これにより、一時データは /config を圧迫せず、キャッシュボリュームに保存されます。ハードウェアアクセラレーションは None のままにします。アクセラレーションに使用できる GPU はありません。

リモートアクセス: TLS リバースプロキシ、または VPN 内に限定

外部から Jellyfin に安全にアクセスする方法は2つあります。避けるべき危険な方法も1つあります。危険なのは、ポート 8096 をそのままインターネットに公開することです。ログイン情報が平文で送信され、数時間以内にポートへの総当たり攻撃を受けます。

方法 A、TLS リバースプロキシ。 Docker アプリケーションで TLS を自動化した Traefik を使うか、Certbot で発行した Let's Encrypt 証明書を設定した nginx を使い、Jellyfin をサブドメインの背後に配置します。Jellyfin はリアルタイム更新に WebSockets を使用するため、プロキシで upgrade ヘッダーを転送する必要があります。Traefik では自動的に処理されます。nginx では明示的に指定し、アップストリームへの接続に 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_PublishedServerUrlhttps:// アドレスに設定します。これにより、ローカルの自動検出で正しい URL が通知され、リモートアプリでは指定したアドレスが使われます。また、ログインへの総当たり攻撃を遅らせるため、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 のテレビ番組は 1 シーズンあたり 15-40 GB です。4K の映画は 1 本あたり 40-100 GB になります。数百本の映画といくつかの番組を収めるライブラリには、2-4 TB のボリュームが必要です。ブロックボリュームは、後から移行するより最初に余裕を持って確保したほうが安価です。

/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 タグは 10.x メジャーバージョン内に収まるため、次のメジャーバージョンへ移行する場合はタグを意図的に編集します。実行前に Jellyfin のリリースノートを確認してください。メジャーバージョンではライブラリスキーマの移行が発生するためです。固定したタグと、バックアップ済みの状態ディレクトリが 1 つあれば、常時稼働するコンテナの構成は整います。これは、セルフホストのエージェントのメモリとスケジュールを再起動後も維持する場合と同じパターンです。

障害パターンと表示される文字列

スキャン後もライブラリが空です。 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 を下回っています。クライアントはダイレクト再生していないため、VPS が CPU でリアルタイムより遅い速度でトランスコードし、再生に追いついていません。原因は、未対応の codec または container、字幕の焼き付け、HDR のトーンマッピングです。修正するには、ダイレクト再生に対応したクライアントへ切り替え、ソースを H.264/AAC に統一します。焼き付けが必要になる image 字幕(PGS/VOBSUB)ではなく、text 字幕(SRT)を使用してください。CPU のみのマシンでは、4K HDR を完全に避けてください。

「互換性のあるストリームがありません。」 完全なメッセージは通常、「このクライアントはメディアに対応しておらず、サーバーも互換性のあるメディア形式を送信していません。」 です。クライアントがソースを拒否し、代替のトランスコードも開始できませんでした。原因は、壊れた ffmpeg コマンド、読み取れないファイル、またはユーザーのプロファイルで動画変換が禁止されていることです。Dashboard → Logs の ffmpeg 行を読み、ファイル自体を再生できることを確認します。トランスコードを使用する場合は、ユーザーの再生権限を確認してください。また、2 つ目のクライアントで試して、ブラウザーの codec に関する問題を切り分けます。

映画のポスターが表示されない、または誤ったポスターが表示されます。 メタデータが一致していません。原因は、映画が専用の Name (Year) フォルダーに入っていないこと、シーズンフォルダーの名前が Season 01 ではなく S01 になっていること、エピソードが S01E01 形式になっていないこと、または年の情報がないことです。修正するには、上記のレイアウトに合わせて名前を変更し、Refresh metadata → Replace all を実行します。1 つの項目だけを修正する場合は、Identify を使用して正しい TMDB/TVDB エントリを指定します。

FAQ

VPS は GPU なしで動画をトランスコードできますか?

はい。ただし CPU のみで処理するため、負荷とコストが大きくなります。1080p のソフトウェアトランスコードを 1 本実行するだけで、複数の 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 にリモートから安全にアクセスするにはどうすればよいですか?

適切な方法は 2 つあります。1 つ目は、サブドメイン上の TLS リバースプロキシの背後に配置し、ログイン情報とストリームを暗号化する方法です。fail2ban も追加してください。平文の port 8096 を公開してはいけません。パスワードが暗号化されずに送信されるためです。2 つ目は、完全に非公開にして VPN 経由でのみアクセスする方法です。家庭で使う場合は、この方法が最も簡単で安全です。アプリには公開アドレスを直接設定してください。自動検出はローカルネットワークのブロードキャストであり、インターネット経由で接続するクライアントには届きません。

Jellyfin 用の VPS には、どの程度のディスク容量と帯域幅が必要ですか?

ディスク容量は画質によって異なります。圧縮された 1080p の映画は 1 本あたり 4-15 GB、remux は 20-40 GB、4K は 40-100 GB を目安にしてください。そのため、多くのライブラリでは 2-4 TB のブロックボリュームが必要になります。帯域幅はダイレクト再生のビットレートで決まります。1080p のストリーム 1 本あたり 8-12 Mbps が必要で、4K ではさらに多くなります。同時視聴者数に対して port speed が十分か確認し、月間転送量の上限も監視してください。トランスコードを予定している場合は CPU に余裕を持たせます。ダイレクト再生を予定している場合は、コア数より帯域幅を優先してください。

VPS で Jellyfin を実行するのは合法ですか?

Jellyfin 自体は無料のオープンソースソフトウェアであり、実行することは完全に合法です。重要なのはコンテンツです。自分が所有している、または保有するライセンスを持つメディアだけをストリーミングしてください。自分で作成したディスクのリッピング、録画、または利用権のあるファイルが対象です。Jellyfin はメディアを同梱しておらず、メディアを入手する手段も提供していません。すでに所有しているライブラリを再生するためのソフトウェアです。