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

VPSでDockerを使うと何が変わる?注意点4つ

VPSのDockerは同じengineですが、RAM不足、公開portがUFWを通過、reboot後にcontainerが停止、disk枯渇が起きます。実際のerror stringと対策を確認できます。

VPS で Docker を実行すると何が変わるか

VPS 上の Docker は、ノート PC 上の Docker と同じ engine と同じ image を使用するため、すでに知っているコマンドはすべてそのまま動作します。変わるのは、Docker を取り巻く余裕です。ノート PC には余剰メモリがあり、誰もスキャンしていない firewall があり、容量を確認する必要がないほど大きなディスクがあります。レンタルサーバーには固定のメモリ上限があり、起動から数分以内にスキャンされる公開 IP アドレスがあり、Docker が確認なしに容量を消費する root filesystem があります。

小規模なサーバーで問題の大半を引き起こす違いは、次の 4 つです。

  • メモリには上限があり、kernel はメモリ不足を解消するために process を強制終了します。
  • 公開した port は UFW(Uncomplicated Firewall)をそのまま通過します。Docker が独自の firewall rule を書き込むためです。
  • 事前に指定しない限り、reboot 後に container は再起動しません。
  • image、container、volume、build cache は、ディスクが満杯になるまで増え続けます。

以下の各セクションでは、障害の内容、実際に表示される文字列、その問題を詳しく解決する guide を示します。まだ compose file を作成していない場合は、先に VPS での Docker Compose の基本 を読み、その後ここに戻ってください。このページでは、すでに stack を起動できることを前提とします。

Docker コンテナはどの程度の RAM を使用しますか?

多くの場合、想定より少ない量です。コンテナは仮想マシンではなく、cgroup(control group)内で実行されるプロセスです。そのため、ゲストカーネルはなく、固定のメモリ割り当てもありません。使用量は、コンテナ内のプロセスが実際に使用する分だけです。同じスタックを仮想マシンで構築した場合には収まらない構成でも、コンテナなら 2 GB に収まるのはそのためです。

以下の数値は、Ubuntu 24.04 上でデフォルト設定を使用した標準イメージについて、起動数分後に docker stats から読み取った一般的なアイドル時の値です。これは計画時の出発点であり、実際のワークロードのベンチマークではありません。以下の数値を含め、どの数値もそのまま信頼する前に、自分の環境で docker stats --no-stream を実行してください。

グラフTypical idle container memory, stock images, megabytes
このグラフのデータ
[
  {
    "label": "nginx",
    "idle_mb": 8,
    "budget_mb": 64
  },
  {
    "label": "Redis 7",
    "idle_mb": 12,
    "budget_mb": 128
  },
  {
    "label": "Traefik v3",
    "idle_mb": 40,
    "budget_mb": 128
  },
  {
    "label": "PostgreSQL 16",
    "idle_mb": 45,
    "budget_mb": 512
  },
  {
    "label": "Uptime Kuma",
    "idle_mb": 95,
    "budget_mb": 256
  },
  {
    "label": "MariaDB 11",
    "idle_mb": 190,
    "budget_mb": 512
  },
  {
    "label": "Nextcloud (Apache)",
    "idle_mb": 210,
    "budget_mb": 768
  }
]

2 つの列は、それぞれ異なる目的で使用します。idle_mb は、何も処理していないときにコンテナが使用する量です。budget_mb は、計画時に確保すべき量です。実際の使用時はアイドル状態ではないためです。PostgreSQL はアイドル時には 45 MB 近くですが、接続、ソート、キャッシュが使用されると 512 MB が必要です。計画時は予算列を使用してください。デバッグ時はアイドル列を使用してください。

7 行の傾向にも注目してください。nginx はアイドル時に 8 MB、Nextcloud は 210 MB を使用します。アプリケーションの前段に置くプロキシの使用量は、ほぼ無視できる程度です。サーバーの容量を決める際に重視すべきなのは、データベースと PHP アプリケーションです。

