SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor

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つのどちらかです。Direct play はファイルをそのままストリーミングします。VPS はディスクからバイト列を読み取り、ネットワークへ送信するだけなので、CPU 使用率はほとんど増えません。Transcoding は動画を再生中に再エンコードします。解像度やコーデックの変更、字幕の焼き付けなどが該当し、CPU に大きな負荷がかかります。

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

したがって、VPS での基本方針は トランスコードを避けること です。クライアントがそのまま再生できるコーデックでライブラリを用意します。動画は H.264、音声は AAC または AC3、コンテナは MP4 または MKV にします。また、Direct play に対応するクライアントアプリを選びます。Android TV、iOS、Roku 向けの Jellyfin 公式アプリに加え、Infuse、Kodi、デスクトップ版 Jellyfin Media Player が利用できます。この構成なら、再生中に VPS で ffmpeg トランスコードを実行する必要がなく、2 vCPU 程度のマシンでも複数人へ同時にストリーミングできます。トランスコードを前提にする場合は、より大容量で高価なマシンが必要です。それでも 4K は避けたほうがよい選択です。

帯域幅も計算してください。ここがもう1つの見落としやすい点です。Direct play では、ファイル本来のビットレートで送信されます。圧縮された 1080p ファイルは 8-12 Mbps、1080p Blu-ray の remux は 20-30 Mbps、4K HDR は 40-80 Mbps です。3人が 10 Mbps のファイルを Direct play すると、VPS から 30 Mbps のアップロードを継続的に送信します。契約プランでは、ポート速度と月間転送量上限を確認してください。ポート速度については、上りで 30 Mbps を送信できるかを確認します。2時間の 10 Mbps の映画1本を送信すると約 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 は、フォルダー名とファイル名を使ってメタデータを照合します。構成を誤ると、映画がポスターのないタイトルなしファイルとして登録されたり、エピソードが別のシリーズに誤って一致したりします。ほとんどの場合、次の 4 つの規則で十分です。各映画は、ファイル名と一致する名前の専用フォルダーに保存し、Name (Year) と命名します(Jellyfin では年は任意ですが、指定すると照合の信頼性が大幅に向上します)。シーズンフォルダーは 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 は写真用の 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

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

docker-compose ファイル

services:
  jellyfin:
    image: jellyfin/jellyfin:12
    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

1 行ずつ説明します。jellyfin/jellyfin:12 は現在の 12.x リリース系列を指定します(2026-10-10 時点では 12.2)。user: "1000:1000" は Jellyfin プロセスの実行に使用する uid/gid を設定します。Docker はマウントしたディレクトリでホスト側の uid/gid を維持するため、これによってファイルの所有者やモードは変更されません。そのため、上記の chown は同じ値に合わせる必要があります。/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 にバインドされているため、ファイアウォールに穴を開けるのではなく、ノートパソコンから 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 を実行すると、すべてのライブラリを再スキャンできます。Jellyfin が取得したアートワークを使って、Halcyon は同じライブラリを歩いて回れる 90 年代のビデオ店として再構成するため、基本機能が動作した後は、こうしたポスターを活用する代替フロントエンドも検討できます。

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

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

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

Option A、TLS リバースプロキシ。 Jellyfin をサブドメインで公開し、Docker アプリケーション用の TLS 自動設定に対応した Traefik の背後に置くか、Certbot が発行した Let's Encrypt 証明書を使用する nginx の背後に置きます。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 を監視し、利用者より先に停止を把握できるようにします。通知先には自己ホスト型の ntfy サーバーを指定します。チェックに失敗すると、翌朝読むメールではなく、スマートフォンのプッシュ通知で知らせを受け取れます。公開インターネット上にログイン機能を置く場合は、443 番ポートだけが応答していると決めつけず、サーバー全体も監査するよい機会です。sudo ss -tlnp には、すべての待ち受けポートと、それを使用するプロセスが表示されます。サーバー上で自分のコードも運用する場合は、open-kritt でリポジトリと依存関係の脆弱性をスキャンできます。これは待ち受けポートではなく、ソースコードを監査します。

