<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nix on Tarragon</title><link>https://tarrragon.github.io/blog/tags/nix/</link><description>Recent content in Nix on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/nix/index.xml" rel="self" type="application/rss+xml"/><item><title>Devcontainer 與 Nix：容器化和宣告式的開發環境</title><link>https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/devcontainer-nix/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/devcontainer-nix/</guid><description>&lt;p>個人 dotfile 管理解決的是「一個人的環境可重現性」。當同樣的需求擴展到團隊——新人 onboarding 要多久能開始寫 code、團隊成員的開發環境差異造成「在我電腦上能跑」的問題、CI 環境跟本機環境不一致——就進入了「團隊開發環境標準化」的範疇。&lt;/p>
&lt;h2 id="個人-dotfile-跟團隊環境的邊界">個人 Dotfile 跟團隊環境的邊界&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>個人 Dotfile&lt;/th>
 &lt;th>團隊環境標準化&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>管理對象&lt;/td>
 &lt;td>個人偏好（alias、keybind、配色）&lt;/td>
 &lt;td>專案依賴（runtime 版本、系統套件、服務容器）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>目標&lt;/td>
 &lt;td>個人效率和舒適度&lt;/td>
 &lt;td>環境一致性和 onboarding 速度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>儲存位置&lt;/td>
 &lt;td>個人 dotfile repo&lt;/td>
 &lt;td>專案 repo 內（.devcontainer/、flake.nix）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>強制程度&lt;/td>
 &lt;td>完全個人自由&lt;/td>
 &lt;td>團隊約定或強制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>變動頻率&lt;/td>
 &lt;td>高（個人隨時調整）&lt;/td>
 &lt;td>低（跟專案版本走）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>兩者共用同一個核心思想（環境 as code、版控、可重現），但管理的對象和約定的範圍不同。個人 dotfile 是「我喜歡怎麼工作」，團隊環境是「這個專案需要什麼才能跑」。&lt;/p>
