1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nix FlakeとDev Containersを組み合わせてセキュアで再現性の高い開発環境を配布したい!

Posted at

これは何?

Nix FlakeはNix 2.4で追加された機能であり、flake.lockを使うことでNix 式の入力(nixpkgs 等)をcommit/hashで固定し、同一の依存定義(flake.nix)から再現可能なビルドを行えるようにする仕組みである。

Dev Containersなどで行われる開発環境のコンテナ化と比べてNix Flakeは以下の点が優れている。

  • Nixはcommit/hashベースで固定される
    • 環境の再現性が高い (Dockerfileでバージョンを指定しても参照先が変更される可能性がある)
    • どのライブラリを使っているかわかるのでセキュリティ的にもうれしい(SBOMとかもすぐ作れそう)
  • Nixのインターネット接続は取得フェーズの1回のみなので環境の立ち上げが速い。
    • 普通にコンテナ立ち上げるのがめんどくさい...

個人の場合はローカルでNix Flakeを使って環境構築するで良いと思うのだが、業務の場合以下のようなユースケースでDev Containresの需要があると思う。

  • セキュリティ的な観点で開発環境を分離したい
    • AIエージェントに好き勝手やってもらう(Claude CodeのbypassPermissions等)
    • 悪意のある第三者により埋め込まれた脆弱性を持つファイル等をダウンロードしてもホストPCまで汚染されづらい
      • カーネルモードで動作しているEDR製品はコンテナ内を保護できるらしい
  • チームでExtensions等含めて開発環境構築手順を自動化したい

そこで、本記事ではNix FlakeとDev Containersのいいとこ取りができないか検証した結果を共有する。

(この記事の続きです)


環境

$ uname -a
Linux lifebook 6.8.0-90-generic #91~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 15:20:45 UTC 2 x86_64 GNU/Linux
$ code -v
1.107.1
994fd12f8d3a5aa16f17d42c041e5809167e845a
x64
$ nix --version
nix (Nix) 2.33.0

自分のリポジトリへのリンク


コンテナ内でNix Flakeを使うか、Nix Flakeの機能でコンテナを作るか

方式としては以下の2つが考えられそう。

  • 方式1: Dockerfileでコンテナを作って中でnixを使う
  • 方式2: flake.nixの中でdockerTools libraryを使ってコンテナを作り、Dev Containresでこれに接続する。

方式1: コンテナ内でNix Flakeを使う

以下の構成にした。
この記事を書いた時点での自分のリポジトリへのリンク

  • direnvnix-direnvを使ってリポジトリトップにcdした時にnix developされてPATHが通る
  • zshをデフォルトにする
    • .zshrcはVS Codeの機能で自分のdotfilesリポジトリから持ってくる(これにeval "$(direnv hook zh)"が記載されている想定
      settings.json
      // Dev Containersでdotfilesを使う。
      "dotfiles.repository": "https://github.com/RyosukeDTomita/dotfiles.git",  // fixme
      "dotfiles.targetPath": "~/dotfiles", // fixme
      "dotfiles.installCommand": "install.sh", // fixme ~/dotfilesのrepository topからみたスクリプトのパスを指定する。
      
  • Nix Flakeやnix-direnvのインストールをPostCreateCommandで実行する
    • devcontainre.json/nixdirenvのキャッシュをvolumeマウントするようにして軽くする
      # マウント先の確認方法(ローカルで実行)
      docker volume inspect nix-store
      [
          {
              "CreatedAt": "2026-01-23T01:24:41+09:00",
              "Driver": "local",
              "Labels": null,
              "Mountpoint": "/var/lib/docker/volumes/nix-store/_data",
              "Name": "nix-store",
              "Options": null,
              "Scope": "local"
          }
      ]
      
  • Haskellのライブラリはとりあえず、nix管理にする(cargoとかstackを使ったらそのうち追記するかも)
  • VS CodeのExtensionsはdevcontainer.jsonに記述して設定はリポジトリの.vscode/settings.jsonに記載
  • baseイメージはDev Containres用のimageを使った
devcontainre.json
{
  "name": "Haskell Development",
  "dockerFile": "../Dockerfile",
  "mounts": [
    "source=nix-store,target=/nix,type=volume",
    "source=direnv-cache,target=/home/vscode/.local/share/direnv,type=volume"
  ],
  "postCreateCommand": "bash .devcontainer/postCreateCommand.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "haskell.haskell",
        "DavidAnson.vscode-markdownlint"
      ],
      "settings": {
        "terminal.integrated.defaultProfile.linux": "zsh"
      }
    }
  }
}
postCreateCommand.sh
#!/bin/zsh
set -e

