インフラ

direnvとは?.envrcでディレクトリ単位に環境変数を切り替える手順と運用設計

direnvとは?.envrcでディレクトリ単位に環境変数を切り替える手順と運用設計

プロジェクトごとに接続先データベースやAPIキーが違うのに、シェルの設定ファイルへ全部まとめて書いている。direnvはその状態を解くツールです。ディレクトリに入ると.envrcを読んで環境変数を入れ、出ると元に戻します。この記事では、2026年9月時点で最新のv2.37.1を前提に、bash・zsh・fishへのhook設定、direnv allowを求める許可モデル、PATH_addlayoutといったstdlib関数の書き分け、.envとの併用とGitへ秘密情報を漏らさない線引き、direnv.tomlのglobal設定とwhitelistの副作用、そしてCIやコンテナで外す判断基準までを実行できるコード付きで扱います。

まとめ:direnvの導入判断とチーム運用で外せない設定の要点

direnvを入れる価値が最も大きいのは、1台の開発機で3つ以上のプロジェクトを行き来する人です。シェル設定ファイルへの直書きは、変数名の衝突と「どの値がどの案件のものか分からない」状態を必ず生みます。ディレクトリを境界にすれば、この2つが同時に消えます。

導入はbrew install direnvなどのパッケージ導入と、シェル設定へのeval "$(direnv hook bash)"の1行追記で終わります。.envrcを置いただけでは読み込まれず、direnv allowで明示的に許可するまで警告が出続ける設計です。この許可モデルは面倒に見えますが、他人から受け取ったリポジトリに紛れた.envrcが勝手に実行されるのを止めています。

チームへ配るときに外せない判断は3つあります。.envrcはコミットして共有し、値そのものは.envへ分離して.gitignoreで除外すること。本番の認証情報はdirenvに置かず、シークレット管理サービス側へ寄せること。CIとコンテナでは原則としてdirenvを外し、それぞれの標準機能へ置き換えることです。設定を全員へ配る段階ではdirenv.tomlstrict_envwarn_timeoutを先に決めておくと、後から個人差が出ません。

direnvの動作原理とbashサブシェルで環境差分だけを戻す仕組み

挙動を誤解したまま使うと、動かないときに切り分けができません。先に何が起きているかを押さえます。

ディレクトリ移動で環境変数を出し入れする挙動と対応シェル8種

direnvはシェルがプロンプトを表示する直前に割り込み、カレントディレクトリと親ディレクトリをさかのぼって.envrcを探します。見つかって許可済みなら、その内容を評価して環境変数を追加する仕組みです。ディレクトリから出ると、追加した変数を取り除いて元の状態へ戻します。公式サイトが掲げる対応シェルは bash, zsh, tcsh, fish, elvish, powershell, murex, nushell の8種です。

変数の追加と撤去が自動なので、案件をまたいだ作業でもexportのし忘れと消し忘れが起きません。環境変数そのものの概念とOS別の設定方法は別記事で扱っています。direnvはその概念を「ディレクトリ境界で自動化する層」として上に乗るものだと考えてください。

.envrcを常にbashで評価する設計が他シェルでも動く理由

fishやzshを使っていても、.envrcの中身はbashの文法で書きます。direnvが.envrcを読むためにbashの子プロセスを新しく起動し、そこで評価した結果の環境の差分だけを元のシェルへ戻す作りだからです。公式サイトはこの設計を「stdlibを読み込むために新しいbashサブプロセスを作っている」と説明しています。

この一点を知っていれば、fish用の構文を.envrcへ書いて動かないという定番の詰まりを避けられます。同時に、シェル固有のエイリアスや関数は.envrcで定義しても元のシェルへは戻らないことも導かれます。子プロセスで定義されて消えるためです。エイリアスの定義先はシェル設定ファイル側に残す、と切り分けてください。

bashとzshへのインストールとhook設定を通す最短の導入手順

導入は2段階です。パッケージを入れ、シェルへhookする。どちらか片方だけでは動きません。

パッケージ導入からhook追記まで4手順で動かす最小構成の作り方

主要なディストリビューションとHomebrewにパッケージが用意されています。hookはシェルごとに書式が違い、設定ファイルの末尾に置く必要があります。プロンプト関連の設定より前に書くと、他のツールに上書きされて効かない場合があるためです。

# 1. パッケージを入れる(macOS / Homebrew)
brew install direnv

# Debian / Ubuntu の場合
sudo apt-get install -y direnv

