Installing NixOS on Raspberry Pi 4

Michael Lynch

在 Raspberry Pi 4 上安裝 NixOS

Nix 是一款能讓你透過程式碼來定義軟體環境的工具。Nix 包含數個組成部分,其中對我而言最有趣的是 NixOS,它能讓你使用 Nix 工具鏈,透過純文字檔案來定義整個作業系統的設定。

我最近才開始嘗試使用 Nix,還有非常多東西要學。我最早嘗試的事情之一就是在我的 Raspberry Pi 上安裝 NixOS,但最初的幾次嘗試都失敗了。我找到的每一篇 NixOS Pi 教學不是內容不完整,就是已經過時。

在此為你呈現一份完整且可行的 Raspberry Pi 4 安裝 NixOS 指南。我也是 NixOS 的新手,因此這份指南是為 Nix 初學者而寫,但我假設你對 Raspberry Pi 和 Linux 已有基本的了解。

需求

若要依照本教學操作,你會需要:

  • 至少具備 2 GB 記憶體的 Raspberry Pi 4
  • 至少 8 GB 儲存空間的 microSD 記憶卡
  • microSD 讀卡機
  • 用來燒錄 microSD 記憶卡的另一台電腦

下載 NixOS microSD 映像檔

首先,請從下方連結下載 NixOS microSD 映像檔:

你可以在 Nix 的建構伺服器上,查看帶有綠色勾號的最新建構,找到更新的映像檔。

我最近曾嘗試使用較新的建構版本(nixos-image-sd-card-25.05beta741800.78886a72ed11,建構於 2025-01-19),來執行此流程,但安裝失敗。後續的 nixos-build 步驟耗盡了我那台 2 GB 記憶體的 Pi 4 的資源。

解壓縮 NixOS microSD 映像檔

NixOS 團隊使用名為 Zstandard 的壓縮格式來壓縮其 microSD 映像檔,這是一種來自 Facebook 的開放原始碼格式。

若要解壓縮 NixOS 映像檔,請為你的平台下載最新版本的 Zstandard:

當你同時備妥 Zstandard 工具與 NixOS microSD 映像檔後,請使用下列指令解壓縮 .img.zst 檔案:

zstd --decompress 'nixos-sd-image-23.11pre515819.8ecc900b2f69-aarch64-linux.img.zst'

解壓縮 Zstandard 檔案後,應該會產生一個名為 nixos-sd-image-23.11pre515819.8ecc900b2f69-aarch64-linux.img 的檔案。

燒錄 NixOS microSD 映像檔

解壓縮映像檔後,請使用你慣用的 microSD 燒錄工具將其燒錄至 microSD 記憶卡。

燒錄 microSD 時,請選擇 .img 檔案而非 .img.zst 檔案,因為大多數燒錄工具無法辨識 Zstandard 格式。

選項 1:balenaEtcher

如果你不知道該使用哪款 microSD 燒錄工具,我推薦 balenaEtcher。它操作簡單,且可在各大作業系統上使用。

balenaEtcher 螢幕截圖

選項 2:caligula

balenaEtcher 無法在 NixOS 上使用,因此如果你使用的是 NixOS,caligula 是一個不錯的替代方案:

caligula burn \
  nixos-sd-image-23.11pre515819.8ecc900b2f69-aarch64-linux.img.zst

caligula 原生支援 Zstandard 檔案壓縮,因此你不需要先解壓縮映像檔。

將 microSD 記憶卡插入 Pi

燒錄完成後,請將 microSD 記憶卡插入 Raspberry Pi 的 microSD 插槽:

已插入 Raspberry Pi microSD 插槽的 microSD 記憶卡照片

將已燒錄好的 microSD 記憶卡插入 Pi 的 microSD 插槽。

為 Pi 連接螢幕與鍵盤

大多數 Raspberry Pi 映像檔在首次開機時都提供了透過網路存取裝置的方式。我還沒找到在 NixOS 上這樣做的方法,因此你需要暫時為 Pi 連接鍵盤和 HDMI 螢幕,才能查看執行狀況。

Raspberry Pi 啟動 NixOS 時連接 HDMI 螢幕與鍵盤的照片

