解説
GitHub ActionsのSecretsとVariablesはどう分ける?デプロイ設定を安全に管理する
GitHub ActionsのSecrets・Variables・envの役割を整理。登録範囲の違い、値を表示しない未設定チェック、設定が反映されないときの確認手順をサンプル付きで紹介するよ。

GitHub Actionsでデプロイを設定すると、「接続先のURLもSecretsに入れる?」「Variablesに鍵を書いても同じ?」と迷うことがあるよね。
まずは、秘密にする必要がある値をSecrets、公開されても困らない設定値をVariablesに分けよう。そのうえで、どの範囲に登録し、workflowからどう読み込むかをそろえると、設定ミスを探しやすくなるよ。
Secrets・Variables・envは役割が違う
| 仕組み | 主な用途 | workflowでの参照例 |
|---|---|---|
| Secrets | SSH秘密鍵、APIキー、パスワードなど | ${{ secrets.DEPLOY_SSH_KEY }} |
| Variables | 公開サイトのURL、ビルド対象名など、秘密ではない設定 | ${{ vars.SITE_URL }} |
| env | jobやstepで使う環境変数に値を渡す | Bashでは$SITE_URL |
SecretsとVariablesは登録先、envは実行時の受け渡し方、と分けて考えると整理しやすいね。envに書いたから秘密になるわけではないよ。値をworkflowへ直接書けば、その値はリポジトリのファイルに残る。
Variablesは機密情報の保管先ではなく、Secretsのようなログの自動マスクを前提にしない。公開URLでも社内の構成が分かる値など、公開したくない事情があればSecrets側へ分けよう。

設定の保管先と、runnerで使う環境変数を分けて描いた整理図。値の暗号化方式や通信経路そのものを表した図ではないよ。
まずはRepositoryから設定する
リポジトリの Settings → Secrets and variables → Actions を開くと、SecretsとVariablesのタブがある。
今回の例では、SecretsにDEPLOY_SSH_KEY、VariablesにSITE_URLを登録する。URLの例はhttps://example.com。鍵には実際の接続で使う秘密鍵を登録するけれど、記事やIssue、workflowの本文には貼らないでね。
SecretsにはOrganization、Repository、Environmentという登録範囲がある。最初はRepositoryに集めると場所を把握しやすい。ステージングと本番で鍵を分けたい場合は、Environmentを使い、job側のenvironmentと対応させよう。
同じ名前のSecretが複数の範囲にあると、EnvironmentがRepositoryより優先される。Repository側の鍵を更新したのに古い鍵が使われるときは、同名のEnvironment secretが残っていないか確認したいね。Environmentの保護ルールや利用可能な機能は、契約・リポジトリの公開範囲にもよるので、使う前に自分の条件で確認しよう。
envに渡して、値を出さずに未設定を検出する
以下は、登録した値をstepの環境変数として受け取り、設定があるか確認する小さな例だよ。SITE_URLはVariables、DEPLOY_SSH_KEYはSecretsに登録してから使う。
name: Check deploy configuration
on:
workflow_dispatch:
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-24.04
steps:
- name: Check required configuration
shell: bash
env:
SITE_URL: ${{ vars.SITE_URL }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
set -euo pipefail
test -n "$SITE_URL" || {
echo 'SITE_URL is not configured'
exit 1
}
test -n "$DEPLOY_SSH_KEY" || {
echo 'DEPLOY_SSH_KEY is not configured'
exit 1
}
echo 'Required configuration is present'
Secretが未設定なら、${{ secrets.DEPLOY_SSH_KEY }}の参照結果は空文字列になる。そこで、値そのものではなく「空ではないか」を調べる形にしているよ。
このサンプルは設定の存在を確かめるところまで。鍵の形式、接続先のホスト鍵、接続権限、デプロイ先の応答は別に検証する。実際のSSH接続では、ホスト鍵の照合を省略せず、使用する鍵やポートも確認しよう。
サンプルで確かめたこと
上のYAMLを読み取り、Bash部分を構文確認したうえで、環境変数を空にした場合と、架空のテスト文字列を渡した場合を実行した。両方未設定、URLだけ設定、鍵だけ設定では終了コード1。両方を設定した場合だけ終了コード0になり、渡したテスト文字列は標準出力・標準エラーに出なかったよ。
同じYAMLはダウンロード用サンプルでも用意している。手元のBash確認と、GitHub上でSecretを注入して実行することは別の確認なので、自分のリポジトリでも手動実行して確かめよう。
ログに出さないことも設計に含める
GitHubはSecretsをログでマスクするけれど、値を加工した結果や、想定外の出力まで常に隠れると考えないようにしよう。
echo "$DEPLOY_SSH_KEY"で値を表示して確認しない。envやprintenvで環境変数を丸ごと出力しない。set -xで秘密値を含むコマンドをトレースしない。- コマンドライン引数へ秘密値を直接渡すより、そのツールが対応する環境変数・標準入力などを使う。
- 鍵ファイルが必要なら権限を絞り、使い終えた一時ファイルを削除する。
問題を調べるときは、変数名、登録範囲、未設定かどうか、接続のどの段階で止まったかを記録すると、値を見せずに切り分けられるよ。認証情報が漏れた可能性がある場合は、ログから隠すだけでなく、発行元で失効・交換まで行おう。
「登録したのに使えない」ときの確認順
| 確認すること | 見直す場所 |
|---|---|
| 名前・参照先が一致している? | secrets.DEPLOY_SSH_KEYとvars.SITE_URLの取り違え、綴り |
| 同名の古いSecretがある? | Organization・Repository・Environmentの登録範囲 |
| 対象Environmentを指定した? | jobのenvironmentとEnvironment secrets |
| どのイベントで動いた? | forkからのpull request、Dependabot等のSecret提供条件 |
| workflowを呼び出している? | 再利用可能workflowへのSecretsの受け渡し |
| 登録後の実行を確認した? | 新しいrunでの注入と、失敗したstep |
通常、forkからのpull requestには、GITHUB_TOKENを除くSecretsは渡されない。Dependabotを起点とする実行や再利用可能workflowにも、それぞれ提供・引き継ぎの条件がある。空だったからといって、鍵をYAMLへ直書きして回避しないでね。
また、Secretsをif:の条件式から直接参照することはできない。必要なら公式資料に沿って環境変数へ渡して条件を組むか、上の例のようにstep内で未設定を確認する方法を選ぼう。
設定を変えたら、新しい実行で確かめよう
Organization・Repository secretsはworkflowがキューに入るとき、Environment secretsはそのEnvironmentを使うjobの開始時に読み取られる。値を登録・更新したら、新しい手動実行で結果を確認すると状況を整理しやすいね。
最後は「Secretの登録ができた」だけで終わらず、設定確認、接続、デプロイ、公開先の応答まで順番に確認しよう。Git管理対象から外しただけのファイルの扱いは、.gitignoreと追跡済みファイルの違いも参考にしてね。Docker側の設定が反映されない場合は、Composeの.envとenv_fileの違いと分けて確認すると、原因を追いやすくなるよ。

みんなのコメント 0
感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。
最初のコメントをどうぞ。