結論

デプロイとは、ビルドした成果物をサーバーに置き、動く状態にする作業です。利用者に公開する「リリース」とは別の工程です。

この記事でわかること

  • ビルド・デプロイ・リリースの違い表と7つの手順
  • nginx で4回デプロイし、2通りで戻した実際の出力
  • 切り替え方で404が3,917件と0件に分かれた測定

2026年10月17日更新|nginx 1.24.0・Ubuntu 24.04 で実行確認

ソースコードは「ビルド」で配れる形になり、「デプロイ」でサーバー上で動く状態になり、「リリース」で利用者が使えるようになります。

flowchart TD
    A[ソースコード] -->|ビルド| B[成果物 tar.gz など]
    B -->|デプロイ| C[サーバー上で動く状態]
    C -->|リリース| D[利用者が使える]
    C -.->|不具合なら| E[ロールバック 前の版へ]
言葉 何をするか この記事の例
デプロイ 成果物をサーバーに置き、動く状態にする releases/ に展開し current を切り替え
ビルド ソースから配れる形を作る build.sh で tar.gz を作る
リリース 利用者に公開・提供を始める 公開日に告知・機能を有効にする
ロールバック 前の版に戻す current を1つ前へ向け直す
CI/CD ビルド〜デプロイを自動で回す仕組み タグの push で deploy.sh を実行
本番環境 利用者が実際に使う環境 nginx が配信している場所

