Core concepts at a glance#
NixOS configuration has three layers. Understanding their relationship is the foundation for everything else:
flake.nix ← nix 命令直接读取,声明依赖,组装系统
└── configuration.nix ← 系统级配置(需要 sudo)
└── home.nix ← 用户级配置(home-manager 管理)Nix language basics#
A Nix file is essentially an expression that returns a value; the most common return value is an attribute set (a collection of key-value pairs).
Attribute sets (collections of key-value pairs)
{
name = "Alice";
age = 25;
nested = {
key = "value";
};
}Functions
# 基本形式:参数: 返回值
x: x + 1
# 解构 attribute set 作为参数
{ name, age }: "Hello, ${name}, you are ${age}"
# 带默认值
{ name, age ? 0 }: "Hello ${name}"
# 捕获整个参数集合(@语法)
{ name, ... }@args: argsCommon syntactic sugar
# with 语句:展开 attribute set,避免重复前缀
with pkgs; [ git neovim curl ]
# 等价于
[ pkgs.git pkgs.neovim pkgs.curl ]
# inherit:继承上层作用域的变量
{ inherit inputs pkgs; }
# 等价于
{ inputs = inputs; pkgs = pkgs; }
# let...in:局部变量
let
myVar = "hello";
in
"${myVar} world"
# 字符串插值
"Hello ${name}, you are on ${config.networking.hostName}"Lists
[ "item1" "item2" pkgs.git ]flake.nix in detail#
flake.nix is not a function, but an attribute set with a fixed schema. Nix tools recognize this structure, much as npm recognizes package.json.
{
description = "My NixOS config"; # 可选,描述信息
inputs = {
# 声明外部依赖;默认作为 flake 处理,也可用 flake = false 引入普通源码
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs"; # 让 HM 用同一份 nixpkgs,避免重复下载
};
};
# outputs 的值是函数,参数是所有 inputs 求值后的结果
outputs = { self, nixpkgs, home-manager, ... }@inputs: {
# nixosConfigurations 是约定的 key,nixos-rebuild 会来这里找
nixosConfigurations."workstation" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# specialArgs 把额外变量传递给所有子模块
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.alice = import ./home.nix;
home-manager.extraSpecialArgs = { inherit inputs; };
}
];
};
};
}What inputs.X.follows does
home-manager.inputs.nixpkgs.follows = "nixpkgs";It points Home Manager’s nixpkgs input to the same locked node as the system’s, reducing duplicate inputs. Different inputs may produce different dependency sets, but do not necessarily cause duplicate downloads or incompatibility; the Home Manager branch also needs to match the system release.
flake.lock
Generated automatically, recording the exact git commit hash of every input. Do not edit it manually.
nix flake update→ update all inputsnix flake update nixpkgs→ update only nixpkgs
configuration.nix in detail#
System-level configuration for the entire machine. Changes require sudo nixos-rebuild switch.
# 这是一个函数,参数由 NixOS 模块系统传入
{ config, lib, pkgs, inputs, ... }:
{
imports = [
./hardware-configuration.nix # 硬件配置,自动生成的
./modules/audio.nix # 可以拆分成子模块
];
# ── Boot ──────────────────────────────
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# ── Network ───────────────────────────
networking.hostName = "workstation";
networking.networkmanager.enable = true;
# ── System packages ───────────────────
environment.systemPackages = with pkgs; [
wget curl git neovim
];
# ── Users ─────────────────────────────
# 本例把用户包统一交给 home.nix,用户身份和组由系统管理
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "video" ];
};
# ── 不要改这个值 ──────────────────────
system.stateVersion = "25.11";
}Parameters explained
| Parameter | Meaning |
|---|---|
pkgs |
The collection of all packages in nixpkgs |
config |
The current system configuration (can reference other options) |
lib |
The nixpkgs utility function library |
inputs |
Flake inputs (must be passed through specialArgs) |
home.nix in detail#
User-level configuration managed by home-manager. It configures an individual user’s home directory, dotfiles, and user software.
{ config, pkgs, inputs, ... }:
{
imports = [
./modules/shell.nix
./modules/dev.nix
inputs.some-flake.homeModules.default # 接口示意,须在 inputs 中定义并按该项目实际输出替换
];
# ── 必填 ──────────────────────────────
home.username = "alice";
home.homeDirectory = "/home/alice";
home.stateVersion = "25.11"; # 不要随意改
# ── 用户软件 ──────────────────────────
home.packages = with pkgs; [
htop btop obsidian
ghostty kitty
];
# ── 程序配置(带 dotfile 管理)─────────
programs.git = {
enable = true;
settings = {
user.name = "Example User";
user.email = "user@example.com";
init.defaultBranch = "main";
};
};
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
};
programs.home-manager.enable = true;
}The boundary between system-level and user-level configuration#
The deciding question: does the “machine” need this, or does the “user” need it?
Put in configuration.nix |
Put in home.nix |
|---|---|
| Kernel modules, drivers | Terminal emulators, editors |
| System services (pipewire, bluetooth) | Shell configuration (zsh, bash) |
| Firewall, networking | dotfiles |
| Fonts (system-wide) | User packages |
users.users.X (accounts and groups) |
programs.git, programs.zsh, and so on |
| Input method framework (fcitx5) | Personal input method configuration |
A single entry point for package management
# 有效的 NixOS 用户包选项,但在此组织方式中避免双入口
users.users.alice.packages = with pkgs; [ obsidian ];
# 使用 Home Manager 时可统一放在 home.nix
home.packages = with pkgs; [ obsidian ];The module system#
NixOS and home-manager use the same module system. Understanding it lets you read any configuration.
# 一个模块的完整结构
{ config, lib, pkgs, ... }:
{
# imports:引入其他模块
imports = [ ./other-module.nix ];
# options:声明这个模块提供的选项(写库/框架时用)
options.myModule.enable = lib.mkOption {
type = lib.types.bool;
default = false;
};
# config:实际配置(条件激活时常用)
config = lib.mkIf config.myModule.enable {
environment.systemPackages = [ pkgs.hello ];
};
}Most of the time, you only need top-level configuration, without options and config wrappers.
Recommended directory structure#
/etc/nixos/
├── flake.nix # 入口,声明依赖
├── flake.lock # 自动生成,不要手动改
├── hardware-configuration.nix # 自动生成
├── configuration.nix # 系统配置入口(精简,只 imports)
├── modules/ # 系统级子模块
│ ├── locale.nix # i18n / 时区 / 输入法
│ ├── graphics.nix # GPU / Hyprland / display
│ ├── audio.nix # pipewire / bluetooth
│ ├── network.nix # network / firewall
│ └── nix-settings.nix # substituters / nix-ld / flakes
└── home/
├── home.nix # 用户配置入口
├── packages.nix # home.packages 列表
└── programs/
├── shell.nix # zsh/bash/starship/zoxide
├── dev.nix # git/zellij/direnv/neovim
└── desktop.nix # foot/ghostty/主题Common command reference#
Updating the lock file and activating are two separate steps. Review the lock-file diff and build first; once the build passes, run test, boot, or switch as needed. Cleanup commands remove rollback points.
# 重建并切换到新配置
sudo nixos-rebuild switch --flake /etc/nixos#workstation
# 临时激活新配置(会切换运行系统,重启回到启动默认项)
sudo nixos-rebuild test --flake /etc/nixos#workstation
# 回滚到上一代
sudo nixos-rebuild switch --rollback
# 列出所有系统代
nix-env --list-generations --profile /nix/var/nix/profiles/system
# 更新所有 flake inputs
sudo nix flake update --flake /etc/nixos
# 只更新某一个 input
sudo nix flake update nixpkgs --flake /etc/nixos
# 清理旧代(释放磁盘)
sudo nix-collect-garbage -d
# 查找包
nix search nixpkgs firefox
# 临时试用一个包(不安装)
nix shell nixpkgs#ripgrep
# 进入 devshell
nix developPer-project dev shell template#
Use Nix to provide project tools and system dependencies. Whether to add Python venv/uv depends on the project; mkShell itself does not provide container-style process isolation.
# 放在项目根目录的 flake.nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = { nixpkgs, ... }:
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
in {
devShells.x86_64-linux.default = pkgs.mkShell {
packages = with pkgs; [
# Python 科学计算(让 nix 管,不要 pip 装)
(python3.withPackages (ps: [
ps.numpy ps.matplotlib ps.scipy
]))
uv # 只用来管理纯 Python 包,不碰系统包
];
shellHook = ''
echo "Dev shell ready"
'';
};
};
}# 进入项目 dev shell
cd my-project
nix develop
# 也可以用 direnv 自动激活(需要 programs.direnv.enable = true)
echo "use flake" > .envrc
direnv allowDebugging tips#
Dependency tracing requires two store paths, and the second must correspond to the package version actually used by the current system. The registry’s nixpkgs may differ from the system’s locked input. nix fmt also requires the Flake to define a corresponding formatter.
# 查看某个选项的文档
nixos-option services.pipewire.enable
# 查看某个包在哪里
nix eval nixpkgs#git.outPath
# 为什么某个包被引入(依赖追踪)
nix why-depends /run/current-system "$(nix eval --raw nixpkgs#gcc.outPath)"
# 检查 flake 结构
nix flake show /etc/nixos
# 格式化 nix 文件
nix fmtReference resources#
- NixOS Option Search
- NixOS Package Search
- Home Manager Options
- Nix Language Tutorial
- NixOS Wiki
- Unofficial Flakes Guide (Chinese-language friendly)
References & further reading
- NixOS Manual ↗
- Nix Language Manual ↗
- Home Manager Manual ↗
- Nix: flake update ↗
- NixOS & Flakes Book in Chinese (cited in the original notes) ↗
- Home Manager Git Module ↗
Adapted from technical notes. Examples use generic names.
Continue along a reading path
NixOS system foundations and configuration
- 01NixOS: the foundations of a declarative system
- 02Installing NixOS: installation media, partitions, and initial configuration
- 03NixOS configuration basics: options and modules
- 04Managing a system with Flakes and Home Manager
- 05The NixOS command line: old and new interfaces and everyday maintenance
- 06NixOS generation management and garbage collection
- 07Specialisation: multiple configuration variants in one generation
- 08If configuration is reproducible, why do apps still forget?