GitHub

GitHub Actionsの環境変数|env・vars・secretsの使い分けと受け渡し

GitHub Actionsの環境変数|env・vars・secretsの使い分けと受け渡し

GitHub Actionsで変数がうまく読めないとき、原因の大半は書式ではなく「どの層に置いたか」です。ワークフローのenvに書いた値はruns-onでは展開されず、GITHUB_ENVへ書き込んだ値は同じステップの中では読めません。本記事では、値の入れ物をenv・vars・secretsの3系統に整理したうえで、workflow・job・stepの3階層スコープと優先順位、ステップ間・ジョブ間で値を渡す2つのファイル、そして式展開が引き起こすスクリプトインジェクションの回避手順までを扱います。GitHub Actions自体の概要やワークフローの組み方はGitHub Actionsでできること・使い方の解説に譲り、ここでは変数まわりだけを実装できる粒度で書きます。

まとめ:env・vars・secretsの振り分けとスコープ設計の結論

先に結論を書きます。3系統の選択は「ログに出てよいか」と「環境ごとに変えるか」の2問で決まります。ログに出てよく環境でも変わらない値はワークフローファイルのenv、ログに出てよいが環境で変わる値は構成変数vars、漏れると被害が出る値はsecrets。この振り分けを最初に決めておけば、後から置き場所を移す作業はほぼ発生しません。

スコープは3階層です。ワークフロー全体、ジョブ、ステップの順に狭くなり、同じ名前があれば狭いほうが勝ちます。組織・リポジトリ・Environmentの3層で持つvarsとsecretsも同じ発想で、Environmentが最も強い層になります。

設計上の落とし穴を1つだけ挙げるなら、envコンテキストの可用性でしょう。envはjobs.<job_id>.ifやruns-on、strategyでは使えません。一方varsはこれらのキーでも展開されます。ジョブの実行可否やランナー種別を変数で切り替えたいなら、置き場所はenvではなくvarsです。

値の受け渡しは環境ファイルが担当します。ステップをまたぐ受け渡しはGITHUB_ENV、ステップの戻り値として名前を付けて渡すならGITHUB_OUTPUT。どちらも書き込んだステップ自身は新しい値を読めません。最後に安全面として、外部から来る文字列をrunの中へ直接展開しないでください。中間の環境変数へ一度退避させる。この一手間だけで、ワークフローのスクリプトインジェクションはほぼ塞がります。

env・vars・secretsの3系統と保存場所・ログ出力の違い

GitHub Actionsで「環境変数」と呼ばれるものは、実際には性質の異なる3つの機構をまとめた呼び方です。混同したまま設計すると、機密をログに出したり、変更のたびにコードを書き換えたりする構成になります。まず3系統の輪郭を分けます。OSプロセスとしての環境変数の仕組みそのものは環境変数の実体とOS別の設定方法の解説にまとめました。

ワークフローファイル内で完結するenvの書き方と適した用途の範囲

envはYAMLに直接書く方式です。値はリポジトリのコードとして残るため、誰がいつ変えたかがコミット履歴から追えます。ビルド時の言語バージョン、成果物の名前、タイムゾーンのように、公開されても困らず環境で変わらない定数はここが置き場所になります。

env:
  APP_ENV: production
  TZ: Asia/Tokyo
jobs:
  build:
    env:
      APP_ENV: staging
    steps:
      - run: echo "$APP_ENV"

この例で出力されるのはstagingです。ジョブ側の定義がワークフロー側を上書きする関係です。envの弱点は、値を変えるたびにコミットが必要な点と、リポジトリを横断して同じ値を共有できない点にあります。開発者のローカル環境で使う.envファイルとは別物で、こちらの仕組みはdotenvで環境変数を管理する仕組みの解説にまとめています。

構成変数varsの定義場所と組織1,000個・リポジトリ500個の上限

varsは画面またはAPIで登録する平文の変数です。組織・リポジトリ・Environmentの3層が配置先です。vars.NAMEの形で参照し、値はログにそのまま出力されます。デプロイ先のホスト名、機能フラグ、通知先のチャンネル名など、秘匿する必要はないが環境やリポジトリごとに変えたい値がここに入ります。

