dotenvx 入門:.env を暗号化して「そのまま commit できる形」にする

dotenvx 入門:.env を暗号化して「そのまま commit できる形」にする

#セキュリティ#Web開発#開発環境

.env ファイルは便利です。 APIキーやDB接続文字列をファイルに書き、dotenv などで読み込めば、アプリケーションから環境変数として参照できます。 ところが、チームで開発を始めた途端に困ることが増えます。 新しく入ったメンバーに .env の中身をどう渡すか、本番とステージングの値をどう分けるか、誰かが誤って .env をコミットしたらどうするか、といった問題です。

こうした運用を見直す選択肢の一つが、dotenvxです。 dotenv の作者が作ったツールで、.env の値を暗号化したままリポジトリにコミットし、実行時に秘密鍵で復号して環境変数として渡します。

この記事では、.env 運用で起きがちな問題を整理したうえで、dotenv との違い、dotenvx の導入、暗号化の仕組み、CIや本番での使い方、環境ごとの分け方までを順に説明します。 対象は .env を使ったことがあるエンジニアで、dotenvx の事前知識は不要です。 記述は2026年10月26日時点の dotenvx v2.38.2 と dotenv v18 系の公式資料に基づきます。

dotenvx とは?

dotenvx は、.env ファイルの読み込みと暗号化を扱うツールです。 CLI とライブラリの両方で利用できます。 ライセンスは BSD-3-Clause で、ソースコードは GitHub で公開されています。

CLI では、dotenvx run -- <コマンド> のように、実行したいコマンドの前に付けて使います。 そのため、Node.js だけでなく、Python、Ruby、Go、シェルスクリプトなど、環境変数を読むプログラムならどれにでも使えます。

提供する機能は次の三つに整理できます。

機能内容
どこでも動く言語やフレームワークを問わず、.env を読み込んでコマンドに環境変数を渡す
複数環境.env.production や .env.staging を切り替えたり合成したりできる
暗号化.env の値を公開鍵で暗号化し、秘密鍵を持つ環境だけが復号できる

現行の dotenv も CLI と複数ファイルの読み込みに対応しています。 三つ目の暗号化が、dotenvx を選ぶ理由の中心です。

.env 運用で起きがちな問題

暗号化の話に入る前に、.env をそのまま使う運用で何が起きるかを押さえておきます。

中身の共有経路が決まらない

.env は .gitignore に入れるのが定石です。 その結果、ファイルの中身はリポジトリの外で受け渡すことになります。 よくあるのは Slack の DM や、共有ドキュメントへの貼り付けです。 貼り付けた先には平文のシークレットが残り、閲覧権限や保存期間の管理が必要になります。

環境ごとの差分が追えない

本番、ステージング、開発で値が違う変数は珍しくありません。 各環境の .env を誰かのローカルや CI の設定画面にだけ置いていると、「本番にはあるがステージングにはない変数」がいつ追加されたのか追えません。 .env.example を用意しても、更新が漏れると実際の .env とズレていきます。

誤コミットの事故が起きる

.gitignore に入れ忘れた、別名でコピーした、という理由で .env がコミットされる事故は定期的に起きます。 一度プッシュされたシークレットは、履歴を書き換えても漏れたものとして扱い、ローテーションするしかありません。

dotenvx で暗号化した .env を Git で管理すると、ファイルの共有と変更履歴の管理をまとめられます。 暗号化済みの値は秘密鍵なしでは復号できませんが、平文を追記したままコミットしないための確認は引き続き必要です。

dotenv と何が違う?

dotenv を使っている人向けに、違いを表にまとめます。 dotenv にも v18.0.0 で CLI が追加されているため、CLI の有無ではなく、暗号化や鍵管理の機能が主な違いです(dotenv の変更履歴)。

観点dotenvdotenvx
形態Node.js のライブラリと CLICLI とライブラリ
CLI の対象環境変数を読む各言語のプログラム環境変数を読む各言語のプログラム
.env の扱い平文。.gitignore に入れる暗号化してコミットできる
複数環境path や CLI の -f で指定-f オプションや鍵名の接尾辞で切り替え
変数展開別パッケージ(dotenv-expand)が必要標準で対応
CLI の実行例dotenv run -- node index.jsdotenvx run -- node index.js

すでに環境変数を参照しているアプリケーションなら、その参照部分を変える必要はありません。 process.env.API_KEY や os.environ["API_KEY"] はそのまま使えます。 起動コマンドを dotenvx 経由にし、リポジトリに置く .env を暗号化します。

インストールと最初の一歩

インストール方法は複数あります。 プロジェクト単位で入れるなら npm、マシン全体で使うなら brew か curl が手軽です。

# npm(プロジェクトに追加)
npm install @dotenvx/dotenvx --save

# Homebrew
brew tap dotenvx/brew
brew trust dotenvx/brew
brew install dotenvx