# Nixストアの権限を修正(ボリュームが初めて作成された場合root所有になるため)
if [ -d /nix ] && [ ! -w /nix ]; then
  echo "Fixing /nix permissions..."
  sudo chown -R vscode: /nix
fi

# direnvキャッシュの権限を修正
if [ -d /home/vscode/.local/share/direnv ] && [ ! -w /home/vscode/.local/share/direnv ]; then
  echo "Fixing direnv cache permissions..."
  sudo chown -R vscode: /home/vscode/.local/share/direnv
fi

# Install Nix if not already installed
if [ ! -d /nix/store ]; then
  echo "Installing Nix..."
  curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install | sh -s -- --no-daemon
fi

# Set up user config for flakes
mkdir -p /home/vscode/.config/nix
echo "experimental-features = nix-command flakes" | tee /home/vscode/.config/nix/nix.conf

# Setup direnv hooks dotfiles経由の.zshrdに記載があるのでコメントアウト
# echo 'eval "$(direnv hook bash)"' >> /home/vscode/.bashrc
# echo 'eval "$(direnv hook zsh)"' >> /home/vscode/.zshrc

# Nixプロファイルを読み込む
if [ -e ~/.nix-profile/etc/profile.d/nix.sh ]; then
  source ~/.nix-profile/etc/profile.d/nix.sh
fi

# Install nix-direnv
echo "Installing nix-direnv..."
nix profile install nixpkgs#nix-direnv

# Setup direnvrc for nix-direnv
mkdir -p /home/vscode/.config/direnv
cat > /home/vscode/.config/direnv/direnvrc << 'EOF'
if [ -f "$HOME/.nix-profile/share/nix-direnv/direnvrc" ]; then
  source "$HOME/.nix-profile/share/nix-direnv/direnvrc"
elif [ -f "/nix/var/nix/profiles/default/share/nix-direnv/direnvrc" ]; then
  source "/nix/var/nix/profiles/default/share/nix-direnv/direnvrc"
elif [ -f "/run/current-system/sw/share/nix-direnv/direnvrc" ]; then
  source "/run/current-system/sw/share/nix-direnv/direnvrc"
fi
EOF

# direnvを許可して新しいシェルで自動的に環境がロードされるように
echo "Allowing direnv for workspace..."
cd /workspaces/atcoder
direnv allow

echo "Setup complete!"
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/devcontainers/base:trixie
WORKDIR /workspace

# install dependencies for nix (will be installed in postCreateCommand)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
  --mount=type=cache,target=/var/lib/apt,sharing=locked \
  apt-get update && \
  apt-get install -y curl xz-utils ca-certificates direnv

# enable nix in PATH
ENV PATH="/nix/var/nix/profiles/default/bin:${PATH}"

方式1をやってみての感想

  • 満足いく環境を作るには設定がそれなりに複雑
  • コンテナビルドの初回が若干重い
  • 使ってみてそんなに不便はないのでチームに展開する目的ならありかもしれない

以下に失敗したポイントを書いておくので参考までに

失敗1: baseイメージをnixos/nixにした

image.png

Nix Flakeを使うならNix用のイメージを使うべきかと思ったが、VS Code Serverを動かすのに必要なパッケージが足りないと言われたので普通にDev Containers用のimageを使うことにした。

失敗2: DockerfileにWORKDIRを指定しなかった

(失敗3を受けてDockerfile内でnix developしないように変更した)

/nix developを実行するとうまくいかないのでDockerfileWORKDIR /workspaceを追加した。

失敗3: Dockerfile内で初回のnix developをする

キャッシュが効かない時に初回インストールにめちゃくちゃ時間がかかるのでおすすめしない。

postCreateCommandNixをインストールし、/nixをボリュームマウントで永続化する方法を真似させていただいた(大感謝)。

失敗4: nixをmulti userモードでインストールする

postCreateCommand実行時にmulti userモードの場合にはNix Deamonの起動を待つ必要がある。そのためsingle user modeでインストールする方針に変更。


方式2: Nix Flakeの機能でコンテナを作ってDev Containresで接続する(未検証)

多分今までalpineとかdistrolessとかにバイナリ配置していた感覚でデプロイに使う?ので開発環境のコンテナカスタマイズをがちゃがちゃやるには向かなそう(エアプ)

試したら書く


関連する自分の記事

↓Nix Flakeについて知りたい方向け

↓Dev Containresについて知りたい方向け

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?