上限は公式ドキュメントに数値で示されています。2026年8月時点の記載では、組織変数が最大1,000個、リポジトリ変数が最大500個、Environmentの変数が最大100個。1つの変数は最大48KBで、組織変数とリポジトリ変数の合計サイズはワークフロー実行あたり256KBまでです。命名にも制約があり、英数字とアンダースコアのみ、数字始まり不可、GITHUB_で始まる名前は使えません。参照時に大文字小文字は区別されないため、API_URLとapi_urlを別物として登録する運用は破綻します。

secretsの自動マスキングが効かなくなる3つの条件と回避策

secretsは暗号化して保存され、ログに出力されると伏せ字へ置き換わります。この自動マスクは便利ですが、公式ドキュメントは保証しないと明言しています。理由は、マスク処理が登録した値との完全一致に依存しているためです。値そのものをリポジトリ・組織・環境のどこへ置くか、伏せ字が効いていても外へ出てしまう経路をどう塞ぐかは、GitHub ActionsのSecrets管理で置き場所の選定と漏えい経路を整理した解説にまとめています。

効かなくなる条件は3つあります。1つ目は、JSONやYAMLのような構造化データの中に埋め込まれた場合。整形やエスケープで文字列が変形すると一致判定を外れます。2つ目は、Base64やURLエンコードを通した値です。変換後の文字列は別の値なので、必要ならそれ自体を追加登録するしかありません。3つ目は、シークレットを分割して使う場合。長い文字列を切り出して使うと、断片は一致しません。

回避策は単純で、ログへ出す前に自分でマスクを宣言することです。ワークフローコマンドのadd-maskに値を渡すと、以降の出力でその文字列が伏せられます。加えて、シークレットをコマンドラインの引数として渡さないでください。公式ドキュメントは、同じランナーで動く別ジョブからps x -wで引数が見える点を挙げ、環境変数か標準入力を使うよう案内しています。クラウド側のシークレット保管と組み合わせる構成なら、AWS Secrets Managerの料金とローテーションの解説が判断材料になります。

workflow・job・stepの3階層スコープと上書きの優先順位

変数が読めない、想定と違う値になるという問題は、ほとんどが階層の理解不足から起きます。GitHub Actionsのスコープは2系統あり、YAML上の3階層と、変数登録先の3層が別々に動きます。

envの3階層とstepが常に勝つという上書き規則の確認方法

YAML上のenvは、ワークフロー直下・jobs.<job_id>.env・steps配下の3階層で定義できます。同じ名前があれば、より内側の定義が有効です。ステップが最も強く、ワークフロー直下が最も弱い。この規則に例外はありません。

デバッグの手順としては、疑わしいステップの直前に環境一覧を出力するステップを1つ挟むのが確実です。ランナー上の実際の環境をそのまま吐き出すため、どの階層の値が勝ったかが一目で分かります。なおCIは常にtrue、RUNNER_OSはLinux・Windows・macOSのいずれかが入るなど、既定の環境変数も同じ一覧に現れます。GITHUB_ACTORやGITHUB_SHAを自前の変数名で上書きしようとして失敗するのは、命名規則でGITHUB_接頭辞が禁じられているためです。

envコンテキストが使えないキーとvarsで代替する判断の分かれ目

ここが本記事で最も実務に効く箇所です。公式のコンテキスト可用性表によれば、envコンテキストはjobs.<job_id>.if、runs-on、jobs.<job_id>.name、concurrency、container.image、strategy、timeout-minutesでは使えません。使えるのはステップ側のif・run・with・working-directoryなどが中心です。

理由は評価のタイミングにあります。ジョブをランナーへ振り分ける前に評価されるキーでは、ランナー上でしか存在しない環境変数を参照できません。envコンテキストは、ジョブの実行が始まったランナーでのみ利用可能だと説明されています。

