OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 072

通过 SSH 部署 NixOS

区分本地构建、远端临时激活、持久切换和故障回滚。

所在专题NixOS 虚拟化与部署 ↗
约 14 分钟阅读开始阅读

版本范围:下列示例保留 NixOS 24.05 的教程上下文;该版本已结束维护。实际项目应选择受支持的发行版、锁定 nixpkgs revision,并核对相应选项。system.stateVersion 表示兼容性基线,不应随升级机械改动。本文命令是教学示例,未在目标系统运行验证。

翻译自 nix.dev: Provisioning remote machines via SSH

通过 SSH 将 NixOS 部署到远程服务器,实现基础设施即代码。

前置要求#

  • 已安装 NixOS 的目标服务器
  • SSH 密钥访问权限
  • 本地已安装 Nix

基本部署流程#

1. 准备目标服务器

确保远程服务器已启用 SSH:

# 在远程服务器上检查
systemctl status sshd

2. 本地配置构建

创建部署配置 deployment.nix:

{ config, pkgs, ... }:

{
  # 网络配置
  networking.hostName = "remote-server";
  networking.firewall.enable = true;
  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
  
  # SSH 配置
  services.openssh = {
    enable = true;
    settings = {
      PermitRootLogin = "no";
      PasswordAuthentication = false;
    };
  };
  
  # 用户配置
  users.users.deploy = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    openssh.authorizedKeys.keys = [
      "<DEPLOY_SSH_PUBLIC_KEY>"
    ];
  };
  
  # Web 服务示例
  services.nginx = {
    enable = true;
    virtualHosts."example.com" = {
      root = "/var/www";
      locations."/" = {
        extraConfig = "index index.html;";
      };
    };
  };
  
  system.stateVersion = "24.05";
}

3. 使用 nixos-rebuild 远程部署

目标必须已经安装 NixOS,部署配置还应导入该目标的 hardware-configuration.nix、文件系统与引导模块;这里的服务片段不是完整裸机配置。先保持一个可用 SSH 会话和控制台恢复路径,确认 deploy 用户能提升到 root。24.05 的 rebuild 使用 --use-remote-sudo;新版本 rebuild-ng 的对应参数可能为 --sudo,以目标工具的 --help 为准。

先构建,再测试;test 会修改正在运行的系统,只是不设为下次启动默认值。switch 同时激活并更新系统 generation/启动配置。

# 构建,不激活目标
nixos-rebuild build -I nixos-config=./deployment.nix
# 实际远程临时激活,先确认影响再执行
nixos-rebuild test --target-host deploy@remote-server --use-remote-sudo \
  -I nixos-config=./deployment.nix
# 远程部署并应用配置
nixos-rebuild switch \
  --target-host deploy@remote-server \
  --use-remote-sudo \
  -I nixos-config=./deployment.nix

参数说明:

  • --target-host: 目标服务器地址
  • --build-host: 指定远程构建主机;不指定时通常在本地构建。不要把 localhost 特殊值当作跨版本保证。
  • --use-remote-sudo:历史 rebuild 对非 root 目标用户启用远端 sudo。是否可交互输密码取决于工具版本与连接方式。

使用 Flakes 部署#

flake.nix

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
  
  outputs = { self, nixpkgs }: {
    nixosConfigurations.remote-server = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [ ./configuration.nix ];
    };
  };
}

部署命令

# 构建并部署
nixos-rebuild switch \
  --flake .#remote-server \
  --target-host deploy@remote-server --use-remote-sudo

批量部署多台服务器#

以下脚本实际逐台激活服务器。每个 nixosConfigurations key 必须与数组中的名称完全对应;应先对单台节点完成 build/test 和 SSH 验证,再批准整个列表。失败会停止后续部署,已成功节点不会自动整体回滚。创建 deploy.sh:

#!/usr/bin/env bash

SERVERS=(
  "server1.example.com"
  "server2.example.com"
  "server3.example.com"
)

for server in "${SERVERS[@]}"; do
  echo "Deploying to $server..."
  nixos-rebuild switch \
    --flake ".#$server" \
    --target-host "deploy@$server" --use-remote-sudo || exit 1
  echo "✓ $server deployed"
done

echo "All servers deployed!"

使用 deploy-rs 工具#