NixOS 沒有完全透過網路的安裝方式,因此在初始設定期間,你需要連接鍵盤和 HDMI 螢幕。

在本教學中,我使用 TinyPilot 來控制我的 Pi,這是一款我為這類情境所打造的裝置。

TinyPilot 連接至 Raspberry Pi 4 的照片TinyPilot 控制執行 Plasma 桌面環境的 NixOS 系統的螢幕截圖

我使用 TinyPilot 裝置在 Raspberry Pi 上安裝 NixOS,因為它讓我不必在不同鍵盤之間來回切換。

本教學不需要 TinyPilot,你使用一般的鍵盤和 HDMI 螢幕也能跟著操作。

啟動你的 NixOS 系統

見真章的時刻到了。請啟動你的 Raspberry Pi。

如果一切順利,你應該會看到如下的開機過程:

NixOS microSD 映像檔在 Raspberry Pi 4 上成功開機的畫面。

當你看到 NixOS 命令提示字元時,即表示開機完成:

[nixos@nixos~:]$

如果開機失敗,請嘗試將 Pi 的 bootloader 更新至最新可用版本後再試一次。

啟用 SSH 存取(選用)

在使用 Raspberry Pi 時,我覺得透過 SSH 操作比在另一組鍵盤上輸入要方便得多。

在全新的 NixOS 系統上,有兩種啟用 SSH 存取的方式。

選項 1:設定密碼

在 NixOS 系統上,你可以執行下列指令,為預設的 nixos 使用者帳號設定密碼:

passwd

設定密碼後,你就可以正常透過 SSH 連線至 NixOS 系統:

ssh [email protected]

選項 2:新增 SSH 金鑰

你也可以將你的 SSH 公鑰新增為系統上的授權金鑰。

如果你使用 SSH 金鑰向 GitHub 進行驗證,GitHub 提供了一個方便的方法,可將你的公開 SSH 金鑰下載到任何裝置上:

GITHUB_USERNAME='your-github-username' # Replace this.

mkdir -p ~/.ssh && \
  curl "https://github.com/${GITHUB_USERNAME}.keys" > ~/.ssh/authorized_keys

如果你看到顯示 certificate is not valid yet 的錯誤訊息,表示你的 Pi 仍在同步系統時間。請等待 60 秒後再重試該指令。

將公開 SSH 金鑰新增至 NixOS 系統後,你就可以像平常一樣透過 SSH 連線:

ssh [email protected]

撰寫 NixOS 設定檔

你現在已經進入 NixOS 了!

目前還沒太多可做的事,因為這是一個精簡的 NixOS 環境,尚未安裝任何套件。

為了讓 NixOS 體驗更有趣,來安裝桌面圖形介面和一些應用程式。首先,請下載我的 NixOS 設定檔範例

curl \
  --show-error \
  --fail \
  https://mtlynch.io/nixos-pi4/configuration.nix \
  | sudo tee /etc/nixos/configuration.nix

此時你可以使用 nanovim/etc/nixos/configuration.nix 進行修改。你可能會想變更檔案最上方 hostnameuserpassword 的值。

sudo nano /etc/nixos/configuration.nix

先不用太擔心要把設定檔寫到完美。使用 NixOS 時,你隨時都可以改變任何選項的想法,而套用變更就像再次編輯設定檔一樣簡單。

當你對 configuration.nix 檔案感到滿意後,請執行下列指令將設定套用至系統並重新啟動:

sudo nixos-rebuild boot && \
  echo "install complete, rebooting..." && \
  sudo poweroff --reboot

重新啟動完成後,你應該會看到如下畫面:

你的 Pi 現在已經在執行搭載 Gnome 桌面環境的 NixOS 了!

如果你使用上述預設的 configuration.nix 檔案,你的使用者名稱為 tempuser,密碼為 somepass

試用 NixOS

到此,你的 NixOS 系統已經啟動並開始運作。

你可以隨心所欲地探索 NixOS,不過我在下方為你在新系統上準備了幾個適合初學者的實驗可以試試看。

實驗 1:更換桌面環境

上述的 configuration.nix 檔案假設你想使用 Gnome 桌面環境,但或許你偏好其他選擇。還有另一款名為 Plasma 的桌面管理員,其設計與 Microsoft Windows 相似。