&lt;h2 id="devcontainer容器化的開發環境">Devcontainer：容器化的開發環境&lt;/h2>
&lt;p>Devcontainer 是微軟提出的開放規格（devcontainers.org），定義了怎麼用 Docker 容器作為開發環境。VS Code、GitHub Codespaces、JetBrains 都支援。&lt;/p>
&lt;h3 id="核心概念">核心概念&lt;/h3>
&lt;p>專案 repo 裡放一個 &lt;code>.devcontainer/devcontainer.json&lt;/code>，描述這個專案的開發環境需要什麼：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;My Project&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;image&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mcr.microsoft.com/devcontainers/base:ubuntu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;features&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;ghcr.io/devcontainers/features/go:1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.22&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;ghcr.io/devcontainers/features/node:1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;20&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;postCreateCommand&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;go mod download &amp;amp;&amp;amp; npm install&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;customizations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;vscode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;extensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;golang.go&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;esbenp.prettier-vscode&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;forwardPorts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">8080&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3000&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>打開專案時，IDE 自動啟動這個容器、在裡面安裝指定版本的 Go 和 Node、跑 dependency install、裝 VS Code extension。新人 clone repo → 打開 → 等容器建好 → 直接開始寫 code。&lt;/p>
&lt;h3 id="跟個人-dotfile-的互動">跟個人 Dotfile 的互動&lt;/h3>
&lt;p>Devcontainer 管的是「專案需要什麼」，但你在容器裡工作時還是會想要自己的 shell alias、Git 設定、editor keybind。兩者的整合方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>dotfiles repo 自動部署&lt;/strong>：devcontainer.json 支援 &lt;code>&amp;quot;dotfiles.repository&amp;quot;&lt;/code> 欄位，容器啟動時自動 clone 你的 dotfile repo 並執行 install script&lt;/li>
&lt;li>&lt;strong>個人 vs 團隊設定分離&lt;/strong>：&lt;code>.devcontainer/&lt;/code> 裡放團隊共用的環境定義，個人偏好透過 dotfiles 機制注入，不互相干擾&lt;/li>
&lt;/ul>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;dotfiles.repository&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/you/dotfiles&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;dotfiles.installCommand&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;scripts/install.sh&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;dotfiles.targetPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;~/dotfiles&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這是個人 dotfile 和團隊環境標準化最乾淨的接合點——團隊定義「環境長什麼樣」，個人 dotfile 定義「在這個環境裡我怎麼操作」。&lt;/p>
&lt;h3 id="devcontainer-的限制">Devcontainer 的限制&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Docker 是前提&lt;/strong>：團隊每個人的機器都要裝 Docker，macOS 上要跑 Docker Desktop 或 OrbStack&lt;/li>
&lt;li>&lt;strong>GUI 應用不適合&lt;/strong>：devcontainer 定位是 headless 開發環境，不處理圖形桌面&lt;/li>
&lt;li>&lt;strong>效能折扣&lt;/strong>：檔案系統操作在 macOS 上的 Docker volume 有效能折扣（Linux 上幾乎沒差）&lt;/li>
&lt;li>&lt;strong>離線環境&lt;/strong>：建容器需要拉 image 和 feature，斷網環境要另外處理（見 &lt;a href="https://tarrragon.github.io/blog/infra/air-gapped/" data-link-title="斷網環境的 infra：沒有網路時怎麼做" data-link-desc="實體隔離或無法連網的環境裡，IaC、套件管理、容器映像、監控、CI/CD 怎麼運作 — 原則不變、工具路徑全部要換">Infra 斷網模組&lt;/a>）&lt;/li>
&lt;/ul>
&lt;h2 id="nix宣告式的環境管理">Nix：宣告式的環境管理&lt;/h2>
&lt;p>Nix 是另一條技術路線，用宣告式的方式描述整個開發環境，不依賴 Docker。&lt;/p>
&lt;h3 id="核心概念-1">核心概念&lt;/h3>
&lt;p>Nix 的 &lt;code>flake.nix&lt;/code>（或 &lt;code>shell.nix&lt;/code>）宣告了開發環境需要哪些套件，&lt;code>nix develop&lt;/code> 進入這個環境：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nix" data-lang="nix">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># flake.nix&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="n">inputs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nixpkgs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;github:NixOS/nixpkgs/nixos-unstable&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="n">outputs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">nixpkgs&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="o">...&lt;/span> &lt;span class="p">}:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="k">let&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="n">pkgs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nixpkgs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">legacyPackages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">x86_64-linux&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="k">in&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="n">devShells&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pkgs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mkShell&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="n">packages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">with&lt;/span> &lt;span class="n">pkgs&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="n">go_1_22&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="n">nodejs_20&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="n">postgresql_16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="n">redis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="n">shellHook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="s1"> echo &amp;#34;Dev environment ready&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="s1"> &amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>跟 Devcontainer 的差異：Nix 不用容器，直接在宿主機上建立隔離的環境（透過 Nix store 的路徑隔離）。優點是沒有 Docker 的效能折扣和額外層級；缺點是 Nix 的學習曲線陡峭、語法不直覺。&lt;/p></description><content:encoded><![CDATA[<p>個人 dotfile 管理解決的是「一個人的環境可重現性」。當同樣的需求擴展到團隊——新人 onboarding 要多久能開始寫 code、團隊成員的開發環境差異造成「在我電腦上能跑」的問題、CI 環境跟本機環境不一致——就進入了「團隊開發環境標準化」的範疇。</p>
<h2 id="個人-dotfile-跟團隊環境的邊界">個人 Dotfile 跟團隊環境的邊界</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>個人 Dotfile</th>
          <th>團隊環境標準化</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>管理對象</td>
          <td>個人偏好（alias、keybind、配色）</td>
          <td>專案依賴（runtime 版本、系統套件、服務容器）</td>
      </tr>
      <tr>
          <td>目標</td>
          <td>個人效率和舒適度</td>
          <td>環境一致性和 onboarding 速度</td>
      </tr>
      <tr>
          <td>儲存位置</td>
          <td>個人 dotfile repo</td>
          <td>專案 repo 內（.devcontainer/、flake.nix）</td>
      </tr>
      <tr>
          <td>強制程度</td>
          <td>完全個人自由</td>
          <td>團隊約定或強制</td>
      </tr>
      <tr>
          <td>變動頻率</td>
          <td>高（個人隨時調整）</td>
          <td>低（跟專案版本走）</td>
      </tr>
  </tbody>
