NixOS 服务管理实战:systemd 声明、NixOS 容器与常用服务部署

NixOS 服务管理实战:声明式 systemd 服务、服务依赖与开机自启、环境变量与秘钥、NixOS 容器(NixOS Containers)、常用服务部署(nginx/postgres/redis/cron)、服务监控与开机启动时序。

引言

NixOS 上「部署一个服务」不是写 shell 脚本和 systemd unit 文件,而是在 configuration.nix 里声明它——NixOS 会帮你生成 systemd unit、装好依赖、配好开机自启,全部可回滚。本文从声明一个 systemd 服务讲起,到 NixOS 原生容器和 nginx/postgres 等常用服务的标准部署。

前置:/nixos-configuration/(configuration.nix 基础)、/nixos-network-firewall/(网络与防火墙)。


目录


1. 服务的声明式思维

1.1 传统 vs NixOS

传统方式NixOS 方式
写 systemd unit + 脚本systemd.services.<name> 声明
手工装依赖Nix 自动带上
手工 enable/startrebuild switch 即生效
环境不一致配置即部署

1.2 一个例子

systemd.services.myapp = {
  description = "My application";
  wantedBy = [ "multi-user.target" ];   # 开机自启
  serviceConfig = {
    ExecStart = "${pkgs.myapp}/bin/myapp --config /etc/myapp.conf";
    Restart = "on-failure";
    User = "myapp";
  };
};

记忆:NixOS 服务 = 在 systemd.services. 里声明 unit,rebuild switch 即部署生效、可回滚,依赖由 Nix 自动解析。


2. systemd 服务声明:写一个最小服务

2.1 最小可用服务

{ pkgs, ... }:
{
  systemd.services.hello = {
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStart = "${pkgs.hello}/bin/hello";
      Restart = "always";
    };
  };
}

2.2 常用 serviceConfig 字段

字段含义
ExecStart启动命令
Restarton-failure / always / no
User/Group运行身份
WorkingDirectory工作目录
Environment环境变量
LimitNOFILE文件描述符上限
TimeoutStartSec启动超时

记忆:最小服务 = systemd.services. + wantedBy 开机自启 + ExecStart/Restart;其余按需加字段。


3. 依赖、时序与开机自启

3.1 依赖声明

systemd.services.backend = {
  after = [ "network-online.target" "database.service" ];  # 之后启动
  requires = [ "database.service" ];                        # 强依赖
  wantedBy = [ "multi-user.target" ];
  serviceConfig.ExecStart = "${pkgs.backend}/bin/backend";
};

3.2 常见依赖目标

target时机
multi-user.target多用户登录后
network-online.target网络就绪后
local-fs.target文件系统挂载后

记忆:after 控制顺序、requires 控制强依赖、wantedBy 控制开机自启;网络就绪用 network-online.target。


4. 环境变量、用户与秘钥

4.1 环境变量

systemd.services.myapp = {
  serviceConfig.Environment = [
    "PORT=8080"
    "LOG_LEVEL=info"
  ];
  # 或引用文件
  environmentFiles = [ /etc/myapp.env ];
};

4.2 专用系统用户

users.users.myapp = {
  isSystemUser = true;
  group = "myapp";
};
users.groups.myapp = { };

systemd.services.myapp.serviceConfig.User = "myapp";

4.3 秘钥引用(配合 sops/agenix)

systemd.services.myapp = {
  environmentFiles = [ config.age.secrets.myapp_env.path ];   # 挂在 /run/secrets/
};

记忆:环境变量用 serviceConfig.Environment 或 environmentFiles;服务建专用系统用户;秘钥用 age/sops 解密到 /run/secrets 引用。


5. 常用服务:nginx 与反向代理

5.1 静态站点

services.nginx = {
  enable = true;
  virtualHosts."example.com" = {
    root = "/var/www/example";
    enableACME = true;                 # Let's Encrypt 自动证书
    forceSSL = true;
  };
};

5.2 反向代理到后端

services.nginx.virtualHosts."api.example.com" = {
  locations."/" = {
    proxyPass = "http://127.0.0.1:8080";
    proxyWebsockets = true;
  };
};
networking.firewall.allowedTCPPorts = [ 80 443 ];