Option B、VPN 内に限定。 ポート 8096 はインターネットへ一切公開せず、同じサーバー上で終端する WireGuard トンネル経由でのみ Jellyfin に接続します。家庭で利用する場合、これは最も簡単で安全な選択肢です。証明書も、外部公開も、総当たり攻撃の対象となる範囲も必要ありません。compose ファイルの localhost バインドだけでは不十分です。127.0.0.1 で公開したポートは VPS 自身からのみ到達でき、VPN ピアからは到達できないためです。代わりに、サーバーのトンネルアドレスで公開します(例えば "10.8.0.1:8096:8096"。実際には自身の WireGuard サーバー IP を使用します)。または 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 のボリュームが必要です。後から移行するより、最初に 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 です。上記の :12 tag は 12.x 系列にとどまるため、次の major version へ移行する場合は tag を意図的に編集します。実行前に Jellyfin の release notes を確認してください。major version では library schema の migration が行われるためです。Jellyfin は 12.0 で version number の先頭にあった "10." を廃止しました(2026 年 9 月)。12.0 は従来の 10.12 に相当します。11.x は存在せず、10.11.x が旧方式での最後の系列です。古い compose file にまだ jellyfin/jellyfin:10 と記載されている場合、その環境は 10.11 に固定されています。この tag が最後に更新されたのは 2026-06-06 の 10.11.11 で、12.x を取得することはありません。:12 に変更するだけでは不十分です。12.0 は database schema を変更し、初回起動時に data を書き換えます。先にサーバーを停止し、上記のバックアップを取得してください。そのバックアップだけが復旧手段になります。12.0 は 10.10.7 または任意の 10.11.x release から直接 upgrade できます。ただし、その後は library の完全 scan が必要で、通常より明らかに時間がかかります。固定した tag と、バックアップ済みの state directory が 1 つあれば、常時稼働する container の構成は十分です。これは、self-hosted agent の memory と schedule を reboot 後も維持するための基本パターンでもあります。

スキャン後もライブラリが空の場合に表示される文字列

ライブラリがスキャン後も空の場合

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 トランスコードを実行し、再生に追いついていません。原因は、対応していないコーデックまたはコンテナ、字幕の焼き付け、HDR のトーンマッピングです。対策として、ダイレクト再生に対応したクライアントへ切り替えます。ソースは H.264/AAC のままにします。多くの Jellyfin クライアントでは自力で描画できず、焼き付けが必要になるため、字幕は image 字幕(PGS/VOBSUB)ではなく text 字幕(SRT)を使用します。CPU のみで処理する環境では、4K HDR は完全に避けてください。

「互換性のあるストリームがありません」と表示される場合

完全なメッセージは通常、"このクライアントはメディアと互換性がなく、サーバーも互換性のあるメディア形式を送信していません。" です。クライアントがソースを拒否し、フォールバックのトランスコードも開始できていません。原因は、ffmpeg コマンドの誤り、ファイルを読み取れないこと、またはユーザーのプロファイルで動画変換が禁止されていることです。Dashboard → Logs で ffmpeg の行を確認し、ファイル自体を再生できるか確認します。トランスコードを使用する構成では、ユーザーの再生権限も確認します。ブラウザーのコーデック固有の問題を切り分けるため、別のクライアントでも試します。

映画のポスターがない、または誤っている場合

メタデータが一致していません。原因は、メタデータプロバイダーに登録されたタイトルと一致しないフォルダー名またはファイル名、年がなくリメイク作品や一般的なタイトルの作品を区別できないこと、シーズンフォルダーが S01 ではなく Season 01 という名前になっていること、またはエピソードが S01E01 形式になっていないことです。上記の構成に合わせて名前を変更し、Refresh metadata → Replace all を実行します。単一の項目では 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 はメディアを同梱しておらず、メディアを入手する手段も提供していません。すでに所有しているライブラリを再生するためのソフトウェアです。