Nix最佳实践指南 nix-best-practices

本指南详细介绍了Nix包管理器和Flakes的最佳实践模式,包括Flake结构设计、Overlay应用、非自由软件处理、二进制包管理、开发环境配置和故障排除。适用于DevOps工程师、系统管理员和开发人员构建可重复、声明式的开发和生产环境。关键词:Nix, Flakes, Overlay, 包管理, DevOps, 可重复构建, 开发环境, NixOS

DevOps 0 次安装 0 次浏览 更新于 2/23/2026

name: nix最佳实践 description: 适用于flakes、overlays、非自由软件处理和二进制overlay的Nix模式。在编写flake.nix或shell.nix时使用。

Nix最佳实践

Flake结构

标准flake.nix结构:

{
  description = "项目描述";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };
      in {
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            # 此处添加包
          ];
        };
      });
}

Follows模式(避免重复的Nixpkgs)

添加overlay输入时,使用follows共享父级nixpkgs,避免下载多个版本:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  # Overlay跟随父级nixpkgs
  some-overlay.url = "github:owner/some-overlay";
  some-overlay.inputs.nixpkgs.follows = "nixpkgs";

  # 通过中间输入链式跟随
  another-overlay.url = "github:owner/another-overlay";
  another-overlay.inputs.nixpkgs.follows = "some-overlay";
};

所有输入必须在输出函数中列出,即使未直接使用:

outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:

应用Overlays

Overlays用于修改或向nixpkgs添加包:

let
  pkgs = import nixpkgs {
    inherit system;
    overlays = [
      overlay1.overlays.default
      overlay2.overlays.default
      # 内联overlay
      (final: prev: {
        myPackage = prev.myPackage.override { ... };
      })
    ];
  };
in

处理非自由软件包

选项1:nixpkgs-unfree(团队推荐)

使用numtide/nixpkgs-unfree处理EULA许可的软件包,无需用户配置:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable";
  nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs";

  # 非自由overlay跟随nixpkgs-unfree
  proprietary-tool.url = "github:owner/proprietary-tool-overlay";
  proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree";
};

链式关系:proprietary-toolnixpkgs-unfreenixpkgs

选项2:用户配置

用户在~/.config/nixpkgs/config.nix中添加:

{ allowUnfree = true; }

选项3:特定软件包(Flake)

let
  pkgs = import nixpkgs {
    inherit system;
    config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
      "specific-package"
    ];
  };
in

注意:flake.nix中的config.allowUnfree不适用于nix develop - 请使用nixpkgs-unfree或用户配置。

创建二进制Overlay仓库

当nixpkgs构建的社区版本缺少功能时(常见于开源核心工具),创建一个获取官方二进制文件的overlay。

模式(参考0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay)

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};

        version = "1.0.0";

        # 平台特定二进制文件
        sources = {
          "x86_64-linux" = {
            url = "https://example.com/tool-linux-amd64-v${version}";
            sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
          };
          "aarch64-linux" = {
            url = "https://example.com/tool-linux-arm64-v${version}";
            sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
          };
          "x86_64-darwin" = {
            url = "https://example.com/tool-darwin-amd64-v${version}";
            sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=";
          };
          "aarch64-darwin" = {
            url = "https://example.com/tool-darwin-arm64-v${version}";
            sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=";
          };
        };

        source = sources.${system} or (throw "不支持的系统: ${system}");

        toolPackage = pkgs.stdenv.mkDerivation {
          pname = "tool";
          inherit version;

          src = pkgs.fetchurl {
            inherit (source) url sha256;
          };

          sourceRoot = ".";
          dontUnpack = true;

          installPhase = ''
            mkdir -p $out/bin
            cp $src $out/bin/tool
            chmod +x $out/bin/tool
          '';

          meta = with pkgs.lib; {
            description = "工具描述";
            homepage = "https://example.com";
            license = licenses.unfree;  # 或适当的许可证
            platforms = builtins.attrNames sources;
          };
        };
      in {
        packages.default = toolPackage;
        packages.tool = toolPackage;

        overlays.default = final: prev: {
          tool = toolPackage;
        };
      })
    // {
      overlays.default = final: prev: {
        tool = self.packages.${prev.system}.tool;
      };
    };
}

获取SHA256哈希值

nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0
# 返回base32格式的哈希,转换为SRI格式:
nix hash to-sri --type sha256 <base32-hash>

或直接使用SRI:

nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0

开发Shell模式

基础Shell

devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs
    python3
  ];

  shellHook = ''
    echo "开发环境就绪"
  '';
};

带环境变量

devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [ postgresql ];

  # 在shell入口设置
  DATABASE_URL = "postgres://localhost/dev";

  # 或在shellHook中设置动态值
  shellHook = ''
    export PROJECT_ROOT="$(pwd)"
  '';
};

原生依赖(C库)

devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [
    openssl
    postgresql
  ];

  # 暴露头文件和库
  shellHook = ''
    export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH"
    export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH"
    export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
  '';
};

Direnv集成

flake项目的.envrc

use flake

对于没有nixpkgs-unfree的非自由软件包:

export NIXPKGS_ALLOW_UNFREE=1
use flake --impure

常用命令

# 更新所有输入
nix flake update

# 更新特定输入
nix flake update some-input

# 检查flake有效性
nix flake check

# 显示flake元数据
nix flake metadata

# 进入开发shell
nix develop

# 在开发shell中运行命令
nix develop -c <command>

# 构建包
nix build .#packageName

# 运行包
nix run .#packageName

故障排除

"unexpected argument"错误

所有输入必须在输出函数中列出:

# 错误
outputs = { self, nixpkgs }: ...

# 正确(如果有更多输入)
outputs = { self, nixpkgs, other-input, ... }: ...

使用nix develop时的非自由软件包错误

flake.nix中的config.allowUnfree不会传播到nix develop。使用:

  1. nixpkgs-unfree输入(推荐)
  2. 用户的~/.config/nixpkgs/config.nix
  3. NIXPKGS_ALLOW_UNFREE=1 nix develop --impure

重复的Nixpkgs下载

使用follows将输入链式连接到单个nixpkgs源。

Overlay未应用

导入nixpkgs时确保overlay在overlays列表中:

pkgs = import nixpkgs {
  inherit system;
  overlays = [ my-overlay.overlays.default ];
};

哈希不匹配

使用nix-prefetch-url重新获取并更新哈希。当上游在同一URL更新二进制文件时,哈希会改变。