# 2. hook をシェル設定ファイルの末尾へ追記する
# bash なら ~/.bashrc
eval "$(direnv hook bash)"

# zsh なら ~/.zshrc
eval "$(direnv hook zsh)"

# fish なら ~/.config/fish/config.fish
direnv hook fish | source

# 3. シェルを開き直してから動作確認
direnv version

バージョンが表示されれば導入は完了です。GitHub Releasesで確認できる最新版は v2.37.1(2025年7月20日公開)でした。ディストリビューションのパッケージはこれより古いことがあるため、direnv versionの出力は控えておいてください。direnv.tomlの一部のキーには必要バージョンがあります。

direnv allowを求める許可モデルと未許可時に出る警告の読み方

次にプロジェクト側で.envrcを作ります。作成しただけでは読み込まれず、direnv allowを実行するまで警告が出続けます。

cd ~/work/sample-api

cat > .envrc <<'EOF'
export APP_ENV="development"
export DATABASE_URL="postgres://localhost:5432/sample_dev"
EOF

# 許可するまで読み込まれない
direnv allow .

ファイルを1文字でも書き換えると許可は取り消され、再度direnv allowが要ります。許可情報はXDG_DATA_HOME配下に保存され、消えたプロジェクトの分はdirenv pruneで掃除できます。許可を取り消す側のコマンドはdirenv denyです。この仕組みがあるおかげで、クローンしてきたリポジトリに悪意ある.envrcが入っていても、内容を読む前に実行されることはありません。警告が出たら、まず中身を読んでから許可してください。

.envrcの基本文法とstdlib関数で書く実務パターンの型

exportを並べるだけでも動きますが、stdlibの関数を使うと記述が短くなり、想定外の挙動も減ります。実務で頻度が高い2組を示します。

PATH_addとlayoutで開発ツールの参照先を切り替える書き方

PATH_add <path>は指定したパスを展開してPATHの先頭へ足します。相対パスを書いても.envrcのある場所を基準に絶対パスへ変換されるため、プロジェクト内のバイナリを優先させる用途に適した書き方です。layout <type>は言語ごとの定型処理をまとめたディスパッチャで、Pythonなら仮想環境の作成と有効化までを1行で担います。

# プロジェクト内のツールを PATH の先頭へ
PATH_add bin
PATH_add node_modules/.bin

# PATH 以外の変数にも同じことができる
path_add PYTHONPATH src

# Python の仮想環境を作って有効化する
layout python python3.12

# 言語ランタイムのバージョン切り替えは use 側
use node 22

ディレクトリを出ればPATHは元に戻ります。グローバルにツールを入れずにプロジェクト単位で完結させたいときの中心になる書き方です。

source_envとwatch_fileで親設定の継承と再読込を制御する構成

モノレポのように.envrcが階層で並ぶ構成では、親の設定を読んでから差分だけを子で上書きします。source_env_if_existsなら親が無い場合もエラーになりません。watch_fileは、.envrc以外のファイルが変わったときにも再読込させるための宣言です。

# 親ディレクトリの共通設定を継承する
source_env_if_exists ../.envrc

# .env があれば読む(無くても止めない)
dotenv_if_exists

# これらが更新されたら再読込の対象にする
watch_file .tool-versions
watch_file .env

# Git のブランチで分岐させることもできる
on_git_branch main && export APP_ENV="staging"

watch_fileを書き忘れると、.envの値を変えてもシェルには反映されません。「変更したのに古い値のまま」という詰まりの大半はこれです。direnv reloadで手動の読み直しもできますが、監視対象に入れておく方が運用は安定します。

.envとの併用とAPIキーをGitへ漏らさない分離設計の基準

.envrc.envを混同すると、秘密情報をそのままGitへ載せる事故につながります。役割を先に決めてください。

dotenv_if_existsでの読み分けとgitignoreの線引き基準

線引きの基準は単純です。.envrc手順を書く場所でコミットして共有する。.env値を書く場所でコミットしない。この分担にすると、新しく参加した人が.envrcを読むだけで「何を用意すべきか」が分かり、値だけを個別に受け取れば動きます。

# .gitignore
.env
.env.local
.direnv/

# .envrc は共有する(値そのものは書かない)
!.envrc

.envの読み込みはdotenv_if_existsで行います。direnv.tomlload_dotenvを真にすれば自動で読ませることもできますが、既定値は偽です。プロジェクトごとに挙動が変わると事故を招くため、.envrcへ明示的に書く方を推奨します。docker-composeで複数コンテナを定義している場合は、同じ.envをComposeとdirenvの両方から参照させると二重管理を避けられます。