docker stats について、1 点注意が必要です。このメモリ使用量には、コンテナ自身がファイルを読み取ったことで取り込まれたページキャッシュも含まれます。そのため、起動後しばらくは増加し、その後に安定します。メモリリークと判断する前に、1 時間監視してください。

VPS のサイズ選定: 2 GB、4 GB、8 GB で運用できる構成

まず、ホスト自身が使用する分を差し引きます。kernel、systemd、journald、sshd、Docker daemon は、コンテナと同じ RAM を使用します。dockerd と containerd で、そのうち約 100 MB を使用します。また、page cache 用の空きメモリと、イメージのビルドやデータベースのダンプ実行時に発生するスパイク分も必要です。

グラフRAM budget by plan size, megabytes
このグラフのデータ
[
  {
    "plan": "2 GB VPS",
    "total_mb": 2048,
    "host_reserve_mb": 768,
    "container_mb": 1280
  },
  {
    "plan": "4 GB VPS",
    "total_mb": 4096,
    "host_reserve_mb": 1024,
    "container_mb": 3072
  },
  {
    "plan": "8 GB VPS",
    "total_mb": 8192,
    "host_reserve_mb": 1536,
    "container_mb": 6656
  }
]

host_reserve_mb は、オペレーティングシステム、Docker daemon、および負荷時にもサーバーの応答性を維持するための余裕を含みます。残りが container_mb で、実際に割り当てられるのはこの容量だけです。最小のプランでは 768 MB、最大のプランでは 1536 MB と、プランが大きいほど予備容量も増えます。大きいサーバーでは、より多くのコンテナを実行し、より多くのログを書き込み、より多くの page cache が必要になるためです。

2 GB プランでは、コンテナ用に 1280 MB が残ります。そのうち 512 MB を PostgreSQL、128 MB を Traefik に使うと、すでに半分がなくなります。残りは、約 256 MB ずつの小規模なアプリケーション 2 つ分です。これは実用になる構成です。ただし、その上に Nextcloud と検索クラスタまで載せる余裕はありません。

4 GB プランでは 3072 MB が残ります。この容量なら、データベース、リバースプロキシ、3 つのアプリケーション、監視用コンテナを同時に収容できます。重要な用途で使う価値がある最小サイズです。余ったメモリが、問題のあるデプロイによる一時的な増加を吸収するためです。

8 GB プランでは、8192 MB のうち 6656 MB が残ります。この規模では、通常はメモリではなく CPU またはディスクスループットが制約になります。SSD Nodes にはこれより小さいプランがありません。最小プランは RAM 8 GB です(料金ページの確認日: 2026-10-11)。そのため、SSD Nodes ではこの行が該当し、2 GB と 4 GB の行は他のプロバイダーの小規模なサーバーを示します。コンテナサイズの中には、負荷ではなく設定によって必要容量が決まるものもあります。ローカルモデルサーバーは、コンテキストウィンドウに比例した KV cache を確保するため、リクエストを 1 件も受ける前に Ollama の num_ctx を増やす と数 GB が予算から差し引かれることがあります。計算上スタックが収まらない場合は、設定で無理に調整せず、大きいプランを購入してください。VPS に実際にかかる費用では、追加の GB に毎月いくら払う価値があるかを説明しています。

計算を正確に保つには、2 つのルールがあります。まず、すべてのサービスにメモリ制限を設定します。これにより、1 つの暴走プロセスがサーバー全体を枯渇させることを防げます。次に、予算の上限まで使い切らないでください。docker compose build と pg_dump は、最も負荷が高い瞬間にどちらもメモリを必要とするためです。Docker Compose のメモリ制限で構文と注意点を確認できます。

コンテナが終了コード 137 で終了するのはなぜですか?

kernel がコンテナを強制終了したためです。137 は 128 に 9 を加えた値で、シグナル 9 は SIGKILL です。コンテナが許可された量を超えるメモリを要求したため、out of memory (OOM) killer がコンテナを終了しました。

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