deploy-rs 是社区维护的部署工具,支持原子部署和回滚。

安装

# 在 shell.nix 或配置中添加
environment.systemPackages = with pkgs; [
  deploy-rs
];

配置 deploy.nix

下面的 attrset 应作为 flake 的 deploy 输出,由 self 和 deploy-rs inputs 提供作用域;仅保存成孤立文件并不会自动接入工具。建议同时暴露 deploy-rs.lib.<system>.deployChecks self.deploy 到 checks。原子 profile 切换、失败回滚及 magic rollback 都受配置和 SSH 恢复条件约束,不能代替数据备份。

{
  nodes = {
    web-server = {
      hostname = "web.example.com";
      profiles.system = {
        user = "root";
        sshUser = "deploy";
        path = deploy-rs.lib.x86_64-linux.activate.nixos 
          self.nixosConfigurations.web-server;
      };
    };
    
    db-server = {
      hostname = "db.example.com";
      profiles.system = {
        user = "root";
        sshUser = "deploy";
        path = deploy-rs.lib.x86_64-linux.activate.nixos 
          self.nixosConfigurations.db-server;
      };
    };
  };
}

执行部署

# 部署所有节点
deploy

# 部署特定节点
deploy .#web-server

# 显示待激活变化:可能构建、上传并在远端 dry-activate,仍会使用远程连接
deploy --dry-activate

# 只做本地 flake 检查与系统构建,不触碰目标
nix flake check
nix build .#nixosConfigurations.web-server.config.system.build.toplevel

密钥管理#

不要把明文密钥放入 Nix 字符串、store、镜像或 Terraform state。agenix 的密文可以随配置管理,私钥留在目标的受控路径;应用在运行时读取解密文件。

使用 age 加密密钥

services.myapp 是示意接口,必须由真实应用模块定义;owner/group 也须预先存在。agenix input 通过 specialArgs 等方式传入模块。

{ config, pkgs, agenix, ... }:

{
  imports = [ agenix.nixosModules.default ];
  
  age.secrets.api-key = {
    file = ./secrets/api-key.age;
    owner = "myapp";
    group = "myapp";
  };
  
  services.myapp = {
    enable = true;
    apiKeyFile = config.age.secrets.api-key.path;
  };
}

部署检查与回滚#

# 先测试配置
nixos-rebuild test \
  --flake .#remote-server \
  --target-host deploy@remote-server --use-remote-sudo

# 如果出问题,远程回滚
ssh deploy@remote-server 'sudo nixos-rebuild switch --rollback'

# 查看远程系统版本
ssh deploy@remote-server 'sudo nix-env -p /nix/var/nix/profiles/system --list-generations'

持续部署 CI/CD#

GitHub Actions 示例

这是实际部署 workflow,push 会触发远端变更。生产环境应配置受保护 deployment environment/审批与最小权限,第三方 actions 固定到核验过的 commit SHA。安装 Nix 不保证 runner 已有 nixos-rebuild,应通过锁定的工具环境提供对应版本。known_hosts 从独立可信渠道核验后预存;临时 ssh-keyscan 只能采集候选公钥,不能建立服务器身份信任。

name: Deploy NixOS

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Nix
        uses: DeterminateSystems/nix-installer-action@main
        
      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > ~/.ssh/known_hosts
          chmod 600 ~/.ssh/known_hosts
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
          DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
          
      - name: Deploy
        run: |
          nixos-rebuild switch \
            --flake .#remote-server \
            --target-host deploy@remote-server --use-remote-sudo

参考与延伸

  1. nix.dev SSH 配置部署 ↗
  2. deploy-rs 官方仓库 ↗
  3. deploy-rs CLI 参数源码 ↗
  4. agenix 官方仓库 ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

NixOS 虚拟化与部署

  1. 01用 QEMU 验证 NixOS 配置
  2. 02构建可启动的 NixOS ISO
  3. 03用 dockerTools 构建与运行容器镜像
  4. 04通过 SSH 部署 NixOS
  5. 05Terraform 与 NixOS 的部署分工
  6. 06设计分层的桌面与开发环境
CONTINUE READING / 下一篇Terraform 与 NixOS 的部署分工

由 Terraform 管理云资源,再用受控流程部署 NixOS 系统配置。