env_vars_requiredとstrict_envで設定漏れを起動時に止める条件

値の不足は、アプリが起動してから謎のエラーとして表面化します。env_vars_requiredを使えば、ディレクトリへ入った時点で未設定を検出することが可能です。strict_envset -euo pipefail相当を有効にし、未定義変数の参照やコマンドの失敗を黙って通さなくします。

strict_env

dotenv_if_exists .env.local

# 揃っていなければここで止まる
env_vars_required DATABASE_URL STRIPE_SECRET_KEY REDIS_URL

PATH_add bin

新規参加者のセットアップで効きます。「READMEに書いてあるのに読まれない」問題を、シェルの側から機械的に止める配置です。strict_envを入れると既存の.envrcが落ちる場合がありますが、その大半は未定義変数を参照している箇所で、直す価値のある不具合です。

direnv.tomlのglobal設定とwhitelistで許可操作を省く条件

個人設定は$XDG_CONFIG_HOME/direnv/direnv.tomlに置きます。v2.21.0以下はconfig.tomlという名前でした。チームへ配る前に決めておく値を整理します。

global配下で効く主要キーと既定値・必要バージョンの対応

direnv.tomlのmanページで確認できる[global]のキーと既定値は次の通りです。バージョン条件が付くキーがあるため、direnv versionの出力と突き合わせてから配布してください。

キー 既定値 効果 必要バージョン
strict_env false set -euo pipefail 相当を有効化
load_dotenv false .env を自動で読み込む v2.31.0以上
warn_timeout 5s 読み込みが遅いときに警告を出すまでの時間
hide_env_diff false 読み込み時の環境差分の表示を抑える
disable_stdin false .envrc 評価中の標準入力を塞ぐ
bash_path 未指定 評価に使う bash のパスを固定する
log_format / log_filter 未指定 ログの書式指定と正規表現での絞り込み v2.36.0以上
# ~/.config/direnv/direnv.toml
[global]
strict_env = true
warn_timeout = "10s"
hide_env_diff = true
load_dotenv = false

hide_env_diffは、変数を大量に入れるプロジェクトでプロンプトが差分で埋まる状況に効きます。warn_timeoutlayout pythonのように初回が重い処理を含むとき、既定の5秒では警告が頻発するため延ばします。

whitelistのprefix指定が許可確認を無効化する副作用の範囲

[whitelist]にはprefixexactの2つの配列を書けます。prefixに登録したディレクトリ以下では、direnv allowを求められなくなります。

[whitelist]
prefix = [ "/home/alice/work/internal" ]
exact = [ "/home/alice/tools/.envrc" ]

ここで判断を明確にしておきます。外部から受け取るコードが入りうる階層をprefixへ登録してはいけません。許可確認はdirenvの唯一の安全装置で、これを外した階層ではgit cloneしたリポジトリの.envrcが、ディレクトリへ入った瞬間に任意コードとして実行されます。登録してよいのは、自分だけが作るディレクトリに限られます。社内の共通リポジトリ置き場であっても、コントリビュータが複数いるならexactでファイル単位に絞る方が安全です。

direnvを採用しない方がよい場面とCI・コンテナでの代替手段

direnvは開発機のシェルのための道具です。それ以外の実行環境へ持ち込むと、得るものより失うものが多くなります。

CIとコンテナでdirenvを外す判断基準と置き換え先の選び方

判断基準は「対話的なシェルがあるかどうか」です。CIのジョブとコンテナのENTRYPOINTは非対話で動くため、プロンプト直前に割り込むdirenvのhookはそもそも発火しません。direnv exec . <command>で無理やり通すことはできますが、CIには変数を渡す標準機能がすでにあります。二重化は設定の所在を分散させるだけです。

置き換え先は明確です。GitHub Actionsならenv・vars・secretsの使い分けで渡します。コンテナならenvironmentenv_fileで注入します。開発機のシェルだけをdirenvが担当し、CIとコンテナには一切持ち込まない。この線引きを守ってください。なお v2.37.0 ではGitHub Actions向けのエクスポート形式が強化されていますが、これはCIでdirenvを常用する根拠にはなりません。

秘密情報の保管先としてdirenvを使ってはいけない理由と代替

本番の認証情報を.envへ置いて配る運用は、条件を問わず見送るべきです。理由は3つあり、いずれも技術的に回避できません。平文でディスクに残ること。ローテーションの手段が無く、更新のたびに全員へ再配布が要ること。誰がいつ参照したかの記録が残らないこと。開発用のダミー値ならこの限りではありませんが、線を引かずに運用すると本番値が紛れ込みます。