# インストールスクリプト
curl -sfS https://dotenvx.sh | sh

導入方法の詳細は公式インストール手順を参照してください。 まずは空の作業ディレクトリで、暗号化せずに動かしてみます。

echo "HELLO=World" > .env
echo "console.log('Hello ' + process.env.HELLO)" > index.js

npx dotenvx run -- node index.js

dotenvx の読み込みログに続いて、次のように表示されれば成功です。 ログの書式はバージョンによって異なります。

Hello World

-- の後ろが実行するコマンドです。 Python なら npx dotenvx run -- python app.py、Rails なら npx dotenvx run -- bin/rails server と書けます。 Homebrew などでマシン全体にインストールした場合は、npx を省いて実行できます。 npm start のような既存のスクリプトの前に付けるだけなので、package.json の scripts に組み込むのも簡単です。

{
  "scripts": {
    "dev": "dotenvx run -- next dev"
  }
}

.env を暗号化する

ここからが本題です。 先ほどの .env を暗号化します。

npx dotenvx encrypt

初回の対話操作では、秘密鍵の保存先を尋ねられます。 この記事では「Local」、続いて「File .env.keys」を選びます。 OS のシークレットストアなどを選ぶと保存先が変わるため、以降は .env.keys に保存する場合の手順です(公式の保存手順)。

.env の中身を見ると、値が暗号文に置き換わっています。

#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/docs/quickstart/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
# .env
HELLO="encrypted:BDqDBibm4wsYqMpCjTQ6BsDHmMadg9K3dAt+Z9HPMfLEIRVz50hmLXPXRuDBXaJi/LwWYEVUNiq0HISrslzQPaoyS8Lotg3gFWJTsNCdOWnqpjF2xNUX2RQiP05kAbEXM6MWVjDr"

ファイルの先頭に DOTENV_PUBLIC_KEY が入り、各値は encrypted: で始まる文字列になりました。 同時に .env.keys というファイルが作られ、そこに秘密鍵 DOTENV_PRIVATE_KEY が書かれています。

公開鍵と秘密鍵の役割

暗号化に使う公開鍵と、復号に使う秘密鍵には、次の役割があります。

  • 公開鍵 DOTENV_PUBLIC_KEY: 値を暗号化するための鍵。.env の先頭に書かれ、リポジトリにコミットする
  • 秘密鍵 DOTENV_PRIVATE_KEY: 暗号文を復号するための鍵。.env.keys に置かれ、絶対にコミットしない

内部では、楕円曲線 secp256k1 を使う公開鍵暗号と AES-256 を組み合わせた方式(ECIES)で値を暗号化します。 secp256k1 は Bitcoin でも使われている楕円曲線で、詳細は公式のホワイトペーパーにまとまっています。 利用者として押さえておくべきことは、「暗号化は公開鍵だけでできる」「復号には秘密鍵が要る」の二点です。

.env.keys を gitignore する

.env.keys は必ず .gitignore に入れます。 .gitignore に次の行を追加してください。

.env.keys

.env 自体は、秘密の値が暗号化されていることを確認してから .gitignore の除外対象から外し、コミットします。 変数名やコメントは暗号化されないため、コメントに秘密の値を書かないようにします。 .env.keys は Git の外で保管し、鍵を失わないようにバックアップも用意します。

秘密鍵を確認したいときは、keypair コマンドを使います。

npx dotenvx keypair DOTENV_PRIVATE_KEY

秘密鍵なしで値を追加できる

公開鍵だけで暗号化できるということは、秘密鍵を持っていない人でも新しい値を追加できるということです。 set コマンドを使うと、暗号化した状態で値を書き込めます。

npx dotenvx set API_KEY "sk-xxxx"
set API_KEY with encryption (.env)

たとえば外部サービスの担当者が発行したキーを、復号権限を持たないメンバーがそのままリポジトリに追加する、という運用ができます。 値を直接 .env に平文で書いてから encrypt を再実行しても同じ結果になります。

平文に戻す

中身を確認したいときは decrypt を使います。 --stdout を付けると、ファイルを書き換えずに標準出力に表示できます。

npx dotenvx decrypt --stdout

--stdout なしで実行すると .env 自体が平文に戻るので、確認後に再度 encrypt するのを忘れないようにします。

本番・CI で使う

一つの暗号化ファイルを使う場合、本番や CI に渡すのは秘密鍵一つです。 dotenvx run は、環境変数 DOTENV_PRIVATE_KEY が設定されていれば、それを使って復号します。

DOTENV_PRIVATE_KEY="<秘密鍵>" npx dotenvx run -- node index.js

GitHub Actions なら、リポジトリの Secrets に DOTENV_PRIVATE_KEY を登録し、ステップの env で渡します。

- name: Run tests
  env:
    DOTENV_PRIVATE_KEY: ${{ secrets.DOTENV_PRIVATE_KEY }}
  run: npx dotenvx run -- npm test

