NixOS 模块系统
NixOS 的整个配置系统——包括你的 configuration.nix——都是由模块组成的。每个模块是一个函数,接受 { config, pkgs, lib, ... } 并返回包含 options(声明配置接口)和 config(实现配置值)的 attrset。
模块系统工作原理
──────────────────────────────────────────────────
模块 A 模块 B 模块 C
options: options: options:
services.nginx services.nginx ...
.enable .virtualHosts
config: config:
if enable then for each vhost
install nginx generate config
──────────────────────────────────────────────────
│ │
└────────────────────┘
│
NixOS 模块系统合并所有模块的
options 声明和 config 值
│
生成最终系统配置
模块结构剖析
# 一个完整的 NixOS 模块结构
{ config, pkgs, lib, ... }:
{
# ── options:声明这个模块提供的配置接口 ──
options.my.service = {
# mkEnableOption 生成标准的 enable 选项
enable = lib.mkEnableOption "my custom service";
port = lib.mkOption {
type = lib.types.port; # 类型验证
default = 8080;
description = "Listening port";
};
logLevel = lib.mkOption {
type = lib.types.enum [ "debug" "info" "warn" "error" ];
default = "info";
};
};
# ── config:当 enable = true 时的实现 ──
config = lib.mkIf config.my.service.enable {
systemd.services.my-service = {
description = "My Custom Service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.my-app}/bin/my-app --port ${toString config.my.service.port}";
Restart = "always";
User = "my-service";
};
};
users.users.my-service = {
isSystemUser = true;
group = "my-service";
};
users.groups.my-service = {};
};
}
常用 lib 函数
lib.mkIf
lib.mkIf condition value:条件性地设置配置项。当 condition 为 false 时,value 被忽略。用于"只有 enable = true 时才生成实际配置"。lib.mkEnableOption
lib.mkEnableOption "description":生成标准的 enable 布尔选项,默认值为 false,描述为 "Whether to enable description"。lib.mkOption
声明一个配置选项,指定类型、默认值、描述、示例等属性。支持的类型:bool、int、port、str、path、package、listOf、attrsOf、enum 等。
lib.mkDefault / lib.mkForce
控制选项值的优先级。
mkDefault 设置低优先级默认值(可被其他模块覆盖),mkForce 设置高优先级值(强制覆盖其他设置)。lib.mkMerge
lib.mkMerge [ config1 config2 ]:合并多个配置集合,等价于多个模块同时被激活。Nginx 服务配置
{ config, pkgs, ... }:
{
services.nginx = {
enable = true;
# 推荐的安全配置
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
# 虚拟主机配置
virtualHosts = {
# 静态站点
"example.com" = {
enableACME = true; # 自动申请 Let's Encrypt 证书
forceSSL = true;
root = "/var/www/example.com";
locations."/".tryFiles = "$uri $uri/ /index.html";
};
# 反向代理到后端
"api.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
extraConfig = ''
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
};
};
};
# ACME(Let's Encrypt)配置
security.acme = {
acceptTerms = true;
defaults.email = "admin@example.com";
};
# 开放 80/443 端口
networking.firewall.allowedTCPPorts = [ 80 443 ];
}
PostgreSQL 服务配置
{
services.postgresql = {
enable = true;
package = pkgs.postgresql_16; # 指定版本
# 认证配置(pg_hba.conf)
authentication = pkgs.lib.mkOverride 10 ''
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
'';
# 初始化脚本(创建数据库和用户)
initialScript = pkgs.writeText "pg-init" ''
CREATE USER myapp WITH PASSWORD 'secret';
CREATE DATABASE myapp OWNER myapp;
GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp;
'';
# postgresql.conf 设置
settings = {
max_connections = 100;
shared_buffers = "256MB";
effective_cache_size = "1GB";
};
};
}
Docker 与虚拟化
{
# 启用 Docker
virtualisation.docker = {
enable = true;
autoPrune.enable = true; # 定期清理未使用镜像
daemon.settings = {
log-driver = "json-file";
log-opts = { max-size = "10m"; max-file = "3"; };
};
};
# 启用 Podman(rootless 容器,Docker 替代)
virtualisation.podman = {
enable = true;
dockerCompat = true; # 提供 docker 命令兼容层
};
# 将用户加入 docker 组
users.users.alice.extraGroups = [ "docker" ];
}
自定义 Systemd 服务
{ pkgs, ... }:
{
systemd.services.my-webapp = {
description = "My Web Application";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" ];
requires = [ "postgresql.service" ];
environment = {
NODE_ENV = "production";
PORT = "3000";
# 注意:敏感信息应使用 sops-nix(第10章)
};
serviceConfig = {
ExecStart = "${pkgs.nodejs_20}/bin/node /var/www/app/server.js";
WorkingDirectory = "/var/www/app";
User = "webapp";
Group = "webapp";
Restart = "on-failure";
RestartSec = "5s";
# 安全加固
PrivateTmp = true;
ProtectSystem = "strict";
NoNewPrivileges = true;
};
};
# 创建服务用户
users.users.webapp = {
isSystemUser = true;
group = "webapp";
home = "/var/www/app";
createHome = true;
};
users.groups.webapp = {};
}
完整 Web 服务实战
将 Nginx + PostgreSQL + 自定义 App 组合部署:
# /etc/nixos/configuration.nix 片段
{ config, pkgs, ... }:
{
# 包含其他模块文件
imports = [
./hardware-configuration.nix
./services/webapp.nix # 自定义模块
];
# 启用自定义模块
my.webapp = {
enable = true;
domain = "mysite.com";
port = 3000;
};
# Nginx
services.nginx.enable = true;
# PostgreSQL
services.postgresql.enable = true;
# 防火墙
networking.firewall.allowedTCPPorts = [ 22 80 443 ];
}
# /etc/nixos/services/webapp.nix —— 自定义模块
{ config, pkgs, lib, ... }:
let
cfg = config.my.webapp;
in {
options.my.webapp = {
enable = lib.mkEnableOption "my web application";
domain = lib.mkOption { type = lib.types.str; };
port = lib.mkOption { type = lib.types.port; default = 3000; };
};
config = lib.mkIf cfg.enable {
# Nginx 配置
services.nginx.virtualHosts.${cfg.domain} = {
enableACME = true;
forceSSL = true;
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
};
# 应用服务
systemd.services.webapp = {
wantedBy = [ "multi-user.target" ];
after = [ "postgresql.service" ];
serviceConfig.ExecStart = "...";
};
};
}
本章小结
NixOS 模块系统通过 options/config 分离实现了声明式的服务配置:options 定义配置接口,config 实现实际效果,lib.mkIf 实现条件激活。Nixpkgs 中有数百个内置服务模块(Nginx、PostgreSQL、Redis、Docker 等),配置只需几行 Nix 代码,且所有配置都是类型安全的,错误的配置值在 nixos-rebuild 时就会报错,而不是运行时才发现。