代替は保管先を分けることです。クラウド側ならAWS Secrets Managerのローテーション設定、CI側ならGitHub ActionsのSecretsとOIDC移行が受け皿になります。direnvの役割は「取得したものをシェルへ流し込む最後の1メートル」に限定してください。開発環境の標準化や、こうした手順をチーム全体へ配って定着させる工程は、保守運用・内製化支援でも引き受けています。

読み込まれない不具合の切り分け手順とstatusで見る原因の特定

「動かない」と言われたときに見る場所は決まっています。上から順に潰してください。

direnv statusの出力から許可状態とロード経路を読む手順

direnv.1のmanページに載るサブコマンドは allow, deny, edit, exec, export, fetchurl, help, hook, prune, reload, status, stdlib, version です。切り分けの起点はdirenv statusで、読み込んだ設定ファイルの場所、見つかった.envrcのパス、その許可状態がまとめて出ます。

# 設定・検出したファイル・許可状態をまとめて確認
direnv status

# 何が export されるかを直接見る
direnv export bash

# 手動で読み直す
direnv reload

direnv status.envrcのパス自体が出ていなければ、探索の問題です。ファイル名の誤り(.envrcであって.envrc.shではない)か、親をさかのぼる範囲の外にファイルがあります。パスは出ているのに変数が入らないなら、許可の問題か評価時のエラーです。

hook未設定・サブシェル・エディタ起動で起きる不発の典型例

頻度が高い順に3つあります。第1はhookの未設定で、パッケージを入れただけの状態です。direnv versionは通るのに何も起きないなら、まずシェル設定ファイルの末尾を確認してください。第2はシェルを開き直していないケース。追記した設定は、既存のセッションには反映されません。

第3がエディタやIDEの統合ターミナルです。ログインシェルとして起動されず~/.bashrcが読まれない設定になっていると、hookが入らないまま端末が開きます。ターミナルの起動オプションを確認するか、そのエディタ用のdirenv拡張を入れる対応になります。makeやスクリプト経由の非対話シェルでも同じ理由で発火しないため、その場合はdirenv execを明示的に挟んでください。

よくある質問

導入の相談でよく挙がる5点をまとめます。

direnvと.envファイルはどちらを使うべきですか?

対立するものではないので、併用が前提です。.envrcに手順(どのファイルを読むか、どのパスを通すか、何が必須か)を書いてGitで共有し、.envに値だけを書いて除外します。.env単体の運用だと、読み込むのはアプリのライブラリ側の仕事になり、シェルのコマンドからは変数が見えません。psqlawsコマンドを素で叩きたいなら、シェル側へ流し込むdirenvが要ります。

.envrcはGitにコミットしてよいですか?

秘密情報を直書きしていないなら、コミットして共有してください。むしろ共有した方が、新しく入った人が環境構築の手順をファイルから読み取れます。値はdotenv_if_exists.envから読み、そちらを.gitignoreへ入れる運用です。env_vars_requiredを併記しておくと、必要な変数の一覧もそのまま仕様書として機能します。

Windowsでも使えますか?

公式サイトの対応シェル一覧にpowershellが含まれており、v2.37.0ではWindows ARM64向けのビルドターゲットが追加されています。ただし.envrcの評価にbashが必要な設計は変わらないため、WSL上で使うのが素直です。PowerShellで動かす場合、v2.37.0で特殊文字の扱いに修正が入っている点は把握しておいてください。

allowを毎回求められるのを止められますか?

頻度が高いのは、生成物やツールが.envrcを書き換えているケースです。まず何が更新しているかを特定してください。そのうえで自分専用のディレクトリに限るなら、direnv.toml[whitelist]prefixexactで登録すれば確認を省けます。外部のコードが入りうる場所への登録は避けてください。任意コードが無確認で実行される状態になります。

チーム全員へ同じ設定を配るにはどうすればよいですか?

配布する設定は、管理の単位に応じて分けた2層です。プロジェクト単位の.envrcはリポジトリに入れて共有し、個人単位のdirenv.tomlはセットアップ手順として渡します。strict_envwarn_timeoutだけは全員で揃えておくと、「自分の環境だけ落ちる」「警告が出る出ない」という差がなくなります。log_filterを使う場合はv2.36.0以上が要るため、配布前にバージョン下限を決めてください。

関連記事

資料請求

RELATED POSTS 関連記事