推測ではなく、原因を確認します。

docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory"

"OOMKilled": true はコンテナ自身の cgroup 制限に達したことを示し、kernel のログには選択されたプロセスが記録されます。

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

これは、影響が 1 つのコンテナ内に収まるため、望ましい状態です。問題なのは、制限のないコンテナです。制限がない場合、その上限はマシン全体になります。そのため、1 つのサービスのメモリリークによって host のメモリが枯渇し、kernel はシステム全体からサイズを基準に強制終了する対象を選びます。ログ行から Memory cgroup プレフィックスがなくなり、Out of memory: Killed process 2417 (postgres) と表示されます。選ばれるプロセスは database であることが多く、メモリリークを起こしたコンテナは動作し続けます。すべてのサービスに制限を設定することが、個々の制限値を正確に決めることより重要なのはこのためです。

Swap はタイミングを変えるだけで、計算方法は変えません。多くの VPS イメージには Swap がありません。swapon --show で確認できます。Swap がない場合、このコマンドは何も出力しません。Swap file があると、kernel は使用頻度の低いページを退避させる場所を確保できるため、問題に気付くまでの時間を数分延ばせます。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

free -h で Swap 行の合計値が 0 以外になっていることを確認します。Swap は RAM を増やしません。メモリ不足が常態化したマシンは、SSH で接続して修復できないほど遅くなることがあります。そのため、Swap は警告用のバッファーとして扱い、メモリ容量の設計を見直してください。

UFW が公開した Docker ポートをブロックしないのはなぜですか?

トラフィックが、UFW が保護するチェーンを通過しないためです。-p 5432:5432 または compose の ports: エントリでポートを公開すると、デーモンは nat テーブルに DNAT(宛先ネットワークアドレス変換)ルールを書き込み、独自の DOCKER チェーンに accept ルールを書き込みます。コンテナ宛てのパケットはホストに配信されず、そのコンテナへ転送されます。そのため FORWARD パスで処理され、UFW が書き込む INPUT ルールを通過しません。

サーバー上で、この動作を確認できます。

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW が 5432 DENY IN Anywhere と表示していても、nat テーブルには同じポート用の DNAT tcp ... to:172.18.0.2:5432 ルールが存在することがあります。別のマシンからは、nc -vz your.server.ip 5432 で接続できます。データベースはパブリックインターネットに公開されていますが、ファイアウォールには公開されていないように表示されます。

対策は、公開するポートを減らすことです。同じ compose プロジェクト内のコンテナはネットワークを共有し、サービス名で相互に接続できます。そのため、隣接するアプリケーションにだけサービスを提供するデータベースには、ports: エントリは必要ありません。ローカルからのアクセスが必要な場合は、公開先を loopback に限定します。

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

docker compose up -d の後は、外部からの nc -vz your.server.ip 5432 は失敗します。一方、ホスト上の psql -h 127.0.0.1 -p 5432 は引き続き機能します。正常な小規模スタックでは、reverse proxy だけが 80 と 443 でポートを公開します。ポートを公開しつつフィルタリングする必要がある場合の DOCKER-USER チェーンについては、Docker の公開ポートが UFW を迂回する理由 を参照してください。ホスト側のルールについては、UFW ファイアウォールの基本 を参照してください。

再起動後にコンテナが消えるのはなぜですか?

コンテナを再起動する設定がないためです。コンテナは、restart policy を指定しない場合、no で作成されます。そのため、再起動すると停止したままになり、daemon も対応しません。VPS では再起動は珍しくありません。unattended upgrades による kernel 更新、プロバイダーのメンテナンス、前述の OOM シーケンスなどが再起動の原因になります。

2 つの条件を満たす必要があります。まず、daemon がブート時に起動することです。

systemctl is-enabled docker