若要將 NixOS 系統改為使用 Plasma 而非 Gnome,請在文字編輯器中開啟你的 configuration.nix 檔案:

sudo nano /etc/nixos/configuration.nix

在檔案中找到以下幾行:

    displayManager.gdm.enable = true;
    desktopManager.gnome.enable = true;

將它們替換為以下幾行:

    displayManager.sddm.enable = true;
    desktopManager.plasma5.enable = true;

若要套用變更,請儲存檔案、離開 nano,然後執行下列指令:

sudo nixos-rebuild boot && sudo reboot

重新啟動後,你應該會看到如下的桌面:

TinyPilot 控制執行 Plasma 桌面環境的 NixOS 系統在登入畫面的螢幕截圖TinyPilot 控制執行 Plasma 桌面環境的 NixOS 系統在桌面畫面的螢幕截圖

在 NixOS 中,將桌面管理員從 Gnome 切換為 Plasma 只需修改兩行設定。

要更換整個桌面環境,只需修改兩行就完成了。

實驗 2:建立臨時軟體環境

我覺得最容易上手的 Nix 工具之一是 nix-shell。它能讓你即時使用指定的任何軟體套件來建立軟體環境。

nix-shell 不會影響系統上的任何其他設定,因此你可以放心嘗試新工具,而不用擔心會破壞其他東西。

我有時會遇到幾年前寫的專案,其相依的是較舊版本的 Node.js。我曾嘗試使用像 nvm 這類工具來並存安裝多個 Node 版本,但每次總要花 20 分鐘回想如何正確使用和設定 nvm

儘管 nix-shell 是一款用於安裝套件的通用工具,我卻覺得它甚至比 nvm 這類針對特定語言的開發工具還要方便。

以下是如何使用 nix-shell 建立 Node.js 18.x 環境:

$ nix-shell --packages nodejs-18_x
these paths will be fetched (11.25 MiB download, 52.36 MiB unpacked):
  /nix/store/87kgx3ym4kgmqwaijckqvbfrkzm8ax75-nodejs-18.2.0
copying path '/nix/store/87kgx3ym4kgmqwaijckqvbfrkzm8ax75-nodejs-18.2.0' from 'https://cache.nixos.org'...

[nix-shell:~]$ node --version
v18.2.0

[nix-shell:~]$ npm --version
8.9.0

使用完環境後,只需按下 Ctrl+D 或輸入 exit 即可離開。

以下是建立 Node.js 16.x 環境的相同做法:

$ nix-shell --packages nodejs-16_x
these paths will be fetched (10.77 MiB download, 50.24 MiB unpacked):
  /nix/store/1ba3sqw3rkadg2ksywqc85lq2hvx9fvk-nodejs-16.15.0
copying path '/nix/store/1ba3sqw3rkadg2ksywqc85lq2hvx9fvk-nodejs-16.15.0' from 'https://cache.nixos.org'...

[nix-shell:~]$ node --version
v16.15.0

[nix-shell:~]$ npm --version
8.5.5

疑難排解

升級至最新的 Pi bootloader

如果你在 NixOS 上遇到開機問題,你可能需要更新 Pi 的 bootloader 和 EEPROM

請先啟動最新版本的 Raspberry Pi OS (aka “Raspbian”),然後執行下列指令來安裝最新的 bootloader:

sudo raspi-config nonint do_boot_rom E1 && \
  sudo reboot

若要更新 EEPROM,請執行下列指令:

sudo apt update && \
  sudo apt install --yes rpi-eeprom && \
  sudo rpi-eeprom-update -a && \
  sudo reboot

我測試的 Pi 4 裝置可直接開機進入 NixOS 23.11 磁碟映像檔,因此上述步驟對我而言並非必要。

附錄:失敗的嘗試

在製作本教學的過程中,我嘗試了許多行不通的方法。我將它們整理於此,希望能為他人省下重試相同步驟的時間。


感謝來自 NixOS 文件團隊的 Alex Groleau(艾力克斯·格羅洛)對本指南的協助,以及他對官方 NixOS Raspberry Pi 教學的貢獻。

原文由 Michael Lynch 發布

本文章由 muse-spark-1.2-contributor 進行翻譯