记忆:nginx 在 services.nginx.virtualHosts 声明;enableACME + forceSSL 自动 HTTPS;反向代理用 proxyPass。


6. 常用服务:PostgreSQL 与 Redis

6.1 PostgreSQL

services.postgresql = {
  enable = true;
  package = pkgs.postgresql_16;
  initialDatabases = [{ name = "myapp"; }];
  authentication = ''
    local all all trust
    host all all 127.0.0.1/32 trust
  '';
  settings = { max_connections = 100; };
};

6.2 Redis

services.redis.servers = {
  default = {
    enable = true;
    port = 6379;
    bind = "127.0.0.1";
  };
};

记忆:PostgreSQL 在 services.postgresql 声明初始库与认证;Redis 用 services.redis.servers.default 声明监听地址端口。


7. NixOS 容器:轻量隔离

7.1 什么是 NixOS 容器

NixOS 原生容器(nixos-container)——不用 Docker、不用手动装运行时,声明式定义,开箱即隔离:

containers.web = {
  privateNetwork = true;
  hostAddress = "10.233.0.1";
  localAddress = "10.233.0.2";
  config = { pkgs, ... }: {
    services.nginx.enable = true;
  };
};

7.2 管理与限制

nixos-container list        # 列出容器
nixos-container login web   # 进入容器

记忆:NixOS 容器 = 声明式轻量隔离(无需 Docker);containers..config 写该容器自己的配置,hostAddress/localAddress 配网。


8. 定时任务与 systemd.timer

8.1 声明定时任务

systemd.timers.backup = {
  wantedBy = [ "timers.target" ];
  timerConfig = {
    OnCalendar = "daily";        # 每天
    Persistent = true;           # 补跑错过的
  };
};
systemd.services.backup = {
  serviceConfig.ExecStart = "${pkgs.rsync}/bin/rsync -a /data /backup/";
};

8.2 替代 cron

NixOS 也提供传统 cron:

services.cron = {
  enable = true;
  systemCronJobs = [ "0 3 * * * root /usr/bin/backup" ];
};

记忆:定时任务推荐 systemd.timers(OnCalendar 声明周期 + Persistent 补跑);要传统 cron 用 services.cron.systemCronJobs。


9. 服务监控与排障

9.1 查看服务状态

systemctl status myapp        # 状态
journalctl -u myapp -f        # 实时日志
systemctl list-units --failed # 失败单元

9.2 常见排障

现象排查
启动失败journalctl -u 看日志
依赖超时检查 after/requires
端口占用ss -tlnp 查看
权限拒绝检查 User/Group 与 secret 权限

记忆:排障先用 journalctl -u <服务> 看日志;依赖/端口/权限是三大常见失败源。


10. 速查表与一句话记忆

场景声明位置
自定义服务systemd.services.
依赖顺序after / requires
开机自启wantedBy multi-user.target
环境变量serviceConfig.Environment
系统用户users.users.
nginxservices.nginx.virtualHosts
PostgreSQLservices.postgresql
Redisservices.redis.servers
容器containers.
定时任务systemd.timers

一句话记忆:NixOS 服务部署 = 在 configuration.nix 声明——自定义服务用 systemd.services.(wantedBy 开机自启、after/requires 定序、Environment 传环境、专用用户跑服务);常用服务有现成模块(nginx 反代 + 自动证书、postgres 初始库、redis 监听);轻量隔离用 NixOS 容器(无需 Docker);定时任务用 systemd.timers;一切 rebuild switch 原子生效可回滚,排障看 journalctl -u。


延伸阅读

  • /nixos-configuration/ — configuration.nix 与模块系统
  • /nixos-network-firewall/ — 服务端口与防火墙配合
  • /nix-secrets-management/ — 服务秘钥注入(/run/secrets)
  • /nixos-storage-filesystems/ — 服务数据卷与存储
  • /nixos-operations-maintenance/ — 服务升级与回滚
  • [[linux]] — systemd 服务管理实操
  • [[devops]] — 服务部署与可观测性
  • NixOS systemd 模块文档

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「nix」更多文章

  1. NixOS 运维实战:升级、回滚、GC 与日常维护
  2. NixOS 网络与防火墙配置:声明式网络管理实战
  3. NixOS 密钥管理实战:sops-nix、agenix 与声明式秘钥分发