標準的な Ubuntu インストールでは、enabled と表示されます。次に、各サービスに policy を設定します。

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped は再起動後にコンテナを起動します。また、意図的に停止したコンテナはそのまま停止させます。always は、daemon が再起動するたびに、意図的に停止したコンテナも再起動します。デバッグ中には予期しない動作になります。ファイルを編集するだけでは不十分です。restart policy はコンテナの作成時に設定されるためです。docker compose up -d を実行してコンテナを再作成し、現在の値を確認します。

docker inspect my-app | grep -A3 RestartPolicy

その後、意図的にサーバーを再起動し、プロジェクトディレクトリで docker compose ps を実行します。計画的な再起動後も稼働する stack であれば、予期しない再起動にも対応できます。stack に起動順序の保証やブート時に 1 回だけ実行するジョブが必要な場合は、systemd unit のほうが適しています。ブート時に Docker Compose を起動するに unit file があります。再起動後に戻ったコンテナが実際にサービスを提供しているか確認するには、Compose の healthchecksを追加します。

VPS のディスクが満杯になるのはなぜですか?

Docker は削除を指示するまで、すべてを保持するためです。これまでに pull したすべてのイメージタグ、停止したすべてのコンテナ、再作成時に残されたすべての匿名ボリューム、ビルドキャッシュのすべてのレイヤーがディスクに残ります。ディスクを大きくしても、時間を稼げるだけです。SSD Nodes の最小プランにある 160 GB SSD(料金ページの確認日: 2026-10-11)でも、毎週イメージを pull してビルドするサーバーでは同じように満杯になります。root ファイルシステムが小さい場合は、より早く障害になります。

ディスクが満杯になっても、クラッシュのようには見えません。同じ 1 時間のうちに、コンテナ、apt、journald、docker pull から no space left on device が発生します。PostgreSQL は書き込みを受け付けなくなります。サーバー自体は稼働しているため、再起動ループより気付きにくい状態です。

削除する前に確認します。

docker system df
df -h /

docker system df は、イメージ、コンテナ、ローカルボリューム、ビルドキャッシュの合計を分類し、それぞれの横に RECLAIMABLE 列を表示します。独自にイメージをビルドするサーバーでは、通常、ビルドキャッシュが最大の項目です。

docker image prune -a
docker builder prune
docker system df

docker image prune -a は、どのコンテナからも使用されていないすべてのイメージを削除します。docker builder prune はビルドキャッシュを削除します。使用中のものはスキップされるため、サービスの稼働中でもどちらも安全です。安全でないのは docker system prune --volumes です。これは停止したコンテナを削除し、その後、残っているどのコンテナからも使用されていない匿名ボリュームをすべて削除します。週末の間だけ停止したスタックは、まさにこの状態になります。データベースが匿名ボリュームにデータを保存していた場合、そのデータも削除されます。これは、イメージが volume を宣言していて、compose ファイルでそこに何も mount していない場合に Docker が作成するボリュームです。名前付きボリュームはそのコマンドでは残りますが、docker volume prune --all は未使用の名前付きボリュームも削除します。どちらかの flag を入力する前に、bind mount と名前付きボリュームの違いを確認し、先にバックアップを取得してください。

コンテナログは、気付きにくいディスク使用量の増加要因です。デフォルトの json-file driver にはサイズ制限がないため、ログを頻繁に出力するコンテナが /var/lib/docker/containers にギガバイト単位のデータを書き込みます。すべてのコンテナで /etc/docker/daemon.json により上限を設定します。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

sudo systemctl restart docker で適用します。この操作によってコンテナが再起動するため、実行するタイミングを選んでください。上限は変更後に作成されたコンテナに適用されます。実行中のコンテナを docker compose up -d --force-recreate で再作成し、次のように確認します。

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

inspect の出力に max-size が設定されていることを確認します。空の場合、そのコンテナは変更前に作成されたものであり、現在も制限なしで書き込みを続けています。

小規模な Docker ホストを健全に保つ習慣