Docker でも同じです。 コンテナ起動時に DOTENV_PRIVATE_KEY を環境変数として渡し、CMD を dotenvx run 経由にします。 暗号化済みの .env はイメージに含め、.env.keys は .dockerignore に追加してイメージに含めないようにします。

RUN npm install -g @dotenvx/dotenvx
CMD ["dotenvx", "run", "--", "node", "index.js"]
docker run -e DOTENV_PRIVATE_KEY="<秘密鍵>" my-app

この構成なら、CI やホスティングサービスの設定画面に変数を一つずつ登録していた作業が、秘密鍵一つの登録に変わります。 変数を追加するときも、.env の値を暗号化してコミットし、デプロイすればよく、設定画面を触る必要がありません。

環境ごとに分ける

本番とステージングで値を分けたい場合は、ファイル名に環境名を付けます。

echo "HELLO=Production" > .env.production
npx dotenvx encrypt -f .env.production

-f オプションで対象ファイルを指定します。 保存先を尋ねられたら、ここでも「Local → File .env.keys」を選びます。 .env.keys には DOTENV_PRIVATE_KEY_PRODUCTION という名前で別の秘密鍵が追加されます。 この手順では、.env と .env.production がそれぞれ別の鍵ペアを持ちます。

実行時に -f でファイルを指定しない場合は、設定した秘密鍵の環境変数名から対象ファイルが選ばれます。

DOTENV_PRIVATE_KEY_PRODUCTION="<本番の秘密鍵>" npx dotenvx run -- node index.js
Hello Production

DOTENV_PRIVATE_KEY_PRODUCTION が設定されていると、dotenvx は .env.production を探して復号します。 _STAGING なら .env.staging、という規則です。

複数ファイルを合成する

共通の値は .env に、環境固有の値は .env.production に置く、という分け方もできます。 両方の秘密鍵を設定し、-f で読み込み順を明示します。

DOTENV_PRIVATE_KEY="<共通の秘密鍵>" \
DOTENV_PRIVATE_KEY_PRODUCTION="<本番の秘密鍵>" \
npx dotenvx run -f .env.production -f .env -- node index.js
Hello Production

同じ変数が両方にある場合は、先に読み込まれたファイルの値が優先されます。 上の例では .env.production を先に指定しているので、実行前の環境に HELLO がなければ、値は Production になります。 秘密鍵の環境変数をコマンドに書く順番に頼らず、ファイルの優先順位は -f の順番で指定します。

すでにシェルや CI に設定されている環境変数は、どのファイルよりも優先されます。 --overload を付けると後から読んだファイルが優先されますが、シェルや CI の既存の環境変数も上書きする点に注意してください。 詳しくは公式 README の複数環境の説明を参照してください。

向いている場面、向かない場面

dotenvx が解決するのは、「平文のシークレットをどう配るか」という問題です。 代わりに、「ファイルごとの秘密鍵をどう守るか」という問題が残ります。 この置き換えが割に合うかどうかで、向き不向きが決まります。

向いている場面

  • 数人から十数人のチームで、.env の受け渡しを Slack やドキュメント経由でやっている
  • 開発、ステージング、本番など複数環境の値を Git の履歴で追いたい
  • Node.js、Python、Ruby など言語が混在していて、共通の仕組みが欲しい
  • CI やホスティングサービスに登録するシークレットを、ファイルごとの秘密鍵にまとめたい
  • Vault やクラウドの Secrets Manager を入れるほどの規模や予算ではない

向かない場面

ここまでの .env.keys を使う構成だけでは、次の要件は満たせません。

  • シークレットの自動ローテーションが必要
  • 人ごと、サービスごとに細かいアクセス権限を分けたい
  • 監査ログや承認フローが要件に入っている

これらが要件なら、HashiCorp Vault や AWS Secrets Manager なども含め、必要な権限管理や監査機能を比較します。 すでに Secrets Manager を運用している場合も、既存の構成を置き換える利点があるかを先に確認します。

dotenvx は秘密鍵が漏れると、その鍵で暗号化された履歴上のすべての値が復号できてしまいます。 秘密鍵の保管場所に加え、漏えい時に暗号鍵を更新し、復号され得る API キーやパスワードを失効・再発行する手順も決めておく必要があります。 暗号鍵を作り直しても、古い鍵で読める過去の暗号文は保護できません。

なお、2026年6月に始まった v2 系では、鍵の保管方法や外部サービスとの連携も増えています。 秘密鍵の保存先には、OS のシークレットストア(macOS の Keychain など)や、有料のリモート鍵管理サービスである Armor も選べます。 また、.env に 1Password や Bitwarden の参照(op://、bw://)を書き、そこからシークレット値を取得する機能もあります。 本記事の .env.keys 運用を基本として、チームの保管方針や権限管理の要件に応じてこれらも検討できます。

この記事をシェア