確認環境:Ubuntu 24.04.4 LTS・nginx 1.24.0・GNU coreutils 9.4(ln・mv)・Git 2.43.0・Python 3.11.15 で、小さな Web アプリを releases/ と current のシンボリックリンクでデプロイし、ロールバックと切り替え中のエラー件数を測りました。システムコールは strace で見ています。nginx・GNU coreutils・Linux の rename(2)・Capistrano・GitHub Actions・Kubernetes の公式ドキュメントで仕様を確かめています(確認日 2026年10月17日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「デプロイとは|ビルド・リリースとの違いとロールバックを実例で解説」

デプロイとは:意味と読み方

デプロイ(deploy)は、英語で「配置する・展開する」という意味の言葉です。 IT では、作ったプログラムをサーバーなどの実行環境に置いて、動く状態にする作業を指します。名詞では「デプロイメント(deployment)」とも言います。

「動く状態」には、ファイルを置くことに加えて、次のような作業が入ります。

  • 設定ファイルを置く(接続先・パスワードなどは環境ごとに違う)
  • 必要ならデータベースの構造を変える(マイグレーション)
  • アプリを再起動する、または Web サーバーの向き先を切り替える
  • 正しく動いているかを確かめる(ヘルスチェック)

デプロイ先は1つではありません。多くのチームは、本番の前に同じ形の環境で確かめます。

環境 使う人 デプロイする目的
開発環境 開発者 書いたコードを手元や共有サーバーで動かす
検証環境(ステージング) 開発者・テスト担当 本番と同じ形で、出す前に確かめる
本番環境 利用者 実際のサービスとして動かす

ビルド・デプロイ・リリースの違い

3つは「何を相手にする作業か」で分けると区別しやすくなります。

ビルド デプロイ リリース
相手 ソースコード サーバー(環境) 利用者
すること コンパイル・まとめて、配れる形にする 成果物を置き、動く状態にする 使えるように公開・告知する
できるもの 実行ファイル・jar・tar.gz・コンテナイメージ 動いているアプリ 利用できるサービス・機能
失敗したとき 成果物ができない(サーバーは無傷) 前の版に戻す(ロールバック) 機能を止める・公開を取り下げる
例 ./build.sh → myapp-v1.1.0.tar.gz ./deploy.sh myapp-v1.1.0.tar.gz 「新機能を公開しました」

順番は ビルド → デプロイ → リリース です。小さなサービスでは、デプロイした瞬間に利用者が新しい版を見るので、デプロイとリリースが同時に起きます。分けて考えると役立つのは、次のような場面です。

混同 1デプロイ=利用者に公開すること
→
実際デプロイは「置いて動かす」まで。公開(リリース)は別に決められる

新機能を隠したままデプロイし、公開日に設定で有効にする(フィーチャーフラグ)やり方がある

混同 2ビルドが通れば、デプロイも成功する
→
実際ビルドが通っても、置き方を間違えると動かない

この記事の v1.2.0 は、ビルドは成功したが配置先がずれて 404 になった(後の節)

混同 3リリース=バージョン番号を付けること
→
実際文脈で意味が変わる。番号付き成果物を「リリース」と呼ぶツールもある

この記事の「リリース」は、利用者への公開を指す

デプロイの流れ:7つの手順

この記事のデプロイは、次の7手順で進みます。手動でもツールでも、中身はほぼ同じです。

1

ビルドする

ソースから成果物(tar.gz・jar・コンテナイメージ)を作る。版番号を成果物の名前と中身に入れる

例:./build.sh → myapp-v1.1.0.tar.gz

2

テストする

単体テスト・結合テストを、デプロイの前に通す

CI で自動化しやすい手順

3

サーバーへ送る

scp・rsync・コンテナレジストリなどで成果物を届ける

例:scp myapp-v1.1.0.tar.gz deploy@server:/tmp/

4

新しい場所に展開する

動いている版を上書きせず、別のディレクトリに置く

例:releases/20261017020633-v1.1.0/

5

切り替える

Web サーバーが見ている current の向きを新しい版へ変える

ln -sfn と mv -T で一瞬で置き換える

6

動作を確かめる

ヘルスチェック用の URL が新しい版を返すかを見る。失敗なら前の版に戻す

例:curl -fsS /health.json

7

片付ける

前の版はロールバック用にいくつか残し、それより前を消す

この記事では新しい順に5個残す

実際にデプロイしてみる:nginx と releases/current

ここからは、小さな Web アプリを nginx で配信し、手順4〜7をスクリプトで動かします。サーバー上のディレクトリは次の形です。Ruby のデプロイツール Capistrano の公式ドキュメントにある構成(current は最新のリリースを指すシンボリックリンク、releases は時刻付きのフォルダ)と同じ考え方です。

/var/www/myapp
|-- current -> /var/www/myapp/releases/20261017020651-v1.2.1
`-- releases
    |-- 20261017020628-v1.0.0
    |-- 20261017020633-v1.1.0
    `-- 20261017020651-v1.2.1

シンボリックリンク(symlink)は、別の場所を指す「ショートカット」のようなファイルです。nginx はいつも current/public を配信し、デプロイでは current がどの版を指すかだけを変えます。前の版のファイルは消さずに残るので、戻すときも向きを変えるだけです。

nginx の設定

server {
    listen 127.0.0.1:24400;
    root /var/www/myapp/current/public;
    location = /health.json { add_header Cache-Control no-store; }
}

nginx のドキュメントでは、disable_symlinks の既定値は off(パスの途中のシンボリックリンクを許可し、確かめない)です。この設定のまま current を通して配信できます。

まだ何もデプロイしていない状態で開くと、current が無いので 404 になります。error.log には次の1行が出ました。

HTTP/1.1 404 Not Found
2026/10/17 02:06:25 [error] 974#974: *1 "/var/www/myapp/current/public/index.html" is not found (2: No such file or directory), client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "127.0.0.1:24400"

ビルドするスクリプト(build.sh)

Git のタグ(v1.1.0 など)を版番号として、HTML とヘルスチェック用の health.json に埋め込み、tar.gz にまとめます。

#!/usr/bin/env bash
# ビルド:src/ から、配る形(dist/)を作る
set -euo pipefail
cd "$(dirname "$0")"
VERSION=$(git describe --tags --always)
COMMIT=$(git rev-parse --short HEAD)
rm -rf dist && mkdir -p dist/public
sed "s/__VERSION__/${VERSION}/" src/index.html > dist/public/index.html
cp src/app.css dist/public/
printf '{"version":"%s","commit":"%s"}\n' "$VERSION" "$COMMIT" > dist/public/health.json
tar -czf "myapp-${VERSION}.tar.gz" -C dist .
echo "built myapp-${VERSION}.tar.gz ($(du -h "myapp-${VERSION}.tar.gz" | cut -f1))"
$ git tag v1.0.0 && ./build.sh
built myapp-v1.0.0.tar.gz (4.0K)
$ tar -tzf myapp-v1.0.0.tar.gz
./
./public/
./public/app.css
./public/health.json
./public/index.html

デプロイするスクリプト(deploy.sh)

サーバー上で実行します。展開 → 切り替え → 確認 → 片付け の4段で、確認に失敗したら自動で前の版に戻します。

#!/usr/bin/env bash
# デプロイ:成果物を releases/ に展開し、current の向きを切り替える
set -euo pipefail
APP=/var/www/myapp
KEEP=5
PKG=$1                                   # 例:myapp-v1.1.0.tar.gz
VERSION=$(basename "$PKG" .tar.gz); VERSION=${VERSION#myapp-}
REL="$APP/releases/$(date +%Y%m%d%H%M%S)-$VERSION"

echo "[1/4] 展開: $REL"
mkdir -p "$REL"
tar -xzf "$PKG" -C "$REL"

PREV=$(readlink "$APP/current" || true)
echo "[2/4] 切り替え: current -> $(basename "$REL")(前: ${PREV:+$(basename "$PREV")})"
ln -sfn "$REL" "$APP/current.tmp"
mv -T "$APP/current.tmp" "$APP/current"   # rename は一瞬で置き換わる

echo "[3/4] 確認: /health.json"
if ! curl -fsS --max-time 5 http://127.0.0.1:24400/health.json | grep -q "\"$VERSION\""; then
  echo "NG: 新しい版が応答しません。元に戻します"
  if [ -n "$PREV" ]; then
    ln -sfn "$PREV" "$APP/current.tmp" && mv -T "$APP/current.tmp" "$APP/current"
    echo "rollback: current -> $(basename "$PREV")"
  fi
  rm -rf "$REL"                          # 失敗した版は消す
  exit 1
fi
echo "OK: $(curl -fsS http://127.0.0.1:24400/health.json)"

echo "[4/4] 片付け: 新しい順に ${KEEP} 個だけ残す"
ls -1d "$APP"/releases/* | sort -r | tail -n +$((KEEP + 1)) | xargs -r rm -rf

1回目と2回目のデプロイ

v1.0.0 を初めてデプロイしたときの出力です。

$ ./deploy.sh myapp-v1.0.0.tar.gz
[1/4] 展開: /var/www/myapp/releases/20261017020628-v1.0.0
[2/4] 切り替え: current -> 20261017020628-v1.0.0(前: )
[3/4] 確認: /health.json
OK: {"version":"v1.0.0","commit":"137ab2b"}
[4/4] 片付け: 新しい順に 5 個だけ残す
$ curl -sS http://127.0.0.1:24400/
<!doctype html>
<html lang="ja">
<head><meta charset="utf-8"><title>myapp</title><link rel="stylesheet" href="/app.css"></head>
<body><h1>myapp</h1><p>version: v1.0.0</p></body>
</html>

画面に「お知らせ欄」を足して v1.1.0 としてデプロイすると、current の向きだけが変わり、v1.0.0 のフォルダはそのまま残ります。

$ ./deploy.sh myapp-v1.1.0.tar.gz
[1/4] 展開: /var/www/myapp/releases/20261017020633-v1.1.0
[2/4] 切り替え: current -> 20261017020633-v1.1.0(前: 20261017020628-v1.0.0)
[3/4] 確認: /health.json
OK: {"version":"v1.1.0","commit":"c76ef2f"}
[4/4] 片付け: 新しい順に 5 個だけ残す
$ ls -1 /var/www/myapp/releases
20261017020628-v1.0.0
20261017020633-v1.1.0

ロールバックを動かす:自動で戻す・手で戻す

ロールバックは、デプロイした版に不具合があったとき、前の版に戻すことです。 releases/current の形なら、current を前の版へ向け直すだけで戻ります。

自動ロールバック:ヘルスチェックに失敗した v1.2.0

v1.2.0 では、ビルドスクリプトの出力先を誤って public から pub に変えてしまいました。ビルドは成功しますが、nginx が見ている public/health.json が無いので 404 になります。

$ ./deploy.sh myapp-v1.2.0.tar.gz
[1/4] 展開: /var/www/myapp/releases/20261017020645-v1.2.0
[2/4] 切り替え: current -> 20261017020645-v1.2.0(前: 20261017020633-v1.1.0)
[3/4] 確認: /health.json
curl: (22) The requested URL returned error: 404
NG: 新しい版が応答しません。元に戻します
rollback: current -> 20261017020633-v1.1.0
$ echo $?
1
$ curl -sS http://127.0.0.1:24400/health.json
{"version":"v1.1.0","commit":"c76ef2f"}

nginx の error.log には、切り替えた直後のリクエストが次のように残っていました。

2026/10/17 02:06:38 [error] 974#974: *7 open() "/var/www/myapp/current/public/health.json" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /health.json HTTP/1.1", host: "127.0.0.1:24400"

curl -f は HTTP のエラー(400 以上)で終了コード 22 を返すので、スクリプトがそれを見て戻しています。ヘルスチェックが「新しい版番号」まで確かめているのがポイントで、200 が返るだけでは前の版が応答している場合と区別できません。

手動ロールバック:1つ前の版へ戻す(rollback.sh)

デプロイ後しばらくしてから不具合に気づいたときは、手で戻します。

#!/usr/bin/env bash
# ロールバック:current を、1つ前のリリースへ向け直す
set -euo pipefail
APP=/var/www/myapp
CUR=$(readlink "$APP/current")
PREV=$(ls -1d "$APP"/releases/* | sort | grep -B1 -xF "$CUR" | head -n 1)
if [ "$PREV" = "$CUR" ]; then echo "これより前のリリースがありません"; exit 1; fi
ln -sfn "$PREV" "$APP/current.tmp"
mv -T "$APP/current.tmp" "$APP/current"
echo "rollback: $(basename "$CUR") -> $(basename "$PREV")"
curl -fsS http://127.0.0.1:24400/health.json
$ ./rollback.sh
rollback: 20261017020633-v1.1.0 -> 20261017020628-v1.0.0
{"version":"v1.0.0","commit":"137ab2b"}

直した v1.2.1 をデプロイすると、元どおり前に進みます。さらに v1.3.0〜v1.5.0 を続けてデプロイすると、6個目で一番前の v1.0.0 が片付けられ、5個が残りました。

$ ./deploy.sh myapp-v1.5.0.tar.gz
[1/4] 展開: /var/www/myapp/releases/20261017020843-v1.5.0
[2/4] 切り替え: current -> 20261017020843-v1.5.0(前: 20261017020843-v1.4.0)
[3/4] 確認: /health.json
OK: {"version":"v1.5.0","commit":"e7fad12"}
[4/4] 片付け: 新しい順に 5 個だけ残す
$ ls -1 /var/www/myapp/releases
20261017020633-v1.1.0
20261017020651-v1.2.1
20261017020843-v1.3.0
20261017020843-v1.4.0
20261017020843-v1.5.0

残す数は、戻したい範囲で決めます。Capistrano のドキュメントでは、残すリリース数 keep_releases の既定値は 5 です。

ロールバックで戻らないもの

current を戻して元に戻るのは、releases/ の中にあるファイルだけです。次のものは別に考えます。

戻らないもの 理由 対策の例
データベースの構造・データ DB は releases/ の外にある 前の版でも動く形で列を足す。消すのは後のデプロイで
利用者がアップロードしたファイル 版ごとのフォルダに置くと、切り替えで見えなくなる 版の外(shared/ など)に置き、各版からリンクする
送ったメール・外部 API への処理 外の世界に出たものは取り消せない 機能をフラグで止める
ブラウザのキャッシュ 利用者の手元に前の CSS・JS が残る ファイル名に内容のハッシュを入れる

Capistrano のドキュメントでも、shared はデプロイをまたいで残すもの(DB の設定ファイルや、利用者のファイルの保存場所)を置き、各リリースからシンボリックリンクでつなぐ場所とされています。

切り替え方で404は出るか:300回切り替えて測った

current の付け替え方は何通りもあります。4つのクライアントが /health.json を取り続ける間に、2つの版の間で current を300回切り替え、返ったステータスを数えました(同じ測定を2回)。

3,917件rm してから ln -s で付け直した場合の 4041回目 11,172件中。2回目は 11,837件中 3,967件
0件ln -sfn で上書きした場合の 4041回目 7,694件・2回目 7,817件がすべて 200
0件一時リンクを作って mv -T で置き換えた場合の 4041回目 13,297件・2回目 11,530件がすべて 200
rm+ln     切り替え300回  リクエスト 11172件  200:7255  404:3917
ln -sfn   切り替え300回  リクエスト  7694件  200:7694
ln+mv -T  切り替え300回  リクエスト 13297件  200:13297

違いの理由は、strace(プログラムが OS に頼んだ処理を表示するツール)で見ると分かります。

# rm してから ln -s:消してから作るので、その間 current が無い
unlinkat(AT_FDCWD, "current", 0) = 0
symlinkat("/var/www/myapp/releases/20261017020651-v1.2.1", AT_FDCWD, "current") = 0

# ln -sfn(GNU coreutils 9.4):一時名で作ってから rename で置き換えている
symlinkat("/var/www/myapp/releases/20261017020651-v1.2.1", AT_FDCWD, "current") = -1 EEXIST (File exists)
symlinkat("/var/www/myapp/releases/20261017020651-v1.2.1", AT_FDCWD, "CumdvVmM") = 0
renameat(AT_FDCWD, "CumdvVmM", AT_FDCWD, "current") = 0

# ln -sfn current.tmp と mv -T:rename で置き換える
symlinkat("/var/www/myapp/releases/20261017020651-v1.2.1", AT_FDCWD, "current.tmp") = 0
renameat(AT_FDCWD, "current.tmp", AT_FDCWD, "current") = 0

Linux の rename(2) のマニュアルには、置き換え先がすでにあるとき「アトミックに置き換えられ、他のプロセスが置き換え先を見失う瞬間は無い」とあります。ln -sfn が一時名+rename で動くかはツールの実装しだいなので、この記事のスクリプトは mv -T で rename を明示しています。 -T は GNU coreutils の mv のオプションで、「最後の引数がディレクトリ(またはディレクトリへのリンク)でも特別扱いしない」という意味です。

エラーが出ないのに切り替わらない3つの書き方

current がディレクトリを指すリンクのとき、次の3つはどれも終了コード 0 で終わりますが、current の向きは変わりません。リンクを「たどった先」のフォルダの中に、新しいリンクが作られます。

$ ln -s releases/20261017020633-v1.1.0 current
$ echo $?; readlink current; ls -A releases/20261017020651-v1.2.1
0
/var/www/myapp/releases/20261017020651-v1.2.1
20261017020633-v1.1.0
public

$ ln -sf releases/20261017020633-v1.1.0 current
$ echo $?; readlink current; ls -A releases/20261017020651-v1.2.1
0
/var/www/myapp/releases/20261017020651-v1.2.1
20261017020633-v1.1.0
public

$ ln -sfn releases/20261017020633-v1.1.0 current.tmp; mv current.tmp current
$ echo $?; readlink current; ls -A releases/20261017020651-v1.2.1
0
/var/www/myapp/releases/20261017020651-v1.2.1
current.tmp
public
書き方 起きること 直し方
ln -s 新 current current の先のフォルダに、リンクが1つ増える -n を付けてリンク自体を対象にする
ln -sf 新 current 同じ。-f だけでは current をたどってしまう ln -sfn にする
mv current.tmp current current.tmp が、current の先のフォルダへ移動する mv -T にする

GNU coreutils のマニュアルでは、ln の -n は「最後の引数がディレクトリへのシンボリックリンクでも特別扱いせず、普通のファイルとして扱う」オプションです。シンボリックリンクの基本は関連記事「Linuxシンボリックリンクln -sの使い方|作成・削除・確認一覧」で扱っています。

手動・スクリプト・CI/CD:デプロイの3段階

デプロイのやり方は、多くのチームで次の順に育ちます。中でやっていることは同じ7手順で、人がやる部分が減っていくのが違いです。

段階 やり方 向いている場面 起きやすいこと
1. 手動 SSH でつないで、手でファイルを置き、再起動する 初めての公開・個人の検証 手順の抜け・人による違い
2. スクリプト deploy.sh・rollback.sh を1行で実行する 小さなチーム・サーバー数台 実行する人の PC の状態に左右される
3. CI/CD タグの push などで、ビルド〜デプロイが自動で動く 頻繁に出す・複数人で開発する 秘密情報(鍵)の管理が要る

1. 手動デプロイの例

scp myapp-v1.1.0.tar.gz deploy@server:/tmp/
ssh deploy@server
cd /var/www/myapp
mkdir releases/20261017020633-v1.1.0
tar -xzf /tmp/myapp-v1.1.0.tar.gz -C releases/20261017020633-v1.1.0
ln -sfn /var/www/myapp/releases/20261017020633-v1.1.0 current.tmp
mv -T current.tmp current
curl -fsS http://127.0.0.1:24400/health.json

2. スクリプトにする

上の手順をまとめたものが、前の節の deploy.sh と rollback.sh です。手順の抜けが無くなり、失敗したときに自動で戻せます。

3. CI/CD で自動にする(GitHub Actions の例)

CI/CD は、ビルド・テスト・デプロイを自動で回す仕組みです。次のワークフローは、v1.2.1 のようなタグを push すると、GitHub のマシンで build.sh を動かし、サーバーへ送って deploy.sh を実行します。

name: deploy
on:
  push:
    tags: ['v*']          # v1.2.1 のようなタグを push したら動く
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production   # 承認者などの保護ルールを付けられる
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0      # git describe --tags にタグの履歴が要る
      - name: ビルド
        run: ./build.sh
      - name: 転送とデプロイ
        env:
          SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
          KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
          HOST: ${{ secrets.DEPLOY_HOST }}
        run: |
          mkdir -p ~/.ssh
          printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
          printf '%s\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts
          PKG=myapp-${GITHUB_REF_NAME}.tar.gz
          scp "$PKG" deploy@"$HOST":/tmp/
          ssh deploy@"$HOST" "/opt/myapp/deploy.sh /tmp/$PKG"
書いたもの GitHub のドキュメントでの意味
actions/checkout@v7 リポジトリを取得するアクション。README の使用例は v7
environment: production 環境を使うジョブは、その環境の保護ルール(承認者・待ち時間・デプロイできるブランチとタグ)を満たしてから動く
secrets.DEPLOY_SSH_KEY 環境に登録したシークレットは、その環境を使うジョブだけが、保護ルールを通った後に読める
GITHUB_REF_NAME ワークフローを動かしたブランチまたはタグの短い名前(例:v1.2.1)

CI/CD の全体像と最初のパイプラインの作り方は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。

デプロイでよく起きる失敗と直し方

症状・エラー 原因 直し方
"/var/www/myapp/current/public/index.html" is not found (2: No such file or directory) current が無い、または成果物の中の配置がずれている readlink current と tar -tzf で中身の場所を確かめる
curl: (22) The requested URL returned error: 404 ヘルスチェックの URL が新しい版に無い 成果物に health.json などを必ず入れる
終了コード 0 なのに版が変わらない -n・-T なしで current をたどった ln -sfn + mv -T にする(前の節)
切り替えの瞬間だけ 404 が出る rm してから ln -s している 一時リンク+mv -T で置き換える
PHP などで、切り替えた後も前の版が動く アプリ側がパスを解決して覚えている nginx の $realpath_root で実際のパスを渡す。アプリのキャッシュを再読み込みする
デプロイ後に画像やアップロードが消えた 版ごとのフォルダに置いていた shared/ に置き、各版からリンクする
画面の見た目だけ前の版のまま ブラウザが前の CSS をキャッシュした ファイル名にハッシュや版を入れる
ロールバックしたら DB エラー 新しい版で DB の構造を変えた 前の版でも動く変更だけを先に出す

nginx のドキュメントでは、$realpath_root は「root または alias の値に対応する絶対パスで、シンボリックリンクをすべて実際のパスに解決したもの」です。PHP-FPM のようにパスを別のプロセスへ渡す構成で、current の代わりに実際のリリースのパスを渡すときに使われます。nginx の location の書き方は、関連記事「nginx location書き方|優先順位とtry_filesをcurlで検証」で扱っています。

デプロイ方式の種類:シンボリックリンクの次に知るもの

この記事のやり方は、1台のサーバーの中で版を切り替える方式です。サーバーが複数になると、切り替える単位が「フォルダ」から「サーバー」や「通信の振り分け先」に変わります。

方式 切り替える単位 ひとことで
インプレース 動いている場所に上書き いちばん単純。途中で失敗すると戻しにくい
シンボリックリンク(この記事) フォルダ 版を並べて置き、向きだけ変える
ブルーグリーン 環境まるごと2つ ロードバランサーの向きを新しい環境へ変える
ローリング サーバー1台ずつ 順に入れ替える。途中は新旧が混ざる
カナリア 通信の一部 一部の利用者だけ新しい版にして様子を見る

それぞれの仕組み・向いている場面・比較は、関連記事「デプロイ方式の種類と違い|ブルーグリーン・カナリア・ローリング比較」で扱っています。

文脈で変わる「デプロイ」

よく見る場面 「デプロイ」が指すもの
PaaS・ホスティング リポジトリへの push をきっかけに、ビルドと配置が自動で行われること
コンテナ イメージを作り、動いているコンテナを新しいイメージのものに入れ替えること
Kubernetes リソースの名前でもある。Deployment は Pod の集まりを管理し、kubectl rollout undo で前の版に戻せる

Kubernetes のドキュメントでは、Deployment は「アプリケーションの処理(多くは状態を持たないもの)を動かす Pod の集まりを管理する」ものと説明されています。コンテナでのデプロイの例は、関連記事「Next.jsをDockerでデプロイ|standaloneで421MB→63MB」で扱っています。

よくある質問

Q. デプロイとは何ですか。 A. 作ったプログラム(ビルドした成果物)をサーバーなどに置き、動く状態にする作業です。英語の deploy(配置する・展開する)から来ています。詳しくは「デプロイとは:意味と読み方」の節です。

Q. デプロイとリリースの違いは何ですか。 A. デプロイはサーバーを相手に「置いて動かす」こと、リリースは利用者を相手に「使えるように公開する」ことです。同時に起きることも多いですが、機能を隠してデプロイし、後で公開することもできます。

Q. ビルドとデプロイの違いは何ですか。 A. ビルドはソースコードから配れる形(実行ファイル・jar・tar.gz など)を作ること、デプロイはそれをサーバーに置いて動かすことです。ビルドが成功しても、置き方がずれるとデプロイは失敗します(v1.2.0 の例)。

Q. デプロイに失敗したらどうなりますか。 A. 新しい版が動かず、エラーやサービス停止につながります。この記事の deploy.sh のように、ヘルスチェックで新しい版番号を確かめ、失敗なら前の版へ自動で戻すと、止まる時間を短くできます。

Q. デプロイは誰がやるものですか。 A. 手動の段階では開発者やインフラ担当が行い、CI/CD の段階では仕組みが自動で行います。人は「どの版を出すか(タグを付ける)」「承認する」を担当します。

まとめ

  • デプロイは、ビルドした成果物をサーバーに置き、動く状態にすること。ビルド → デプロイ → リリースの順
  • releases/ に版を並べ、current の向きを変えると、デプロイもロールバックも向きの付け替えだけになる
  • 付け替えは ln -sfn で一時リンク → mv -T。rm してから作ると、300回で 3,917件の 404 が出た
  • -n・-T を忘れると、終了コード 0 のまま切り替わらない
  • ヘルスチェックは 新しい版番号まで確かめる。DB・アップロード・ブラウザのキャッシュはロールバックで戻らない
  • 手動 → スクリプト → CI/CD と、人がやる部分を減らしていく

参考・出典

確認日はいずれも 2026年10月17日です。

  • nginx「Module ngx_http_core_module」(root・disable_symlinks・$realpath_root・open_file_cache):https://nginx.org/en/docs/http/ngx_http_core_module.html
  • GNU coreutils「ln invocation」:https://www.gnu.org/software/coreutils/manual/html_node/ln-invocation.html
  • GNU coreutils「mv invocation」:https://www.gnu.org/software/coreutils/manual/html_node/mv-invocation.html
  • Linux man-pages「rename(2)」:https://man7.org/linux/man-pages/man2/rename.2.html
  • Capistrano「Structure」:https://capistranorb.com/documentation/getting-started/structure/
  • Capistrano「Configuration」:https://capistranorb.com/documentation/getting-started/configuration/
  • GitHub Docs「Managing environments for deployment」:https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments
  • GitHub Docs「Variables reference」:https://docs.github.com/en/actions/reference/workflows-and-actions/variables
  • actions/checkout(README):https://github.com/actions/checkout
  • Kubernetes「Deployments」:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「デプロイとは|ビルド・リリースとの違いとロールバックを実例で解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?