したがって判断は明快です。ジョブの実行可否、ランナーのラベル、マトリックスビルドの組み合わせ、タイムアウト値を変数で切り替えたいなら、envではなくvarsを使ってください。varsはワークフローの読み込み段階で解決されるため、これらのキーでも展開されます。ifに書いた条件が効かないという相談の大半は、この可用性の差が原因です。

Environment単位の上書きで本番と検証を切り替える設定手順

変数登録先の3層は、組織・リポジトリ・Environmentの順に優先度が高い関係です。同じ名前の変数が3層すべてにあれば、Environmentの値が採用されます。この性質を使うと、ワークフローのYAMLを1本に保ったまま、デプロイ先だけを切り替える構成が組めます。

具体的には、ジョブにenvironment: productionを指定し、同名の変数とシークレットをproductionとstagingの両方へ登録します。YAML側の参照はvars.DEPLOY_HOSTのままで変わりません。Environmentには承認者の設定や待機時間、対象ブランチの制限も付けられるため、本番デプロイのゲートを兼ねられます。ワークフローそのものを複数リポジトリで共有したい場合は、呼び出し境界での受け渡しを扱うReusable Workflowsのinputs・secrets・outputsの解説を併せて設計してください。

GITHUB_ENVとGITHUB_OUTPUTを使ったステップ間の値の受け渡し

実行してみないと決まらない値、たとえばビルド番号やコミットから生成したタグは、YAMLに書けません。この種の値をステップ間で渡すのが環境ファイルです。書き込み先が2つあり、用途が分かれています。

GITHUB_ENVの書式と同一ステップでは読めないという制約の扱い

GITHUB_ENVはランナー上のファイルパスで、ここへキーと値の行を追記すると、後続ステップの環境変数として現れます。

- name: compute version
  run: echo "APP_VERSION=1.4.2" >> "$GITHUB_ENV"
- name: use version
  run: echo "build $APP_VERSION"

注意点は1つです。公式ドキュメントは「環境変数を作成または更新するステップは、新しい値にアクセスできませんが、ジョブにおける後続のすべてのステップはアクセスできます」と記しています。書いた直後に同じステップで読もうとして空になる、という詰まり方が定番です。同じステップ内で使い回したいだけなら、シェル変数として持てば済みます。

複数行の値を渡すデリミタ構文と任意入力で使ってはいけない理由

リリースノートやテスト結果のように改行を含む値は、デリミタ構文で囲みます。変数名のあとに区切り記号を宣言し、値を挟み、同じ区切り記号の行で閉じる形です。

- name: set notes
  run: |
    {
      echo "NOTES<<EOF"
      cat release-notes.txt
      echo "EOF"
    } >> "$GITHUB_ENV"

公式ドキュメントは、区切り記号が値の中に単独の行として現れないことを確認するよう求め、そのうえで「値が完全に任意の場合は、この形式を使用しないでください」と警告しています。外部から入る文字列に区切り記号だけの行を混ぜられると、そこで値が閉じられ、以降の行が環境ファイルへの新しい命令として解釈されるためです。任意入力を扱うなら、ファイルに書き出してパスだけを渡す構成へ切り替えてください。

GITHUB_OUTPUTでステップの戻り値に名前を付けて参照する方法

ステップの結果を戻り値として扱いたい場合はGITHUB_OUTPUTです。参照側はsteps.meta.outputs.tagのようにステップIDを含む形になるため、値がどのステップ由来かがYAMLの上で追跡できます。

- id: meta
  run: echo "tag=v1.4.2" >> "$GITHUB_OUTPUT"
- name: show tag
  run: echo "${{ steps.meta.outputs.tag }}"

2つの使い分けは、追跡可能性で決めてください。GITHUB_ENVはジョブ内のすべての後続ステップへ暗黙に効くため、便利な反面、どこで設定された値かが読み取れなくなります。値の出どころを明示したいならGITHUB_OUTPUT、多数のステップが共通で参照する設定値ならGITHUB_ENV。ジョブをまたぐ場合は、さらにジョブのoutputsへ引き上げる必要があります。実行パスへディレクトリを追加するGITHUB_PATHも同じ環境ファイル方式で、追記した内容は後続ステップから有効です。ワークフロー全体の組み立て方はGitHub Actionsでビルドと自動テストを設定する手順で扱っています。

