关于 nix-best-practices
Nix Best Practices 是关于配置 Nix flake、overlay、开发环境和非自由软件包的参考技能,面向使用 Nix 项目的开发者,提供命令示例与故障排查指引。
主要功能
- 01组织 flake 并声明输入
- 02使用 follows 共享 nixpkgs 输入
- 03应用软件包 overlay
- 04配置非自由软件包
- 05设置开发环境与 direnv
适用场景
- 01为项目搭建 Nix flake
- 02避免重复的 nixpkgs 输入
- 03在开发环境中提供原生依赖
- 04排查常见 flake 和软件包错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ alleneubank/ claude-code --skill nix-best-practices安装后在 Claude Code 中输入/nix-best-practices确认已加载。
详细介绍
Nix 最佳实践
Flake 结构
标准的 flake.nix 结构:
{
description = "Project 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; [
# packages here
];
};
});
}
Follows 模式(避免重复下载 Nixpkgs)
添加 overlay 输入时,使用 follows 共享父级 nixpkgs,避免下载多个版本:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# Overlay follows parent nixpkgs
some-overlay.url = "github:owner/some-overlay";
some-overlay.inputs.nixpkgs.follows = "nixpkgs";
# Chain follows through intermediate inputs
another-overlay.url = "github:owner/another-overlay";
another-overlay.inputs.nixpkgs.follows = "some-overlay";
};
即使没有直接使用,也必须在 outputs 函数中列出所有输入:
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:
应用 Overlays
Overlays 用于修改或向 nixpkgs 添加软件包:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
overlay1.overlays.default
overlay2.overlays.default
# Inline 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";
# Unfree overlay follows nixpkgs-unfree
proprietary-tool.url = "github:owner/proprietary-tool-overlay";
proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree";
};
此链路为:proprietary-tool → nixpkgs-unfree → nixpkgs
选项 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";
# Platform-specific binaries
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 "Unsupported system: ${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 = "Tool description";
homepage = "https://example.com";
license = licenses.unfree; # or appropriate license
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
# Returns hash in base32, convert to SRI format:
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 "Dev environment ready"
'';
};
使用环境变量
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ postgresql ];
# Set at shell entry
DATABASE_URL = "postgres://localhost/dev";
# Or in shellHook for dynamic values
shellHook = ''
export PROJECT_ROOT="$(pwd)"
'';
};
原生依赖项(C 库)
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
openssl
postgresql
];
# Expose headers and libraries
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
常用命令
# Update all inputs
nix flake update
# Update specific input
nix flake update some-input
# Check flake validity
nix flake check
# Show flake metadata
nix flake metadata
# Enter dev shell
nix develop
# Run command in dev shell
nix develop -c <command>
# Build package
nix build .#packageName
# Run package
nix run .#packageName
故障排除
“unexpected argument” 错误
必须在 outputs 函数中列出所有输入:
# Wrong
outputs = { self, nixpkgs }: ...
# Right (if you have more inputs)
outputs = { self, nixpkgs, other-input, ... }: ...
使用 nix develop 时出现非自由软件包错误
flake.nix 中的 config.allowUnfree 不会传递到 nix develop。请使用:
- nixpkgs-unfree 输入(推荐)
- 用户的
~/.config/nixpkgs/config.nix 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 更新二进制文件时,哈希会发生变化。