Docker ComposeでNext.jsを本番起動する【Art Navi開発記録 #5】
はじめに
こんにちは。
なかのひとカンパニーの archer です。
美術展検索サービス Art Navi の開発内容を、備忘録も兼ねて記事にしています。
- サービスURL: https://art-weekend.jp/
前回は、LocalStorageを使ったお気に入り機能について書きました。
今回は、Art Navi を本番環境で動かすために使っている Docker ComposeでNext.jsを起動する構成 についてまとめます。
今回やりたいこと
Art Navi は Next.js で作っています。
開発中は pnpm dev で起動できますが、本番環境では開発サーバーではなく、ビルド済みのNext.jsアプリとして起動したいです。
今回の目的は以下です。
- Next.jsを本番ビルドする
- Dockerイメージとして固める
- Docker Composeで起動する
- NginxからNext.jsへ接続できる状態にする
この記事では、まずNext.jsアプリをDocker Composeで起動するところまでを整理します。
本番環境の構成
ざっくりした構成は以下です。
ユーザー
↓
Cloudflare
↓
Cloudflare Tunnel
↓
Nginx
↓
Next.js
今回はこのうち、以下の部分を扱います。
Nginx
↓
Next.js
Cloudflare TunnelやNginxの詳細は、別の記事でまとめる予定です。
Dockerfileを用意する
Next.jsを本番起動するために、まず Dockerfile を用意します。
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable pnpm \
&& pnpm install --frozen-lockfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN corepack enable pnpm \
&& pnpm build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["pnpm", "start"]
構成としては、以下の3段階に分けています。
deps
依存パッケージをインストール
builder
Next.jsをビルド
runner
本番起動に必要なものだけで起動
マルチステージビルドにすることで、ビルド用と実行用を分けられます。
package.jsonのscripts
Next.jsの本番起動には、build と start を使います。
package.json には以下のようなscriptsを用意しています。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
開発時は以下です。
pnpm dev
本番起動時は以下です。
pnpm build
pnpm start
Dockerでは、この流れをコンテナ内で実行します。
docker-compose.ymlを用意する
次に、Next.jsを起動するための docker-compose.yml を用意します。
services:
nextjs:
build:
context: .
dockerfile: Dockerfile
container_name: art-navi-nextjs
environment:
NODE_ENV: production
SITE_URL: https://art-weekend.jp
expose:
- "3000"
restart: unless-stopped
ここでは、Next.jsコンテナを nextjs というサービス名で定義しています。
ポイントは、ports ではなく expose を使っているところです。
expose:
- "3000"
外部に直接公開するのではなく、同じDockerネットワーク内のNginxからアクセスする想定です。
portsではなくexposeにした理由
Next.jsを直接インターネットに公開するのではなく、前段にNginxを置く構成にしています。
そのため、Next.jsコンテナの3000番ポートをホスト側に公開する必要はありません。
外部
↓
Nginx
↓
Next.js:3000
この構成にすると、外部から直接Next.jsへアクセスされず、Nginxを経由させることができます。
Docker Compose内では、サービス名を使ってアクセスできます。
http://nextjs:3000
Nginxの設定では、このURLに向けてリバースプロキシします。
本番用の環境変数
本番環境では、.env.production のようなファイルを用意して環境変数を管理します。
例です。
NODE_ENV=production
SITE_URL=https://art-weekend.jp
DBを使う場合は、以下のような接続情報も入ります。
DATABASE_URL=postgresql://user:password@db:5432/artnavi
本番用の .env.production にはパスワードなどの秘密情報が含まれるため、Git管理しないようにします。
.env.production
起動手順
Docker Composeで起動します。
docker compose up -d --build
コンテナの状態を確認します。
docker compose ps
ログを確認します。
docker compose logs -f nextjs
Next.jsが正常に起動していれば、コンテナ内では3000番ポートで待ち受けます。
動作確認
Nginxをまだ使わない場合は、一時的に ports を指定して確認することもできます。
ports:
- "3000:3000"
この場合、ブラウザで以下にアクセスできます。
http://localhost:3000
ただし、本番構成ではNginxを前段に置くため、確認が終わったら ports ではなく expose に戻します。
よく使うコマンド
ビルドして起動します。
docker compose up -d --build
ログを確認します。
docker compose logs -f nextjs
コンテナに入ります。
docker compose exec nextjs sh
停止します。
docker compose down
イメージを作り直したい場合は、再ビルドします。
docker compose build --no-cache nextjs
気をつけたこと
DockerでNext.jsを本番起動するときは、以下に注意しました。
-
pnpm buildが成功していること -
pnpm startで起動できること - 本番用の環境変数を設定していること
-
.env.productionをGit管理しないこと - Nginx経由にする場合は
portsではなくexposeにすること - コンテナ名ではなくサービス名で通信すること
特に、Docker Compose内ではサービス名で通信できる点が重要でした。
NginxからNext.jsへ接続する場合は、以下のように指定できます。
http://nextjs:3000
Docker化してよかった点
Next.jsをDocker化したことで、以下のメリットがありました。
- 本番環境で起動手順を固定できる
- Ubuntu側にNode.jsを直接入れなくてよい
- サービスごとに環境を分けやすい
- 再起動や再ビルドがしやすい
- 他のサービスにも構成を流用しやすい
個人開発でも、Docker Composeで起動できるようにしておくと、あとから運用しやすくなると感じました。
まとめ
今回は、Art Navi のNext.jsアプリをDocker Composeで本番起動する構成についてまとめました。
今回のポイントは以下です。
- Next.jsは
pnpm buildしてからpnpm startで起動する - DockerfileでNext.jsの本番イメージを作る
- Docker ComposeでNext.jsコンテナを管理する
- Nginxを前段に置く場合は
portsではなくexposeを使う - コンテナ間通信ではサービス名を使う
次回は、NginxでNext.jsにリバースプロキシする構成について書く予定です。