式展開の評価順とスクリプトインジェクションを安全に塞ぐ書き方

変数まわりで最も被害が大きい事故は、値が読めないことではなく、外部の文字列がコマンドとして実行されることです。原因は展開のタイミングにあります。

runの生成前に式が解決されるという評価順と危険なコンテキスト

波かっこ2つで囲む式は、シェルスクリプトが組み立てられる前に文字列として置換される仕様です。つまりrunに書いたgithub.event.pull_request.titleの参照は、実行時にはタイトルの中身がそのままスクリプトの一部になります。プルリクエストのタイトルは外部の誰でも書き換えられるため、ここにシェルの制御文字を入れられると任意のコマンドが動きます。

危険なのはタイトルだけではありません。issueの本文、コメント、ブランチ名、コミットメッセージのように、リポジトリ外の利用者が値を決められるコンテキストはすべて同じ性質を持ちます。判定の基準は「自社のメンバー以外が値を決められるか」の一点です。

中間の環境変数へ退避させる公式推奨パターンと安全な書き換えの手順

公式ドキュメントが推奨する対処は、式の値を中間環境変数に設定してから参照する方法です。envで受けた値はスクリプトの組み立てではなくプロセスの環境として渡るため、中身がコマンドとして解釈されません。

- name: check title
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "$PR_TITLE" | grep -q "WIP"

書き換えの手順は機械的です。runブロック内の式を探し、外部入力を含むものだけをenvへ移し、本文をシェル変数の参照へ置き換える。式をrunから完全に追放する必要はありません。github.shaやvarsのように値を外部が決められないものは、そのままで差し支えないためです。サードパーティのアクションを経由した混入も同じ経路で起こるため、参照をコミットSHAで固定するSHA Pinningによるアクション固定の解説と併用すると、供給元と入力の両側を押さえられます。

値の置き場所を決める運用時の判断表とやってはいけない3つの構成

ここまでの規則を、設計時に引ける形へ落とします。判断は値の性質から始めてください。置き場所から考えると、必ずどこかで破綻します。

ログ出力と環境差の2軸で置き場所を決める実務の対応表と適用条件

値の性質 置き場所 選ぶ理由
公開可・環境で不変 ワークフローのenv 変更履歴が追える
公開可・環境で変わる Environmentのvars 環境単位で上書き可
複数リポジトリで共通 組織のvars 一箇所で改訂できる
漏えいで被害が出る Environmentのsecrets ログで自動マスク
実行中に確定する GITHUB_ENV 後続ステップへ伝播
ステップの戻り値 GITHUB_OUTPUT 出どころを追跡可

表の使い方は上から順に判定するだけです。ジョブの実行可否やランナー種別を切り替える値だけは例外で、envに置くとifやruns-onで展開されないため、公開可であってもvarsを選んでください。

secretsに寄せすぎる構成が保守コストを押し上げる具体的な症状

迷ったらsecretsという運用は避けるべきです。理由は3つあり、いずれも運用フェーズで効いてきます。第一に、値を後から確認できません。設定ミスの調査で登録値を読めないため、切り分けが長引く原因です。第二に、ログが伏せ字だらけになり、デプロイ先のホスト名すら読めない状態でトラブル対応をすることになります。第三に、Environmentのシークレットには数の制約があり、本来は平文でよい値が枠を消費します。

判定の線引きははっきりしています。その値が第三者に見えたとき、単独で不正な操作が可能になるか。可能ならsecrets、そうでなければvars。ホスト名やバケット名は、それ単体では認証を通せないので後者です。この線引きを守ると、障害時のログがそのまま読める状態を保てます。

組織変数の乱立と256KB上限が招く実行時エラーの安全な回避設計

もう1つの失敗は、組織レベルへ変数を積み上げる構成です。組織変数は全リポジトリのワークフローへ配布されるため、使っていないリポジトリでも合計サイズを消費します。組織変数とリポジトリ変数の合計は、ワークフロー実行あたり256KBが上限です。1変数48KBの値をいくつか置けば、この枠は現実的な範囲で埋まります。

