nix-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Nix Best Practices

Nix 最佳实践

Flake Structure

Flake 结构

Standard flake.nix structure:
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
          ];
        };
      });
}
标准的flake.nix结构:
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 Pattern (Avoid Duplicate Nixpkgs)

遵循依赖跟随模式(避免重复下载Nixpkgs)

When adding overlay inputs, use
follows
to share the parent nixpkgs and avoid downloading multiple versions:
nix
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";
};
All inputs must be listed in outputs function even if not directly used:
nix
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:
添加overlay输入时,使用
follows
来共享父级nixpkgs,避免下载多个版本:
nix
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函数中列出,即使没有直接使用:
nix
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:

Applying Overlays

应用Overlays

Overlays modify or add packages to nixpkgs:
nix
let
  pkgs = import nixpkgs {
    inherit system;
    overlays = [
      overlay1.overlays.default
      overlay2.overlays.default
      # Inline overlay
      (final: prev: {
        myPackage = prev.myPackage.override { ... };
      })
    ];
  };
in
Overlays用于修改或向nixpkgs中添加软件包:
nix
let
  pkgs = import nixpkgs {
    inherit system;
    overlays = [
      overlay1.overlays.default
      overlay2.overlays.default
      # Inline overlay
      (final: prev: {
        myPackage = prev.myPackage.override { ... };
      })
    ];
  };
in

Handling Unfree Packages

处理非自由软件

Option 1: nixpkgs-unfree (Recommended for Teams)

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

Use numtide/nixpkgs-unfree for EULA-licensed packages without requiring user config:
nix
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";
};
This chains:
proprietary-tool
nixpkgs-unfree
nixpkgs
使用numtide/nixpkgs-unfree来获取受EULA许可的软件包,无需用户配置:
nix
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

Option 2: User Config

选项2:用户配置

Users add to
~/.config/nixpkgs/config.nix
:
nix
{ allowUnfree = true; }
用户添加配置到
~/.config/nixpkgs/config.nix
nix
{ allowUnfree = true; }

Option 3: Specific Packages (Flake)

选项3:指定软件包(Flake中)

nix
let
  pkgs = import nixpkgs {
    inherit system;
    config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
      "specific-package"
    ];
  };
in
Note:
config.allowUnfree
in flake.nix doesn't work with
nix develop
- use nixpkgs-unfree or user config.
nix
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或用户配置。

Creating Binary Overlay Repos

创建二进制Overlay仓库

When nixpkgs builds a community version lacking features (common with open-core tools), create an overlay that fetches official binaries.
当nixpkgs构建的社区版本缺少功能时(开源核心工具中常见),可以创建一个获取官方二进制文件的overlay。

Pattern (see 0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay)

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

nix
{
  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;
      };
    };
}
nix
{
  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;
      };
    };
}

Getting SHA256 Hashes

获取SHA256哈希值

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

Returns hash in base32, convert to SRI format:

Returns hash in base32, convert to SRI format:

nix hash to-sri --type sha256 <base32-hash>

Or use SRI directly:
```bash
nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0
nix hash to-sri --type sha256 <base32-hash>

或者直接使用SRI格式:
```bash
nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0

Dev Shell Patterns

Dev Shell 模式

Basic Shell

基础Shell

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

  shellHook = ''
    echo "Dev environment ready"
  '';
};
nix
devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs
    python3
  ];

  shellHook = ''
    echo "Dev environment ready"
  '';
};

With Environment Variables

带环境变量的Shell

nix
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)"
  '';
};
nix
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)"
  '';
};

Native Dependencies (C Libraries)

原生依赖(C库)

nix
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"
  '';
};
nix
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 Integration

Direnv 集成

.envrc
for flake projects:
bash
use flake
For unfree packages without nixpkgs-unfree:
bash
export NIXPKGS_ALLOW_UNFREE=1
use flake --impure
适用于Flake项目的
.envrc
bash
use flake
对于未使用nixpkgs-unfree的非自由软件:
bash
export NIXPKGS_ALLOW_UNFREE=1
use flake --impure

Common Commands

常用命令

bash
undefined
bash
undefined

Update all inputs

Update all inputs

nix flake update
nix flake update

Update specific input

Update specific input

nix flake update some-input
nix flake update some-input

Check flake validity

Check flake validity

nix flake check
nix flake check

Show flake metadata

Show flake metadata

nix flake metadata
nix flake metadata

Enter dev shell

Enter dev shell

nix develop
nix develop

Run command in dev shell

Run command in dev shell

nix develop -c <command>
nix develop -c <command>

Build package

Build package

nix build .#packageName
nix build .#packageName

Run package

Run package

nix run .#packageName
undefined
nix run .#packageName
undefined

Troubleshooting

故障排除

"unexpected argument" Error

"unexpected argument" 错误

All inputs must be listed in outputs function:
nix
undefined
所有输入必须在outputs函数中列出:
nix
undefined

Wrong

Wrong

outputs = { self, nixpkgs }: ...
outputs = { self, nixpkgs }: ...

Right (if you have more inputs)

Right (if you have more inputs)

outputs = { self, nixpkgs, other-input, ... }: ...
undefined
outputs = { self, nixpkgs, other-input, ... }: ...
undefined

Unfree Package Errors with nix develop

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

config.allowUnfree
in flake.nix doesn't propagate to
nix develop
. Use:
  1. nixpkgs-unfree input (recommended)
  2. User's
    ~/.config/nixpkgs/config.nix
  3. NIXPKGS_ALLOW_UNFREE=1 nix develop --impure
flake.nix中的
config.allowUnfree
不会传递到
nix develop
中。请使用:
  1. nixpkgs-unfree输入(推荐方案)
  2. 用户的
    ~/.config/nixpkgs/config.nix
    配置
  3. NIXPKGS_ALLOW_UNFREE=1 nix develop --impure

Duplicate Nixpkgs Downloads

重复下载Nixpkgs

Use
follows
to chain inputs to a single nixpkgs source.
使用
follows
将输入链接到单个nixpkgs源。

Overlay Not Applied

Overlay未生效

Ensure overlay is in the
overlays
list when importing nixpkgs:
nix
pkgs = import nixpkgs {
  inherit system;
  overlays = [ my-overlay.overlays.default ];
};
确保在导入nixpkgs时将overlay添加到
overlays
列表中:
nix
pkgs = import nixpkgs {
  inherit system;
  overlays = [ my-overlay.overlays.default ];
};

Hash Mismatch

哈希不匹配

Re-fetch with
nix-prefetch-url
and update the hash. Hashes change when upstream updates binaries at the same URL.
使用
nix-prefetch-url
重新获取并更新哈希值。当上游在同一URL更新二进制文件时,哈希值会发生变化。