ダッシュボードや、新たに覚える必要があるツールは不要です。

  • 月初に docker system df と df -h / を実行します。2 つのコマンドを 30 秒実行するだけで、障害になるかなり前から傾向を把握できます。
  • 確実に小さいと思うサービスも含め、すべてのサービスにメモリ制限を設定します。制限により、ホスト全体の障害をコンテナ 1 つの再起動に抑えられます。
  • 別の場所からホストを監視し、カーネルが対応する前にメモリやディスクの逼迫を把握します。Uptime Kuma はコンテナで実行でき、アイドル時のメモリ使用量は約 95 MB です。
  • コンテナではなくボリュームをバックアップします。コンテナは破棄できますが、ボリュームはそうではありません。VPS の restic バックアップ で、スケジュール設定とリストアテストに対応できます。
  • compose ファイルではイメージタグを固定し、決めた日に更新します。latest を使用すると、次の docker compose pull から取得するバージョンは、その朝にリリースされたものになります。

Docker を実行する小規模な VPS は、4 つの数値が許容範囲に収まっていれば、何年も健全な状態を維持できます。メモリの予算、公開ポートの一覧、各サービスの再起動ポリシー、空きディスク容量です。それ以外は、自宅ですでに運用している Docker と同じです。

FAQ

Docker を VPS で実行するには、どのくらいの RAM が必要ですか?

Docker 自体の消費量は少なく、daemon と containerd を合わせても約 100 MB です。必要な RAM の大部分はコンテナが使用します。まずホスト用の容量を確保します。768 MB の 2048 MB 構成で、オペレーティングシステム、daemon、余裕分に割り当てると、コンテナには 1280 MB を使用できます。512 MB のデータベース、128 MB のリバースプロキシ、小規模なアプリケーション 2 つであれば、その範囲に収まります。公開されている数値をそのまま信頼せず、docker stats --no-stream で実際の構成を測定してください。

1 GB の VPS で Docker を実行できますか?

はい。軽量なコンテナ 1 つまたは 2 つであれば可能です。開始前に swap ファイルを追加してください。1 GB の構成では、オペレーティングシステムと Docker daemon の起動後に、おおむね半分が使用されます。小規模なアプリケーションとリバースプロキシを動かす余地は残りますが、実際の負荷がかかるデータベースには足りません。この容量のホストでイメージをビルドすると、処理に失敗するか、別のプロセスが停止します。そのため、別の環境でビルドして完成したイメージを pull してください。

UFW は Docker コンテナを保護しますか?

公開したポートには適用されません。Docker は独自の DNAT ルールと forward ルールを書き込むため、公開されたコンテナポート宛てのパケットはホストへ渡されず、コンテナへ転送されます。そのため、UFW が管理する INPUT ルールでは処理されません。ufw deny 5432 が有効でも、そのポートはインターネットから応答できます。127.0.0.1:5432:5432 で loopback に公開するか、内部サービスを公開しないでください。または、DOCKER-USER chain でフィルタリングしてください。

VPS の再起動後、コンテナは再起動しますか?

restart policy を指定して作成した場合に限り、再起動します。各サービスに restart: unless-stopped を設定し、docker compose up -d を実行してコンテナをその設定で再作成してください。systemctl is-enabled docker が enabled を出力することを確認します。その後、意図的に再起動して docker compose ps を確認してください。一度もテストしていない restart policy は、実質的に restart policy として機能していません。

Docker イメージはどのくらいの頻度で prune すべきですか?

多くの小規模サーバーでは、月 1 回で十分です。または、docker system df が、放置すると不足する reclaimable space を報告したときに実行してください。サービスの実行中でも docker image prune -a と docker builder prune は安全です。使用中のイメージと cache はスキップされます。参照されていない volume を正確に把握していない限り、docker system prune --volumes は避けてください。停止中の stack があれば、その anonymous volume も削除されるためです。docker volume prune --all は未使用の named volume も対象にします。

この記事は役に立ちましたか?