設計としては、組織レベルには3つ以上のリポジトリで同じ値を使うものだけを置き、それ以外はリポジトリまたはEnvironmentへ降ろしてください。JSON設定を丸ごと1変数へ詰め込む運用も枠を圧迫するので、ファイルとしてリポジトリに置き、パスだけを変数で渡す形へ切り替えます。

変数の置き場所は一度決めると触る機会が減り、担当者の交代とともに根拠が失われがちな領域です。CI/CDそのものの位置づけを整理したい場合はCI/CDの仕組みと導入判断の解説が前提の整理に使えます。設計の見直しや、社内で回せる状態までの引き上げを外部に任せる選択肢としては、保守運用と内製化支援のサービスで扱っている範囲が該当します。

よくある質問

GitHub Actionsの環境変数について、検索でよく調べられている質問に答えます。

GitHub Actionsで環境変数を設定する方法は何種類ありますか?

大きく3種類です。1つ目はワークフローファイルへenvキーで直接書く方法で、配置先はワークフロー・ジョブ・ステップの3階層です。2つ目は画面またはAPIから構成変数として登録する方法で、組織・リポジトリ・Environmentの3層がありvarsコンテキストで参照します。3つ目は実行中にGITHUB_ENVへ追記する方法で、ビルド番号のように実行してみないと決まらない値に使います。機密値はこれらとは別枠のシークレットへ登録してください。用途が重ならないため、どれか1つに寄せる設計は保守しづらくなります。

GITHUB_ENVで設定した値が同じステップで読めないのはなぜですか?

仕様どおりの動作です。公式ドキュメントは、環境変数を作成または更新したステップ自身は新しい値へアクセスできず、同じジョブの後続ステップからアクセスできると明記しています。ランナーは各ステップを開始する時点で環境ファイルを読み込むため、実行中のステップには反映されません。同一ステップ内で値を使い回したいだけなら、シェル変数として保持すれば済みます。後続ステップへ渡す必要がある場合のみ、環境ファイルへの追記を使ってください。

secretsとvariablesはどちらを使えばよいですか?

その値が第三者に見えたとき、単独で不正な操作が可能になるかどうかで決めます。APIキー、トークン、パスワード、秘密鍵はsecretsです。デプロイ先のホスト名、バケット名、機能フラグ、通知チャンネル名は、それだけでは認証を通せないためvariablesで十分です。variablesは後から値を確認でき、ログにも出るため、設定ミスの調査が容易になります。すべてsecretsへ寄せると、ログが伏せ字だらけになり障害対応の時間が伸びます。

環境変数の値がログでマスクされないことはありますか?

答えは「はい」です。公式ドキュメントは、自動的な伏せ字処理が登録値との完全一致に依存するため保証されないと記しています。JSONやYAMLへ埋め込まれて整形やエスケープを受けた場合、Base64やURLエンコードで変換した場合、値を分割して使った場合はマスクを外れます。対処としては、変換後の値も別のシークレットとして登録するか、ワークフローコマンドのadd-maskで明示的に伏せ字の対象へ追加してください。シークレットをコマンドライン引数として渡す構成も、プロセス一覧から見えるため避けます。

.envファイルをGitHub Actionsで読み込ませてもよいですか?

機密を含まない設定値であれば問題ありません。ただしファイルの中身をそのままGITHUB_ENVへ流し込む書き方は、値に改行や区切り記号と同じ行が混ざったときに環境ファイルの構文を壊します。読み込むならキーを限定し、想定した名前だけを取り出してください。機密値については、.envをリポジトリへコミットする運用自体が事故のもとなので、シークレットへ登録して環境変数として渡す形に切り替えます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

ほか 7 件の記事からもリンクされています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.08 テックブログ 大阪公立大学のランサムウェア被害と仮想化基盤の停止|全授業休講に至った経緯とバックアップを守る設定
  3. 2024.11.08 テックブログ OpenAPI GeneratorでJavaコードを自動生成する方法|CLI導入からSpring・ライブラリ選択まで
  4. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次