VPSにDocker ComposeでアプリをHTTPS公開する基本手順(Caddy使用)

PRこの記事にはアフィリエイト広告が含まれます。紹介したサービスに申し込まれると、運営者が報酬を受け取ることがあります。評価や順位は報酬で変えていません。広告表記について

Dify や n8n のようなアプリを VPS で動かすとき、最初に決めるのが「どうやって HTTPS で公開するか」です。この記事では、どのアプリにも使える基本の形として、Caddy を前に置き、アプリ本体は外に出さない構成を作ります。

できあがる構成は次のとおりです。外から届くのは Caddy の 80番と443番だけで、アプリのポートはサーバーの中だけで使います。

インターネット ──(80/443)──> Caddy ──(Docker の内部ネットワーク)──> アプリ

Caddy を選ぶ理由

Caddy は、設定ファイルにドメイン名を書くだけで、証明書の取得と更新、HTTP から HTTPS への転送を自動で行います。公式ドキュメントによると、条件は次の4つです。

  • ドメインの A レコード(IPv6 なら AAAA レコード)がサーバーを向いている
  • 80番と443番のポートが外から届く
  • Caddy がその2つのポートを使える
  • Caddy のデータディレクトリが書き込み可能で、消えずに残る

Nginx と certbot を組み合わせる方法もありますが、証明書の更新を別に管理する必要があります。1台の VPS で個人がアプリを動かすなら、Caddy のほうが管理する部品が少なくて済みます。

前提

  • Ubuntu 24.04 LTS の VPS(22.04、26.04 でも同じ手順で入れられると Docker 公式は案内しています)
  • 独自ドメイン(サブドメインでも構いません)
  • SSH でログインできること

手順1:ファイアウォールを設定する

SSH、HTTP、HTTPS だけを通します。HTTP/3 を使えるように、443番は UDP も開けます。

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443
sudo ufw enable
sudo ufw status

VPS の会社によっては、管理画面にもファイアウォール(パケットフィルターやセキュリティグループと呼ばれます)があります。その場合は、管理画面の側でも同じ3つを許可してください。

Docker のポート公開は ufw を素通りします

Docker 公式ドキュメントは、Docker と ufw は「互換性がない」と明記しています。ports: で公開したポートへの通信は、ufw のルールが適用される前に Docker 側に振り分けられるためです。ufw で塞いだつもりのポートでも、ports: に書けば外から届きます。

対策は、外に出すポートを Caddy の 80番と443番だけにすることです。アプリ側は ports: ではなく expose: を使うか、どうしてもサーバーに公開する場合は 127.0.0.1:8080:80 のように 127.0.0.1 に限定します。

手順2:Docker をインストールする

Docker 公式の apt リポジトリから入れます。Ubuntu 標準の docker.io パッケージなどが入っている場合は、先に削除するよう公式は案内しています。

sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

公式の GPG 鍵とリポジトリを登録します。

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

Docker 本体と Compose プラグインを入れて、動作を確認します。

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker run hello-world
docker compose version

この手順を Ubuntu 24.04 のコンテナで実行したところ、2026年10月21日の時点で Docker Engine 29.9.2 と Docker Compose v5.6.1 が入りました。

手順3:DNS を設定する

ドメインの管理画面で、公開したいホスト名の A レコードに VPS の IP アドレスを設定します。Caddy は起動したときに証明書を取りにいくため、DNS の設定を先に済ませておきます。

手順4:Compose ファイルと Caddyfile を作る

作業用のディレクトリを作り、次の3つのファイルを置きます。例として、アクセス元の情報を表示するだけの小さなアプリ(traefik/whoami)を公開します。実際に使うときは、app の部分を自分のアプリに置き換えます。

compose.yaml:

services:
  caddy:
    image: caddy:2.11.7
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      - SITE_DOMAIN=${SITE_DOMAIN}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

  app:
    image: traefik/whoami:v1.11.0
    restart: unless-stopped
    # 外には公開せず、Caddy からだけ届くようにする
    expose:
      - "80"

volumes:
  caddy_data:
  caddy_config:

Caddyfile:

{$SITE_DOMAIN} {
	reverse_proxy app:80
}

.env(自分のドメインに置き換えます):

SITE_DOMAIN=app.example.com

ポイントは3つです。

  • イメージのバージョンを固定する:caddy:latest のように書くと、再起動したときに意図せず新しい版に変わることがあります。
  • caddy_data をボリュームにする:取得した証明書がここに保存されます。消えると取り直しになり、短い間に何度も取り直すと、発行元の回数制限に当たるおそれがあります。
  • アプリは expose: にする:Compose の同じネットワーク内では、サービス名(app)で通信できます。外にポートを出す必要はありません。

手順5:起動して確認する

docker compose up -d
docker compose ps
docker compose logs caddy

ブラウザで https://app.example.com を開き、鍵マークが付いて表示されれば完了です。http:// で開いた場合も、Caddy が自動で HTTPS に転送します。

手元の検証では、ドメインの代わりに app.localhost を使い、Caddy の内部証明書(tls internal)で同じ構成を動かしました。結果は次のとおりです。

確認したこと 結果
HTTPS でアプリが表示されるか 表示された(ステータス 200)
HTTP で開いたときの動き HTTPS へ転送された(ステータス 308)
アプリのポートがサーバーの外に出ていないか docker compose ps で app は 80/tcp のみ(公開なし)

次にやること

この形ができれば、app を差し替えるだけでほかのアプリも公開できます。

出典

  1. Install Docker Engine on Ubuntu(Docker 公式)(に確認)
  2. Packet filtering and firewalls(Docker 公式)(に確認)
  3. Port publishing and mapping(Docker 公式)(に確認)
  4. Automatic HTTPS(Caddy 公式)(に確認)