</table>
<p>兩者共用同一個核心思想（環境 as code、版控、可重現），但管理的對象和約定的範圍不同。個人 dotfile 是「我喜歡怎麼工作」，團隊環境是「這個專案需要什麼才能跑」。</p>
<h2 id="devcontainer容器化的開發環境">Devcontainer：容器化的開發環境</h2>
<p>Devcontainer 是微軟提出的開放規格（devcontainers.org），定義了怎麼用 Docker 容器作為開發環境。VS Code、GitHub Codespaces、JetBrains 都支援。</p>
<h3 id="核心概念">核心概念</h3>
<p>專案 repo 裡放一個 <code>.devcontainer/devcontainer.json</code>，描述這個專案的開發環境需要什麼：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;My Project&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nt">&#34;image&#34;</span><span class="p">:</span> <span class="s2">&#34;mcr.microsoft.com/devcontainers/base:ubuntu&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;features&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="nt">&#34;ghcr.io/devcontainers/features/go:1&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.22&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="nt">&#34;ghcr.io/devcontainers/features/node:1&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;20&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nt">&#34;postCreateCommand&#34;</span><span class="p">:</span> <span class="s2">&#34;go mod download &amp;&amp; npm install&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nt">&#34;customizations&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="nt">&#34;vscode&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">            <span class="nt">&#34;extensions&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">                <span class="s2">&#34;golang.go&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">                <span class="s2">&#34;esbenp.prettier-vscode&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="nt">&#34;forwardPorts&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">8080</span><span class="p">,</span> <span class="mi">3000</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>打開專案時，IDE 自動啟動這個容器、在裡面安裝指定版本的 Go 和 Node、跑 dependency install、裝 VS Code extension。新人 clone repo → 打開 → 等容器建好 → 直接開始寫 code。</p>
<h3 id="跟個人-dotfile-的互動">跟個人 Dotfile 的互動</h3>
<p>Devcontainer 管的是「專案需要什麼」，但你在容器裡工作時還是會想要自己的 shell alias、Git 設定、editor keybind。兩者的整合方式：</p>
<ul>
<li><strong>dotfiles repo 自動部署</strong>：devcontainer.json 支援 <code>&quot;dotfiles.repository&quot;</code> 欄位，容器啟動時自動 clone 你的 dotfile repo 並執行 install script</li>
<li><strong>個人 vs 團隊設定分離</strong>：<code>.devcontainer/</code> 裡放團隊共用的環境定義，個人偏好透過 dotfiles 機制注入，不互相干擾</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nt">&#34;dotfiles.repository&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/you/dotfiles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nt">&#34;dotfiles.installCommand&#34;</span><span class="p">:</span> <span class="s2">&#34;scripts/install.sh&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nt">&#34;dotfiles.targetPath&#34;</span><span class="p">:</span> <span class="s2">&#34;~/dotfiles&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>這是個人 dotfile 和團隊環境標準化最乾淨的接合點——團隊定義「環境長什麼樣」，個人 dotfile 定義「在這個環境裡我怎麼操作」。</p>
<h3 id="devcontainer-的限制">Devcontainer 的限制</h3>
<ul>
<li><strong>Docker 是前提</strong>：團隊每個人的機器都要裝 Docker，macOS 上要跑 Docker Desktop 或 OrbStack</li>
<li><strong>GUI 應用不適合</strong>：devcontainer 定位是 headless 開發環境，不處理圖形桌面</li>
<li><strong>效能折扣</strong>：檔案系統操作在 macOS 上的 Docker volume 有效能折扣（Linux 上幾乎沒差）</li>
<li><strong>離線環境</strong>：建容器需要拉 image 和 feature，斷網環境要另外處理（見 <a href="/blog/infra/air-gapped/" data-link-title="斷網環境的 infra：沒有網路時怎麼做" data-link-desc="實體隔離或無法連網的環境裡，IaC、套件管理、容器映像、監控、CI/CD 怎麼運作 — 原則不變、工具路徑全部要換">Infra 斷網模組</a>）</li>
</ul>
<h2 id="nix宣告式的環境管理">Nix：宣告式的環境管理</h2>
<p>Nix 是另一條技術路線，用宣告式的方式描述整個開發環境，不依賴 Docker。</p>
<h3 id="核心概念-1">核心概念</h3>
<p>Nix 的 <code>flake.nix</code>（或 <code>shell.nix</code>）宣告了開發環境需要哪些套件，<code>nix develop</code> 進入這個環境：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># flake.nix</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">inputs</span><span class="o">.</span><span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:NixOS/nixpkgs/nixos-unstable&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="k">let</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="n">pkgs</span> <span class="o">=</span> <span class="n">nixpkgs</span><span class="o">.</span><span class="n">legacyPackages</span><span class="o">.</span><span class="n">x86_64-linux</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">in</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="n">devShells</span><span class="o">.</span><span class="n">default</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">mkShell</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">packages</span> <span class="o">=</span> <span class="k">with</span> <span class="n">pkgs</span><span class="p">;</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">          <span class="n">go_1_22</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">          <span class="n">nodejs_20</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">          <span class="n">postgresql_16</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">          <span class="n">redis</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="p">];</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="n">shellHook</span> <span class="o">=</span> <span class="s1">&#39;&#39;
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s1">          echo &#34;Dev environment ready&#34;
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="s1">        &#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>跟 Devcontainer 的差異：Nix 不用容器，直接在宿主機上建立隔離的環境（透過 Nix store 的路徑隔離）。優點是沒有 Docker 的效能折扣和額外層級；缺點是 Nix 的學習曲線陡峭、語法不直覺。</p>
<h3 id="home-managernix-管理-dotfile">Home Manager：Nix 管理 Dotfile</h3>
<p>Nix 生態裡的 Home Manager 可以用 Nix 語言宣告式地管理整個家目錄的配置——等於用 Nix 取代 stow/chezmoi 做 dotfile 管理：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># home.nix</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">programs</span><span class="o">.</span><span class="n">git</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">userName</span> <span class="o">=</span> <span class="s2">&#34;Your Name&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">userEmail</span> <span class="o">=</span> <span class="s2">&#34;you@example.com&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">extraConfig</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="n">init</span><span class="o">.</span><span class="n">defaultBranch</span> <span class="o">=</span> <span class="s2">&#34;main&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="n">pull</span><span class="o">.</span><span class="n">rebase</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="n">programs</span><span class="o">.</span><span class="n">zsh</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">shellAliases</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">      <span class="n">ll</span> <span class="o">=</span> <span class="s2">&#34;ls -alF&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">      <span class="n">gs</span> <span class="o">=</span> <span class="s2">&#34;git status&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="n">programs</span><span class="o">.</span><span class="n">neovim</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">defaultEditor</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Home Manager 把「安裝軟體」和「寫配置」統一成一份宣告——改完 <code>home-manager switch</code> 就同時更新套件和配置。這是 dotfile 管理的極致形式，但代價是整個技術棧鎖定在 Nix 生態裡。</p>
]]></content:encoded></item><item><title>模組九：從個人到團隊</title><link>https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/</guid><description>&lt;p>個人 dotfile 管理解決的是「一個人的環境可重現性」。當同樣的需求擴展到團隊——新人 onboarding 要多久能開始寫 code、團隊成員的開發環境差異造成「在我電腦上能跑」的問題、CI 環境跟本機環境不一致——就進入了「團隊開發環境標準化」的範疇。這個模組教的是個人 dotfile 的思想怎麼往上延伸，以及在商業環境中有哪些成熟的做法。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/devcontainer-nix/" data-link-title="Devcontainer 與 Nix：容器化和宣告式的開發環境" data-link-desc="團隊開發環境要標準化、或評估 devcontainer 和 nix 跟個人 dotfile 怎麼共存時回來讀">Devcontainer 與 Nix&lt;/a>&lt;/td>
 &lt;td>容器化和宣告式的開發環境、devcontainer 跟個人 dotfile 的互動、Home Manager&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/09-team-environment/commercial-environment/" data-link-title="商業環境的開發環境配置管理" data-link-desc="企業的開發環境標準化要走到什麼程度、什麼訊號該從個人 dotfile 往團隊層級推進">商業環境的開發環境配置管理&lt;/a>&lt;/td>
 &lt;td>四個層級的做法（README → 腳本化 → Devcontainer → MDM）、跟 Infra 的銜接、推進判讀&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/" data-link-title="模組零：Dotfile 心智模型" data-link-desc="換機器、開 VM、重灌系統時需要快速還原開發環境，或想釐清哪些配置該版控、哪些該排除時回來讀">模組零：Dotfile 心智模型&lt;/a>：個人環境 as code 跟組織 IaC 的平行&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：bootstrap script 是團隊腳本化層級的基礎&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南&lt;/a>：Infra IaC 是組織層的環境 as code&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/infra/air-gapped/" data-link-title="斷網環境的 infra：沒有網路時怎麼做" data-link-desc="實體隔離或無法連網的環境裡，IaC、套件管理、容器映像、監控、CI/CD 怎麼運作 — 原則不變、工具路徑全部要換">Infra 斷網模組&lt;/a>：離線環境的 devcontainer 限制&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>個人 dotfile 管理解決的是「一個人的環境可重現性」。當同樣的需求擴展到團隊——新人 onboarding 要多久能開始寫 code、團隊成員的開發環境差異造成「在我電腦上能跑」的問題、CI 環境跟本機環境不一致——就進入了「團隊開發環境標準化」的範疇。這個模組教的是個人 dotfile 的思想怎麼往上延伸，以及在商業環境中有哪些成熟的做法。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/09-team-environment/devcontainer-nix/" data-link-title="Devcontainer 與 Nix：容器化和宣告式的開發環境" data-link-desc="團隊開發環境要標準化、或評估 devcontainer 和 nix 跟個人 dotfile 怎麼共存時回來讀">Devcontainer 與 Nix</a></td>
          <td>容器化和宣告式的開發環境、devcontainer 跟個人 dotfile 的互動、Home Manager</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/09-team-environment/commercial-environment/" data-link-title="商業環境的開發環境配置管理" data-link-desc="企業的開發環境標準化要走到什麼程度、什麼訊號該從個人 dotfile 往團隊層級推進">商業環境的開發環境配置管理</a></td>
          <td>四個層級的做法（README → 腳本化 → Devcontainer → MDM）、跟 Infra 的銜接、推進判讀</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/00-dotfile-mindset/" data-link-title="模組零：Dotfile 心智模型" data-link-desc="換機器、開 VM、重灌系統時需要快速還原開發環境，或想釐清哪些配置該版控、哪些該排除時回來讀">模組零：Dotfile 心智模型</a>：個人環境 as code 跟組織 IaC 的平行</li>
<li>→ <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：bootstrap script 是團隊腳本化層級的基礎</li>
<li>→ <a href="/blog/infra/" data-link-title="Infra 基礎設施建置指南" data-link-desc="從零循序漸進把雲端基礎設施做起來 — IaC、身分憑證、網路地基、環境分離、核心服務、可觀測性、自動化 review 與治理習慣，含怎麼在組織內推動">Infra 基礎設施建置指南</a>：Infra IaC 是組織層的環境 as code</li>
<li>→ <a href="/blog/infra/air-gapped/" data-link-title="斷網環境的 infra：沒有網路時怎麼做" data-link-desc="實體隔離或無法連網的環境裡，IaC、套件管理、容器映像、監控、CI/CD 怎麼運作 — 原則不變、工具路徑全部要換">Infra 斷網模組</a>：離線環境的 devcontainer 限制</li>
</ul>
]]></content:encoded></item></channel></rss>