Merge remote-tracking branch 'upstream/main'

This commit is contained in:
kev 2024-04-07 21:38:47 +08:00
commit 0483b59723
No known key found for this signature in database
GPG Key ID: B94F394B72884017
23 changed files with 1979 additions and 1578 deletions

View File

@ -19,7 +19,7 @@ But please respect the following restrictions:
## License
By contributing, you agree that your contributions will be licensed under its MIT License.
By contributing, you agree that your contributions will be licensed under [CC BY-SA 4.0](../LICENSE.md).
## Pull requests

View File

@ -75,6 +75,7 @@ export default defineConfig({
config: (md) => {
// add support for footnote
md.use(require("markdown-it-footnote"))
md.use(require("markdown-it-cjk-breaks"))
},
},
@ -88,6 +89,22 @@ export default defineConfig({
search: {
provider: "local",
// for debugging
// options: {
// /**
// * @param {string} src
// * @param {import('vitepress').MarkdownEnv} env
// * @param {import('markdown-it')} md
// */
// _render(src, env, md) {
// console.log("start...")
// console.log("src", src)
// let out = md.render(src, env)
// console.log("success...")
// return out
// },
// },
// provider: 'algolia',
// options: {
// appId: '747LJ10EI7',
@ -169,6 +186,14 @@ function themeConfigEnglish() {
text: "NixOS with Flakes Enabled",
link: "/nixos-with-flakes/nixos-with-flakes-enabled.md",
},
{
text: "NixOS's flake.nix Explained",
link: "/nixos-with-flakes/nixos-flake-configuration-explained.md",
},
{
text: "The combination ability of Flakes and Nixpkgs module system",
link: "/nixos-with-flakes/nixos-flake-and-module-system.md",
},
{
text: "Adding Custom Cache Servers",
link: "/nixos-with-flakes/add-custom-cache-servers.md",
@ -236,6 +261,10 @@ function themeConfigEnglish() {
text: "Debugging Derivations and Nix Expressions",
link: "/best-practices/debugging.md",
},
{
text: "Host Custom Binary Cache with S3",
link: "/best-practices/host-custom-binary-cache-with-s3.md",
},
],
},
@ -363,6 +392,14 @@ function themeConfigChinese() {
text: "使用 Flakes 管理 NixOS",
link: "/zh/nixos-with-flakes/nixos-with-flakes-enabled.md",
},
{
text: "NixOS 的 flake.nix 内容详解",
link: "/zh/nixos-with-flakes/nixos-flake-configuration-explained.md",
},
{
text: "Flakes 的组合能力与 Nixpkgs 模块系统",
link: "/zh/nixos-with-flakes/nixos-flake-and-module-system.md",
},
{
text: "添加自定义缓存服务器",
link: "/zh/nixos-with-flakes/add-custom-cache-servers.md",
@ -397,7 +434,7 @@ function themeConfigChinese() {
{ text: "Overriding", link: "/zh/nixpkgs/overriding.md" },
{ text: "Overlays", link: "/zh/nixpkgs/overlays.md" },
{
text: "多 Nixpkgs 实例",
text: "多 Nixpkgs 实例的妙用",
link: "/zh/nixpkgs/multiple-nixpkgs.md",
},
],
@ -430,6 +467,10 @@ function themeConfigChinese() {
text: "调试 Nix 软件包与 Nix 表达式",
link: "/zh/best-practices/debugging.md",
},
{
text: "使用 S3 托管自定义二进制缓存",
link: "/zh/best-practices/host-custom-binary-cache-with-s3.md",
},
],
},
{

View File

@ -0,0 +1,267 @@
# Host Custom Binary Cache with S3 {#host-custom-binary-cache-with-s3}
## TL;DR
A guide on how to set up your own S3 nix binary cache using MinIO S3 server.
## How Software Stored in Nix? {#how-software-stored-in-nix}
Multiple versions of the same software package can be installed on a system, making it
possible to satisfy various dependency chains at a time. This enables the installation of
multiple packages that depend on the same third package, but on different versions of it.
To achieve this, all packages are installed in the global nix store under `/nix/store/`
and are then symlinked to the respective locations. To verify a package's uniqueness, its
whole directory is hashed, and the hash put into the name of the package's main folder.
Every software built from the same Nix expression that uses the same dependency software
versions results in the same hash, no matter what system it was built on. If any of the
dependency software versions were changed, this will result in a new hash for the final
package.
Using symlinks to install a package and link all the right dependencies to it also enables
atomic updating. To make this clearer, let's think of an example where software X is
installed in an older version and should be updated. Software X is installed in its very
own directory in the global nix store and symlinked to the right directory, let's say
`/usr/local/bin/`. When the update is triggered, the new version of X is installed into
the global nix store without interfering with its older version. Once the installation
with all its dependencies in the nix store is completed, the final step is to change the
symlink to `/usr/local/bin/`. Since creating a new symlink that overwrites the old one is
an atomic operation in Unix, it is impossible for this operation to fail and leave the
package in a corrupted state. The only possible problem would be that it fails before or
after the symlink creation. Either way, the result would be that we either have the old
version of X or the newly installed version, but nothing in between.
Quoted from the original work of
https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b
## Nix Binary Caches {#nix-binary-caches}
No matter how great every aspect of Nix sounds, its design has also a major drawback,
which is that every package build triggers the build process for the whole dependency
chain from scratch. This can take quite a while, since even compilers such as gcc or ghc
must be built in advance. Such build processes can eat up a remarkable amount of memory,
introducing an additional hurdle if one wants to use it on restricted platforms such as
the Raspberry Pi.
To overcome the drawback of always building everything from scratch and the chance of
losing access to prebuilt versions of packages, it is also possible to build your Nix
binary cache using an S3 server such as MinIO (https://min.io/).
The process of setting up your cache, populating it with binaries and use the binaries
from your newly built cache will be described step by step. For this manual, I assume that
you already have nix in place, and that you already have a running MinIO server somewhere
in your environment. If not, you may check out the
[official deployment guide](https://min.io/docs/minio/linux/operations/installation.html)
from MinIO. You'll need to ensure that MinIO is accessible via `HTTPS` using a trusted
certificate. Let's Encrypt will be helpful here.
In this post, let's explore how we can self-host an S3-compatible server, MinIO, as a
binary cache store.
Quoted from the original work of
https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b
## How To Use S3 as a Binary Cache Server {#how-to-use-s3-as-a-binary-cache-server}
### Prerequisites {#prerequisites}
- Set up MinIO somewhere in your environment.
- Hold a valid SSL certificates either public or private. For demonstration purpose, we
will use `minio.homelab.local` (private certificate) in the steps mentioned in this
tutorial. If you plan to use a private certificate, you MUST resolve the DNS challenges
on your own. Hence, it is recommended to use a public certificate.
- Install `minio-client` in your environment.
### Generate Password {#generate-password}
```bash
nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1
# oenu1Yuch3rohz2ahveid0koo4giecho
```
### Set Up MinIO Client {#set-up-minio-client}
Install the MinIO command-line client `mc`.
```nix
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
minio-client # A replacement for ls, cp, mkdir, diff, and rsync commands for filesystems and object storage
];
}
```
Create or edit `~/.mc/config.json`.
```json
{
"version": "10",
"aliases": {
"s3": {
"url": "https://s3.homelab.local",
"accessKey": "minio",
"secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho",
"api": "s3v4",
"path": "auto"
}
}
}
```
On the machine we use to build the nix packages, we need the S3 credentials in places. Set
up a file `~/.aws/credentials` and populate it with the credentials of our nixbuilder
user. Replace `<nixbuildersecret>` with the password generated by the `pwgen` command.
```bash
[default]
aws_access_key_id=nixbuilder
aws_secret_access_key=<nixbuildersecret>
```
### Setup S3 Bucket as Binary Cache {#setup-s3-bucket-as-binary-cache}
Create the `nix-cache` bucket.
```bash
mc mb s3/nix-cache
```
Create the `nixbuilder` MinIO user and assign a password.
```bash
mc admin user add s3 nixbuilder <PASSWORD>
```
Create a file called `nix-cache-write.json` in your current working directory with the
following contents:
```json
{
"Id": "AuthenticatedWrite",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AuthenticatedWrite",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"],
"Principal": "nixbuilder"
}
]
}
```
Create a policy with `nix-cache-write.json` that allows `nixbuilder` to upload files to
the cache.
```bash
mc admin policy add s3 nix-cache-write nix-cache-write.json
```
Associate the policy that we created above with the `nixbuilder` user.
```bash
mc admin policy set s3 nix-cache-write user=nixbuilder
```
Allow anonymous users to download files without authenticating.
```bash
mc anonymous set download s3/nix-cache
```
Create a file called `nix-cache-info` in your working directory. This file tells Nix that
the bucket is indeed a binary cache.
```bash
cat > nix-cache-info <<EOF
StoreDir: /nix/store
WantMassQuery: 1
Priority: 40
EOF
```
Copy `nix-cache-info` to the cache bucket.
```bash
mc cp ./nix-cache-info s3/nix-cache/nix-cache-info
```
### Generate Key Pairs {#generate-key-pairs}
Generate a secret and public key for signing store paths. The key name is arbitrary, but
the NixOS developers highly recommend using the domain name of the cache followed by an
integer. If the key ever needs to be revoked or regenerated, the trailing integer can be
incremented.
```bash
nix key generate-secret --key-name s3.homelab.local-1 > ~/.config/nix/secret.key
nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key
cat ~/.config/nix/public.key
# s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=
```
### Activate Binary Cache with Flake {#activate-binary-cache-with-flake}
Put the following lines in `configuration.nix` or any of your custom NixOS module:
```nix
{
nix = {
settings = {
# Substituters will be appended to the default substituters when fetching packages.
extra-substituters = [
"https://s3.homelab.local/nix-cache/"
];
extra-trusted-public-keys = [
"s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs="
];
};
};
}
```
Rebuild the system.
```bash
sudo nixos-rebuild switch --upgrade --flake .#<HOST>
```
### Push Paths to the Store {#push-paths-to-the-store}
Sign some paths in the local store.
```bash
nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system
```
Copy those paths to the cache.
```bash
nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system
```
### Add Automatic Object Expiration Policy {#add-automatic-object-expiration-policy}
```bash
mc ilm rule add s3/nix-cache --expire-days "DAYS"
# Example: mc ilm rule add s3/nix-cache --expire-days "7"
```
### References {#references}
Here are some of the sources that I used in making this document:
- [Blog post by Jeff on Nix binary caches](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html)
- [Binary cache in the NixOS wiki](https://nixos.wiki/wiki/Binary_Cache)
- [Serving a Nox store via S3 in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html)
- [Serving a Nix store via HTTP in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html)

View File

@ -252,7 +252,7 @@ So how to use this method in Flakes? The example `flake.nix` is as follows:
system = "x86_64-linux";
modules = [
{
nigpkgs.crossSystem = {
nixpkgs.crossSystem = {
config = "riscv64-unknown-linux-gnu";
};
@ -304,7 +304,7 @@ follows:
};
modules = [
{
nigpkgs.crossSystem = {
nixpkgs.crossSystem = {
config = "riscv64-unknown-linux-gnu";
};
}

View File

@ -260,7 +260,7 @@ For example, we can directly install this wrapper through NixOS's
`environment.systemPackages`, and then execute it directly:
```nix
{pkgs, lib, ...}{
{pkgs, lib, ...}:{
environment.systemPackages = [
# Install the wrapper into the system
@ -279,7 +279,7 @@ For example, we can directly install this wrapper through NixOS's
mkdir -p $out/bin/
ln -s ${pkgs.nushell}/bin/nu $out/bin/dev-shell
wrapProgram $out/bin/dev-shell --prefix PATH : ${pkgs.lib.makeBinPath packages}
'';)
'')
];
}
```

View File

@ -87,4 +87,4 @@ main configuration is completely shared, and the configuration modified on any h
seamlessly synchronized to other hosts through Git.
Nix almost completely shielded me from the differences between OS and architecture at the
bottom of the three machines, and the experience was very slippery!
bottom of the three machines, and the experience was very smooth!

View File

@ -10,22 +10,12 @@ local build process and significantly improving build speed.
## Why Add Custom Cache Servers {#why-add-custom-cache-servers}
> Note: The methods introduced here can only accelerate the download of packages; many
> `inputs` data sources will still be fetched from GitHub. Also, if the cache is not
> found, local builds will be executed, which typically requires downloading source code
> and building dependencies from GitHub or somewhere else, which may make it slow. To
> completely address the speed issue, it is still recommended to use solutions such as a
> transparent proxy running on your router or local machine.
Two reasons:
1. Add cache servers for some third-party projects, such as the nix-community cache server
[https://nix-community.cachix.org](https://nix-community.cachix.org), which can
significantly improve the build speed of these third-party projects.
2. Adding a mirrored cache server to accelerate downloads.
1. The access speed of the official cache server in China is slow. Without a local
global proxy, it is almost unusable. Adding Chinese Nix cache mirrors like
ustc/sjtu/tuna can alleviate this issue.
1. Add cache server mirror sites closest to the user to speed up downloads.
## How to Add Custom Cache Servers {#how-to-add-custom-cache-servers}
@ -296,35 +286,12 @@ In other words, you can use it like this:
> machine can completely solve the issue of slow package downloads in NixOS, the
> configuration is rather cumbersome and often requires additional hardware.
More users may prefer to directly speed up package downloads by using a HTTP/Socks5 proxy
Some users may prefer to directly speed up package downloads by using a HTTP/Socks5 proxy
running on their machine. Here's how to set it up. Using methods like
`export HTTPS_PROXY=http://127.0.0.1:7890` in the Terminal will not work because the
actual work is done by a background process called `nix-daemon`, not by commands directly
executed in the Terminal.
The implementation code of `nix-daemon` is located at
[nixpkgs/nixos/modules/services/system/nix-daemon.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/modules/services/system/nix-daemon.nix#L184-L191),
which sets environment variables through the `systemd.services.nix-daemon.environment`
option. We can also add proxy-related environment variables to the running environment of
`nix-daemon` in the same way, as shown in the following example Module:
```nix
{
systemd.services.nix-daemon.environment = {
# socks5h means that the hostname is resolved by the SOCKS server
https_proxy = "socks5h://localhost:7891";
# https_proxy = "http://localhost:7890"; # or use http protocol instead of socks5
};
}
```
After deploying this configuration, you can check if the environment variables have been
set by running `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'`.
**However, be aware that when the proxy server is not available, nix-daemon will be unable
to access any cache servers!** Therefore, I still recommend using a transparent proxy to
address acceleration issues.
If you only need to use a proxy temporarily, you can set the proxy environment variables
with the following commands:
@ -338,10 +305,24 @@ sudo systemctl daemon-reload
sudo systemctl restart nix-daemon
```
After deploying this configuration, you can check if the environment variables have been
set by running `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'`.
The settings in `/run/systemd/system/nix-daemon.service.d/override.conf` will be
automatically deleted when the system restarts, or you can manually delete it and restart
the nix-daemon service to restore the original settings.
If you want to permanently set the proxy, it is recommended to save the above commands as
a shell script and run it each time the system starts. Alternatively, you can use a
transparent proxy or TUN and other global proxy solutions.
> There are also people in the community who permanently set the proxy for nix-daemon in a
> declarative way using `systemd.services.nix-daemon.environment`. However, if the proxy
> encounters problems, it will be very troublesome. Nix-daemon will not work properly, and
> most Nix commands will not run correctly. Moreover, the configuration of systemd itself
> is set to read-only protection, making it difficult to modify or delete the proxy
> settings. So, it is not recommended to use this method.
> When using some commercial or public proxies, you might encounter HTTP 403 errors when
> downloading from GitHub (as described in
> [nixos-and-flakes-book/issues/74](https://github.com/ryan4yin/nixos-and-flakes-book/issues/74)).

View File

@ -44,16 +44,6 @@ Overall, I strongly recommend everyone to use Flakes, especially since this book
around NixOS and Flakes. However, it's crucial to be prepared for potential problems that
may arise due to forthcoming breaking changes.
## Flakes Tutorials
I won't go into too much detail here, please refer to the following documents:
- [Flakes - nix.dev](https://nix.dev/concepts/flakes)
- [Nix Flakes, Part 1: An introduction and tutorial (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-05-25-flakes/)
- [Nix Flakes, Part 2: Evaluation caching (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-06-25-eval-cache/)
- [Nix Flakes, Part 3: Managing NixOS systems (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-07-31-nixos-flakes/)
- [Practical Nix Flakes](https://serokell.io/blog/practical-nix-flakes)
## When Will Flakes Be Stabilized?
I delved into some details regarding Flakes:

View File

@ -0,0 +1,293 @@
# The combination ability of Flakes and Nixpkgs module system
## Nixpkgs Module Structure Explained {#simple-introduction-to-nixpkgs-module-structure}
> The detailed workings of this module system will be introduced in the following
> [Modularizing NixOS Configuration](./modularize-the-configuration.md) section. Here,
> we'll just cover some basic knowledge.
You might be wondering why the `/etc/nixos/configuration.nix` configuration file adheres
to the Nixpkgs Module definition and can be referenced directly within the `flake.nix`.
This is because the Nixpkgs repository contains a significant amount of NixOS
implementation source code, primarily written in Nix. To manage and maintain such a large
volume of Nix code and to allow users to customize various functions of their NixOS
systems, a modular system for Nix code is essential.
This modular system for Nix code is also implemented within the Nixpkgs repository and is
primarily used for modularizing NixOS system configurations. However, it is also widely
used in other contexts, such as nix-darwin and home-manager. Since NixOS is built on this
modular system, it is only natural that its configuration files, including
`/etc/nixos/configuration.nix`, are Nixpkgs Modules.
Before delving into the subsequent content, it's essential to have a basic understanding
of how this module system operates.
Here's a simplified structure of a Nixpkgs Module:
```nix
{lib, config, options, pkgs, ...}:
{
# Importing other Modules
imports = [
# ...
./xxx.nix
];
for.bar.enable = true;
# Other option declarations
# ...
}
```
The definition is actually a Nix function, and it has five **automatically generated,
automatically injected, and declaration-free parameters** provided by the module system:
1. `lib`: A built-in function library included with nixpkgs, offering many practical
functions for operating Nix expressions.
- For more information, see <https://nixos.org/manual/nixpkgs/stable/#id-1.4>.
2. `config`: A set of all options' values in the current environment, which will be used
extensively in the subsequent section on the module system.
3. `options`: A set of all options defined in all Modules in the current environment.
4. `pkgs`: A collection containing all nixpkgs packages, along with several related
utility functions.
- At the beginner stage, you can consider its default value to be
`nixpkgs.legacyPackages."${system}"`, and the value of `pkgs` can be customized
through the `nixpkgs.pkgs` option.
5. `modulesPath`: A parameter available only in NixOS, which is a path pointing to
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/modules).
- It is defined in
[nixpkgs/nixos/lib/eval-config-minimal.nix#L43](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/lib/eval-config-minimal.nix#L43).
- It is typically used to import additional NixOS modules and can be found in most
NixOS auto-generated `hardware-configuration.nix` files.
## Passing Non-default Parameters to Submodules {#pass-non-default-parameters-to-submodules}
If you need to pass other non-default parameters to submodules, you will need to use some
special methods to manually specify these non-default parameters.
The Nixpkgs module system provides two ways to pass non-default parameters:
1. The `specialArgs` parameter of the `nixpkgs.lib.nixosSystem` function
2. Using the `_module.args` option in any module to pass parameters
The official documentation for these two parameters is buried deep and is vague and hard
to understand. If readers are interested, I will include the links here:
1. `specialArgs`: There are scattered mentions related to it in the NixOS Manual and the
Nixpkgs Manual.
- Nixpkgs Manual: [Module System - Nixpkgs]
- NixOS Manual:
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]
1. `_module.args`:
- NixOS Manual:
[Appendix A. Configuration Options](https://nixos.org/manual/nixos/stable/options#opt-_module.args)
- Source Code: [nixpkgs/nixos-23.11/lib/modules.nix - _module.args]
In short, `specialArgs` and `_module.args` both require an attribute set as their value,
and they serve the same purpose, passing all parameters in the attribute set to all
submodules. The difference between them is:
1. The `_module.args` option can be used in any module to pass parameters to each other,
which is more flexible than `specialArgs`, which can only be used in the
`nixpkgs.lib.nixosSystem` function.
1. `_module.args` is declared within a module, so it must be evaluated after all modules
have been evaluated before it can be used. This means that **if you use the parameters
passed through `_module.args` in `imports = [ ... ];`, it will result in an
`infinite recursion` error**. In this case, you must use `specialArgs` instead.
The NixOS community generally recommends prioritizing the use of the `_module.args` option
and resorting to `specialArgs` only when `_module.args` cannot be used.
Suppose you want to pass a certain dependency to a submodule for use. You can use the
`specialArgs` parameter to pass the `inputs` to all submodules:
```nix{13}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# Set all inputs parameters as special arguments for all submodules,
# so you can directly use all dependencies in inputs in submodules
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
];
};
};
}
```
Or you can achieve the same effect using the `_module.args` option:
```nix{14}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
# Set all inputs parameters as special arguments for all submodules,
# so you can directly use all dependencies in inputs in submodules
_module.args = { inherit inputs; };
}
];
};
};
}
```
Choose one of the two methods above to modify your configuration, and then you can use the
`inputs` parameter in `/etc/nixos/configuration.nix`. The module system will automatically
match the `inputs` defined in `specialArgs` and inject it into all submodules that require
this parameter:
```nix{3}
# Nix will match by name and automatically inject the inputs
# from specialArgs/_module.args into the third parameter of this function
{ config, pkgs, inputs, ... }:
{
# ...
}
```
The next section will demonstrate how to use `specialArgs`/`_module.args` to install
system software from other flake sources.
## Installing System Software from Other Flake Sources {#install-system-packages-from-other-flakes}
The most common requirement for managing a system is to install software, and we have
already seen in the previous section how to install packages from the official nixpkgs
repository using `environment.systemPackages`. These packages all come from the official
nixpkgs repository.
Now, we will learn how to install software packages from other flake sources, which is
much more flexible than installing directly from nixpkgs. The main use case is to install
the latest version of a software that is not yet added or updated in Nixpkgs.
Taking the Helix editor as an example, here's how to compile and install the master branch
of Helix directly.
First, add the helix input data source to `flake.nix`:
```nix{6,12,18}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
# helix editor, use the master branch
helix.url = "github:helix-editor/helix/master";
};
outputs = inputs@{ self, nixpkgs, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
# This module works the same as the `specialArgs` parameter we used above
# chose one of the two methods to use
# { _module.args = { inherit inputs; };}
];
};
};
}
```
Next, you can reference this flake input data source in `configuration.nix`:
```nix{1,10}
{ config, pkgs, inputs, ... }:
{
# ...
environment.systemPackages = with pkgs; [
git
vim
wget
curl
# Here, the helix package is installed from the helix input data source
inputs.helix.packages."${pkgs.system}".helix
];
# ...
}
```
Make the necessary changes and deploy with `sudo nixos-rebuild switch`. The deployment
will take much longer this time because Nix will compile the entire Helix program from
source.
After deployment, you can directly test and verify the installation using the `hx` command
in the terminal.
Additionally, if you just want to try out the latest version of Helix and decide whether
to install it on your system later, there is a simpler way to do it in one command (but as
mentioned earlier, compiling from source will take a long time):
```bash
nix run github:helix-editor/helix/master
```
We will go into more detail on the usage of `nix run` in the following section
[Usage of the New CLI](../other-usage-of-flakes/the-new-cli.md).
## Leveraging Features from Other Flakes Packages
In fact, this is the primary functionality of Flakes — a flake can depend on other flakes,
allowing it to utilize the features they provide. It's akin to how we incorporate
functionalities from other libraries when writing programs in TypeScript, Go, Rust, and
other programming languages.
The example above, using the latest version from the official Helix Flake, illustrates
this functionality. More use cases will be discussed later, and here are a few examples
referenced for future mention:
- [Getting Started with Home Manager](./start-using-home-manager.md): This introduces the
community's Home-Manager as a dependency, enabling direct utilization of the features
provided by this Flake.
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): Here, different
versions of Nixpkgs are introduced as dependencies, allowing for flexible selection of
packages from various versions of Nixpkgs.
## More Flakes Tutorials
Up to this point, we have learned how to use Flakes to configure NixOS systems.
If you have more questions about Flakes or want to learn more in-depth, please refer
directly to the following official/semi-official documents:
- Nix Flakes's official documentation:
- [Nix flakes - Nix Manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake)
- [Flakes - nix.dev](https://nix.dev/concepts/flakes)
- A series of tutorials by Eelco Dolstra(The creator of Nix) about Flakes:
- [Nix Flakes, Part 1: An introduction and tutorial (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-05-25-flakes/)
- [Nix Flakes, Part 2: Evaluation caching (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-06-25-eval-cache/)
- [Nix Flakes, Part 3: Managing NixOS systems (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-07-31-nixos-flakes/)
- Other useful documents:
- [Practical Nix Flakes](https://serokell.io/blog/practical-nix-flakes)
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix
[Module System - Nixpkgs]:
https://github.com/NixOS/nixpkgs/blob/23.11/doc/module-system/module-system.chapter.md
[nixpkgs/nixos-23.11/lib/modules.nix - _module.args]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/lib/modules.nix#L122-L184
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/doc/manual/development/option-types.section.md?plain=1#L237-L244

View File

@ -0,0 +1,186 @@
# `flake.nix` Configuration Explained {#flake-nix-configuration-explained}
Above, we created a `flake.nix` file to manage system configurations, but you might still
be unclear about its structure. Let's explain the content of this file in detail.
## 1. Flake Inputs
First, let's look at the `inputs` attribute. It is an attribute set that defines all the
dependencies of this flake. These dependencies will be passed as arguments to the
`outputs` function after they are fetched:
```nix{2-5,7}
{
inputs = {
# NixOS official package source, using the nixos-23.11 branch here
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# Omitting previous configurations......
};
}
```
Dependencies in `inputs` has many types and definitions. It can be another flake, a
regular Git repository, or a local path. The section
[Other Usage of Flakes - Flake Inputs](../other-usage-of-flakes/inputs.md) describes
common types of dependencies and their definitions in detail.
Here we only define a dependency named `nixpkgs`, which is the most common way to
reference in a flake, i.e., `github:owner/name/reference`. The `reference` here can be a
branch name, commit-id, or tag.
After `nixpkgs` is defined in `inputs`, you can use it in the parameters of the subsequent
`outputs` function, which is exactly what our example does.
## 2. Flake Outputs
Now let's look at `outputs`. It is a function that takes the dependencies from `inputs` as
its parameters, and its return value is an attribute set, which represents the build
results of the flake:
```nix{11-19}
{
description = "A simple NixOS flake";
inputs = {
# NixOS official package source, here using the nixos-23.11 branch
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# The host with the hostname `my-nixos` will use this configuration
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
Flakes can have various purposes and can have different types of outputs. The section
[Flake Outputs](../other-usage-of-flakes/outputs.md) provides a more detailed
introduction. Here, we are only using the `nixosConfigurations` type of outputs, which is
used to configure NixOS systems.
When we run the `sudo nixos-rebuild switch` command, it looks for the
`nixosConfigurations.my-nixos` attribute (where `my-nixos` will be the hostname of your
current system) in the attribute set returned by the `outputs` function of
`/etc/nixos/flake.nix` and uses the definition there to configure your NixOS system.
Actually, we can also customize the location of the flake and the name of the NixOS
configuration instead of using the defaults. This can be done by adding the `--flake`
parameter to the `nixos-rebuild` command. Here's an example:
```nix
sudo nixos-rebuild switch --flake /path/to/your/flake#your-hostname
```
A brief explanation of the `--flake /path/to/your/flake#your-hostname` parameter:
1. `/path/to/your/flake` is the location of the target flake. The default path is
`/etc/nixos/`.
2. `#` is a separator, and `your-hostname` is the name of the NixOS configuration.
`nixos-rebuild` will default to using the hostname of your current system as the
configuration name to look for.
You can even directly reference a remote GitHub repository as your flake source, for
example:
```nix
sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
```
## 3. The Special Parameter `self` of the `outputs` Function {#special-parameter-self-of-outputs-function}
Although we have not mentioned it before, all the example code in the previous sections
has one more special parameter in the `outputs` function, and we will briefly introduce
its purpose here.
The description of it in the [nix flake - Nix Manual] is:
> The special input named `self` refers to the outputs and source tree of this flake.
This means that `self` is the return value of the current flake's `outputs` function and
also the path to the current flake's source code folder (source tree).
We are not using the `self` parameter here, but in some more complex examples (or
configurations you may find online) later, you will see the usage of `self`.
> Note: You might come across some code where people use `self.outputs` to reference the
> outputs of the current flake, which is indeed possible. However, the Nix Manual does not
> provide any explanation for this, and it is considered an internal implementation detail
> of flakes. It is not recommended to use this in your own code!
## 4. Simple Introduction to `nixpkgs.lib.nixosSystem` Function {#simple-introduction-to-nixpkgs-lib-nixos-system}
**A Flake can depend on other Flakes to utilize the features they provide.**
By default, a flake searches for a `flake.nix` file in the root directory of each of its
dependencies (i.e., each item in `inputs`) and lazily evaluates their `outputs` functions.
It then passes the attribute set returned by these functions as arguments to its own
`outputs` function, enabling us to use the features provided by the other flakes within
our current flake.
More precisely, the evaluation of the `outputs` function for each dependency is lazy. This
means that a flake's `outputs` function is only evaluated when it is actually used,
thereby avoiding unnecessary calculations and improving efficiency.
The description above may be a bit confusing, so let's take a look at the process with the
`flake.nix` example used in this section. Our `flake.nix` declares the `inputs.nixpkgs`
dependency, so that [nixpkgs/flake.nix] will be evaluated when we run the
`sudo nixos-rebuild switch` command.
From the source code of the Nixpkgs repository, we can see that its flake outputs
definition includes the `lib` attribute, and in our example, we use the `lib` attribute's
`nixosSystem` function to configure our NixOS system:
```nix{8-13}
{
inputs = {
# NixOS official package source, here using the nixos-23.11 branch
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
The attribute set following `nixpkgs.lib.nixosSystem` is the function's parameter. We have
only set two parameters here:
1. `system`: This is straightforward, it's the system architecture parameter.
2. `modules`: This is a list of modules, where the actual NixOS system configuration is
defined. The `/etc/nixos/configuration.nix` configuration file itself is a Nixpkgs
Module, so it can be directly added to the `modules` list for use.
Understanding these basics is sufficient for beginners. Exploring the
`nixpkgs.lib.nixosSystem` function in detail requires a grasp of the Nixpkgs module
system. Readers who have completed the
[Modularizing NixOS Configuration](./modularize-the-configuration.md) section can return
to [nixpkgs/flake.nix] to find the definition of `nixpkgs.lib.nixosSystem`, trace its
source code, and study its implementation.
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix
[Module System - Nixpkgs]:
https://github.com/NixOS/nixpkgs/blob/23.11/doc/module-system/module-system.chapter.md
[nixpkgs/nixos-23.11/lib/modules.nix - _module.args]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/lib/modules.nix#L122-L184
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/doc/manual/development/option-types.section.md?plain=1#L237-L244

View File

@ -1,13 +1,18 @@
# Enabling NixOS with Flakes
## Enabling Flakes Support for NixOS {#enable-nix-flakes}
Compared to the default configuration method currently used in NixOS, Flakes offers better
reproducibility. Its clear package structure definition inherently supports dependencies
on other Git repositories, facilitating code sharing. Therefore, this book suggests using
Flakes to manage system configurations. Currently, Flakes is still an experimental feature
and not enabled by default. We need to manually modify the `/etc/nixos/configuration.nix`
file to enable the Flakes feature and the accompanying new nix command-line tool:
Flakes to manage system configurations.
This section describes how to use Flakes to manage NixOS system configuration, and **you
don't need to know anything about Flakes in advance**.
## Enabling Flakes Support for NixOS {#enable-nix-flakes}
Currently, Flakes is still an experimental feature and not enabled by default. We need to
manually modify the `/etc/nixos/configuration.nix` file to enable the Flakes feature and
the accompanying new nix command-line tool:
```nix{12,16}
{ config, pkgs, ... }:
@ -115,10 +120,17 @@ Currently, our flake includes these files:
- `/etc/nixos/hardware-configuration.nix`: This is the system hardware configuration file,
generated by NixOS, which describes the system's hardware information.
Up to this point, `/etc/nixos/flake.nix` has merely been a thin wrapper around
## Conclusion
Up to this point, we have merely added a very simple configuration file,
`/etc/nixos/flake.nix`, which has merely been a thin wrapper around
`/etc/nixos/configuration.nix`, offering no new functionality and introducing no
disruptive changes. In the content of the book that follows, we will gradually see the
benefits that such a wrapper brings.
disruptive changes.
In the content of the book that follows, we will learn about the structure and
functionality of `flake.nix` and gradually see the benefits that such a wrapper can
bring.
> Note: The configuration management method described in this book is NOT "Everything in a
> single file". It is recommended to categorize configuration content into different nix
@ -132,419 +144,8 @@ benefits that such a wrapper brings.
> [Other Useful Tips - Managing NixOS Configuration with Git](./other-useful-tips.md) will
> introduce several best practices for managing NixOS configuration with Git.
## `flake.nix` Configuration Explained {#flake-nix-configuration-explained}
Above, we created a `flake.nix` file to manage system configurations, but you might still
be unclear about its structure. Let's explain the content of this file in detail.
### 1. Flake Inputs
First, let's look at the `inputs` attribute. It is an attribute set that defines all the
dependencies of this flake. These dependencies will be passed as arguments to the
`outputs` function after they are fetched:
```nix{2-5,7}
{
inputs = {
# NixOS official package source, using the nixos-23.11 branch here
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# Omitting previous configurations......
};
}
```
Dependencies in `inputs` has many types and definitions. It can be another flake, a
regular Git repository, or a local path. The section
[Other Usage of Flakes - Flake Inputs](../other-usage-of-flakes/inputs.md) describes
common types of dependencies and their definitions in detail.
Here we only define a dependency named `nixpkgs`, which is the most common way to
reference in a flake, i.e., `github:owner/name/reference`. The `reference` here can be a
branch name, commit-id, or tag.
After `nixpkgs` is defined in `inputs`, you can use it in the parameters of the subsequent
`outputs` function, which is exactly what our example does.
### 2. Flake Outputs
Now let's look at `outputs`. It is a function that takes the dependencies from `inputs` as
its parameters, and its return value is an attribute set, which represents the build
results of the flake:
```nix{11-19}
{
description = "A simple NixOS flake";
inputs = {
# NixOS official package source, here using the nixos-23.11 branch
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
# The `self` parameter is special, it refers to
# the attribute set returned by the `outputs` function itself.
outputs = { self, nixpkgs, ... }@inputs: {
# The host with the hostname `my-nixos` will use this configuration
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
Flakes can have various purposes and can have different types of outputs. The section
[Flake Outputs](../other-usage-of-flakes/outputs.md) provides a more detailed
introduction. Here, we are only using the `nixosConfigurations` type of outputs, which is
used to configure NixOS systems.
When we run the `sudo nixos-rebuild switch` command, it looks for the
`nixosConfigurations.my-nixos` attribute (where `my-nixos` will be the hostname of your
current system) in the attribute set returned by the `outputs` function of
`/etc/nixos/flake.nix` and uses the definition there to configure your NixOS system.
Actually, we can also customize the location of the flake and the name of the NixOS
configuration instead of using the defaults. This can be done by adding the `--flake`
parameter to the `nixos-rebuild` command. Here's an example:
```nix
sudo nixos-rebuild switch --flake /path/to/your/flake#your-hostname
```
A brief explanation of the `--flake /path/to/your/flake#your-hostname` parameter:
1. `/path/to/your/flake` is the location of the target flake. The default path is
`/etc/nixos/`.
2. `#` is a separator, and `your-hostname` is the name of the NixOS configuration.
`nixos-rebuild` will default to using the hostname of your current system as the
configuration name to look for.
You can even directly reference a remote GitHub repository as your flake source, for
example:
```nix
sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
```
### 3. Simple Introduction to `nixpkgs.lib.nixosSystem` Function {#simple-introduction-to-nixpkgs-lib-nixos-system}
**A Flake can depend on other Flakes to utilize the features they provide.**
By default, a flake searches for a `flake.nix` file in the root directory of each of its
dependencies (i.e., each item in `inputs`) and lazily evaluates their `outputs` functions.
It then passes the attribute set returned by these functions as arguments to its own
`outputs` function, enabling us to use the features provided by the other flakes within
our current flake.
More precisely, the evaluation of the `outputs` function for each dependency is lazy. This
means that a flake's `outputs` function is only evaluated when it is actually used,
thereby avoiding unnecessary calculations and improving efficiency.
The description above may be a bit confusing, so let's take a look at the process with the
`flake.nix` example used in this section. Our `flake.nix` declares the `inputs.nixpkgs`
dependency, so that [nixpkgs/flake.nix] will be evaluated when we run the
`sudo nixos-rebuild switch` command.
From the source code of the Nixpkgs repository, we can see that its flake outputs
definition includes the `lib` attribute, and in our example, we use the `lib` attribute's
`nixosSystem` function to configure our NixOS system:
```nix{8-13}
{
inputs = {
# NixOS official package source, here using the nixos-23.11 branch
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
The attribute set following `nixpkgs.lib.nixosSystem` is the function's parameter. We have
only set two parameters here:
1. `system`: This is straightforward, it's the system architecture parameter.
2. `modules`: This is a list of modules, where the actual NixOS system configuration is
defined. The `/etc/nixos/configuration.nix` configuration file itself is a Nixpkgs
Module, so it can be directly added to the `modules` list for use.
Understanding these basics is sufficient for beginners. Exploring the
`nixpkgs.lib.nixosSystem` function in detail requires a grasp of the Nixpkgs module
system. Readers who have completed the
[Modularizing NixOS Configuration](./modularize-the-configuration.md) section can return
to [nixpkgs/flake.nix] to find the definition of `nixpkgs.lib.nixosSystem`, trace its
source code, and study its implementation.
## Nixpkgs Module Structure Explained {#simple-introduction-to-nixpkgs-module-structure}
> The detailed workings of this module system will be introduced in the following
> [Modularizing NixOS Configuration](./modularize-the-configuration.md) section. Here,
> we'll just cover some basic knowledge.
You might be wondering why the `/etc/nixos/configuration.nix` configuration file adheres
to the Nixpkgs Module definition and can be referenced directly within the `flake.nix`.
This is because the Nixpkgs repository contains a significant amount of NixOS
implementation source code, primarily written in Nix. To manage and maintain such a large
volume of Nix code and to allow users to customize various functions of their NixOS
systems, a modular system for Nix code is essential.
This modular system for Nix code is also implemented within the Nixpkgs repository and is
primarily used for modularizing NixOS system configurations. However, it is also widely
used in other contexts, such as nix-darwin and home-manager. Since NixOS is built on this
modular system, it is only natural that its configuration files, including
`/etc/nixos/configuration.nix`, are Nixpkgs Modules.
Before delving into the subsequent content, it's essential to have a basic understanding
of how this module system operates.
Here's a simplified structure of a Nixpkgs Module:
```nix
{lib, config, options, pkgs, ...}:
{
# Importing other Modules
imports = [
# ...
./xxx.nix
];
for.bar.enable = true;
# Other option declarations
# ...
}
```
The definition is actually a Nix function, and it has five **automatically generated,
automatically injected, and declaration-free parameters** provided by the module system:
1. `lib`: A built-in function library included with nixpkgs, offering many practical
functions for operating Nix expressions.
- For more information, see <https://nixos.org/manual/nixpkgs/stable/#id-1.4>.
2. `config`: A set of all options' values in the current environment, which will be used
extensively in the subsequent section on the module system.
3. `options`: A set of all options defined in all Modules in the current environment.
4. `pkgs`: A collection containing all nixpkgs packages, along with several related
utility functions.
- At the beginner stage, you can consider its default value to be
`nixpkgs.legacyPackages."${system}"`, and the value of `pkgs` can be customized
through the `nixpkgs.pkgs` option.
5. `modulesPath`: A parameter available only in NixOS, which is a path pointing to
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/modules).
- It is defined in
[nixpkgs/nixos/lib/eval-config-minimal.nix#L43](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/lib/eval-config-minimal.nix#L43).
- It is typically used to import additional NixOS modules and can be found in most
NixOS auto-generated `hardware-configuration.nix` files.
## Passing Non-default Parameters to Submodules {#pass-non-default-parameters-to-submodules}
If you need to pass other non-default parameters to submodules, you will need to use some
special methods to manually specify these non-default parameters.
The Nixpkgs module system provides two ways to pass non-default parameters:
1. The `specialArgs` parameter of the `nixpkgs.lib.nixosSystem` function
2. Using the `_module.args` option in any module to pass parameters
The official documentation for these two parameters is buried deep and is vague and hard
to understand. If readers are interested, I will include the links here:
1. `specialArgs`: There are scattered mentions related to it in the NixOS Manual and the
Nixpkgs Manual.
- Nixpkgs Manual: [Module System - Nixpkgs]
- NixOS Manual:
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]
1. `_module.args`:
- NixOS Manual:
[Appendix A. Configuration Options](https://nixos.org/manual/nixos/stable/options#opt-_module.args)
- Source Code: [nixpkgs/nixos-23.11/lib/modules.nix - _module.args]
In short, `specialArgs` and `_module.args` both require an attribute set as their value,
and they serve the same purpose, passing all parameters in the attribute set to all
submodules. The difference between them is:
1. The `_module.args` option can be used in any module to pass parameters to each other,
which is more flexible than `specialArgs`, which can only be used in the
`nixpkgs.lib.nixosSystem` function.
1. `_module.args` is declared within a module, so it must be evaluated after all modules
have been evaluated before it can be used. This means that **if you use the parameters
passed through `_module.args` in `imports = [ ... ];`, it will result in an
`infinite recursion` error**. In this case, you must use `specialArgs` instead.
The NixOS community generally recommends prioritizing the use of the `_module.args` option
and resorting to `specialArgs` only when `_module.args` cannot be used.
Suppose you want to pass a certain dependency to a submodule for use. You can use the
`specialArgs` parameter to pass the `inputs` to all submodules:
```nix{13}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# Set all inputs parameters as special arguments for all submodules,
# so you can directly use all dependencies in inputs in submodules
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
];
};
};
}
```
Or you can achieve the same effect using the `_module.args` option:
```nix{14}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
# Set all inputs parameters as special arguments for all submodules,
# so you can directly use all dependencies in inputs in submodules
_module.args = { inherit inputs; };
}
];
};
};
}
```
Choose one of the two methods above to modify your configuration, and then you can use the
`inputs` parameter in `/etc/nixos/configuration.nix`. The module system will automatically
match the `inputs` defined in `specialArgs` and inject it into all submodules that require
this parameter:
```nix{3}
# Nix will match by name and automatically inject the inputs
# from specialArgs/_module.args into the third parameter of this function
{ config, pkgs, inputs, ... }:
{
# ...
}
```
The next section will demonstrate how to use `specialArgs`/`_module.args` to install
system software from other flake sources.
## Installing System Software from Other Flake Sources {#install-system-packages-from-other-flakes}
The most common requirement for managing a system is to install software, and we have
already seen in the previous section how to install packages from the official nixpkgs
repository using `environment.systemPackages`. These packages all come from the official
nixpkgs repository.
Now, we will learn how to install software packages from other flake sources, which is
much more flexible than installing directly from nixpkgs. The main use case is to install
the latest version of a software that is not yet added or updated in Nixpkgs.
Taking the Helix editor as an example, here's how to compile and install the master branch
of Helix directly.
First, add the helix input data source to `flake.nix`:
```nix{6,12,18}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
# helix editor, use the master branch
helix.url = "github:helix-editor/helix/master";
};
outputs = inputs@{ self, nixpkgs, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
# This module works the same as the `specialArgs` parameter we used above
# chose one of the two methods to use
# { _module.args = { inherit inputs; };}
];
};
};
}
```
Next, you can reference this flake input data source in `configuration.nix`:
```nix{1,10}
{ config, pkgs, inputs, ... }:
{
# ...
environment.systemPackages = with pkgs; [
git
vim
wget
curl
# Here, the helix package is installed from the helix input data source
inputs.helix.packages."${pkgs.system}".helix
];
# ...
}
```
Make the necessary changes and deploy with `sudo nixos-rebuild switch`. The deployment
will take much longer this time because Nix will compile the entire Helix program from
source.
After deployment, you can directly test and verify the installation using the `hx` command
in the terminal.
Additionally, if you just want to try out the latest version of Helix and decide whether
to install it on your system later, there is a simpler way to do it in one command (but as
mentioned earlier, compiling from source will take a long time):
```bash
nix run github:helix-editor/helix/master
```
We will go into more detail on the usage of `nix run` in the following section
[Usage of the New CLI](../other-usage-of-flakes/the-new-cli.md).
## Leveraging Features from Other Flakes Packages
In fact, this is the primary functionality of Flakes — a flake can depend on other flakes,
allowing it to utilize the features they provide. It's akin to how we incorporate
functionalities from other libraries when writing programs in TypeScript, Go, Rust, and
other programming languages.
The example above, using the latest version from the official Helix Flake, illustrates
this functionality. More use cases will be discussed later, and here are a few examples
referenced for future mention:
- [Getting Started with Home Manager](./start-using-home-manager.md): This introduces the
community's Home-Manager as a dependency, enabling direct utilization of the features
provided by this Flake.
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): Here, different
versions of Nixpkgs are introduced as dependencies, allowing for flexible selection of
packages from various versions of Nixpkgs.
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix

View File

@ -0,0 +1,243 @@
# 使用 S3 自定义二进制缓存托管
## 简介
一个关于如何使用 MinIO S3 服务器设置自己的 S3 Nix 二进制缓存的指南。
## Nix 中的软件如何存储?
可以在系统上安装同一软件包的多个版本,从而能够同时满足各种依赖链。这使得可以安装多个依赖于
同一个第三方软件包的软件包,但使用不同版本的它。为了实现这一点,所有软件包都安装在全局的
Nix 存储中,路径为 `/nix/store/`,然后通过符号链接到相应的位置。为了验证软件包的唯一性,其
整个目录被哈希,并将哈希放入软件包的主文件夹的名称中。从使用相同依赖软件版本构建的相同 Nix
表达式构建的每个软件包都会产生相同的哈希,无论它是在什么系统上构建的。如果任何依赖软件的版
本发生更改,这将导致最终软件包的新哈希。
使用符号链接来“安装”软件包并将所有正确的依赖项链接到它还使得原子更新成为可能。为了使这一点
更清楚,让我们来考虑一个例子,其中软件 X 安装在旧版本中并且应该进行更新。软件 X 安装在全局
Nix 存储中的自己的目录中,并符号链接到正确的目录,比如 `/usr/local/bin/`。当触发更新时X
的新版本会安装到全局 Nix 存储中,而不会干扰其旧版本。一旦安装完成,包括其所有依赖项在内的
最终软件包在 Nix 存储中,最后一步是将符号链接更改为 `/usr/local/bin/`。由于在 Unix 中创建
新的符号链接来覆盖旧的符号链接是一个原子操作,因此这个操作不可能失败并使软件包处于损坏状
态。唯一可能的问题是在符号链接创建之前或之后失败。无论哪种方式,结果都是我们要么有旧版本的
X要么有新安装的版本但中间没有任何东西。
引用自原作https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b
## Nix 二进制缓存
无论 Nix 的每个方面听起来多么棒,它的设计也有一个主要缺点,那就是每次构建软件包都会触发整
个依赖链的构建过程。这可能需要相当长的时间,因为甚至像 gcc 或 ghc 这样的编译器都必须提前构
建。这样的构建过程可能会消耗大量内存,在受限平台(如树莓派)上使用它时会引入额外的障碍。
为了克服总是从头开始构建一切以及丢失对软件包预构建版本的访问权限的缺点,还可以使用 S3 服务
器(如 MinIO构建自己的 Nix 二进制缓存。
设置您的缓存,填充它的二进制文件并使用您新构建的缓存中的二进制文件的过程将被逐步描述。对于
本手册,我假设您已经有了 Nix并且在您的环境中已经运行了 MinIO 服务器。如果没有,您可以查
看 MinIO 的[官方部署指南](https://min.io/docs/minio/linux/operations/installation.html)。
您需要确保通过信任的证书以 `HTTPS` 方式访问 MinIO。在这里Let's Encrypt 将非常有用。
在本文中,让我们探讨如何自托管一个 S3 兼容服务器 MinIO 作为二进制缓存存储。
引用自原作https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b
## 如何将 S3 用作二进制缓存服务器
### 先决条件
- 在您的环境中设置 MinIO。
- 拥有有效的 SSL 证书,可以是公共证书也可以是私有证书。在本教程中,我们将使用
`minio.homelab.local`(私有证书)的示例步骤。如果您计划使用私有证书,您必须自己解决 DNS
挑战。因此,建议使用公共证书。
- 在您的环境中安装 `minio-client`
### 生成密码
```bash
nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1
# oenu1Yuch3rohz2ahveid0koo4giecho
```
### 设置 MinIO 客户端
安装 MinIO 命令行客户端 `mc`
```nix
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
minio-client # 用于文件系统和对象存储的 ls、cp、mkdir、diff 和 rsync 命令的替代品
];
}
```
创建或编辑 `~/.mc/config.json`
```json
{
"version": "10",
"aliases": {
"s3": {
"url": "https://s3.homelab.local",
"accessKey": "minio",
"secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho",
"api": "s3v4",
"path": "auto"
}
}
}
```
在我们用来构建 Nix 包的机器上,我们需要在一些地方使用 S3 凭据。设置一个文件
`~/.aws/credentials` 并用我们的 nixbuilder 用户的凭据填充它。用 `pwgen` 命令生成的密码替换
`<nixbuildersecret>`
```plaintext
[default]
aws_access_key_id=nixbuilder
aws_secret_access_key=<nixbuildersecret>
```
### 设置 S3 存储桶作为二进制缓存
创建 `nix-cache` 存储桶。
```bash
mc mb s3/nix-cache
```
创建 `nixbuilder` MinIO 用户并分配密码。
```bash
mc admin user add s3 nixbuilder <PASSWORD>
```
在当前工作目录中创建名为 `nix-cache-write.json` 的文件,并具有以下内容:
```json
{
"Id": "AuthenticatedWrite",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AuthenticatedWrite",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"],
"Principal": "nixbuilder"
}
]
}
```
使用 `nix-cache-write.json` 创建允许 `nixbuilder` 上载文件到缓存的策略。
```bash
mc admin policy add s3 nix-cache-write nix-cache-write.json
```
将我们上面创建的策略与 `nixbuilder` 用户关联。
```bash
mc admin policy set s3 nix-cache-write user=nixbuilder
```
允许匿名用户在不进行身份验证的情况下下载文件。
```bash
mc anonymous set download s3/nix-cache
```
在工作目录中创建名为 `nix-cache-info` 的文件。此文件告诉 Nix 桶确实是一个二进制缓存。
```bash
cat > nix-cache-info <<EOF
StoreDir: /nix/store
WantMassQuery: 1
Priority: 40
EOF
```
`nix-cache-info` 复制到缓存桶。
```bash
mc cp ./nix-cache-info s3/nix-cache/nix-cache-info
```
### 生成密钥对
为签署存储路径生成一个密钥对。密钥名称是任意的,但 NixOS 开发人员强烈建议使用缓存的域名后
跟一个整数。如果密钥需要撤销或重新生成,可以递增尾部整数。
```bash
nix key generate-secret --key-name s3.homelab.local-1 > ~/.config/nix/secret.key
nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key
cat ~/.config/nix/public.key
# s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=
```
### 使用 Flake 激活二进制缓存
将以下内容放入 `configuration.nix` 或您的任何自定义 NixOS 模块中:
```nix
{
nix = {
settings = {
# 在获取软件包时,替代器将被附加到默认的替代器。
extra-substituters = [
"https://s3.homelab.local/nix-cache/"
];
extra-trusted-public-keys = [
"s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs="
];
};
};
}
```
重新构建系统。
```bash
sudo nixos-rebuild switch --upgrade --flake .#<HOST>
```
### 推送路径到存储
对本地存储中的一些路径进行签名。
```bash
nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system
```
将这些路径复制到缓存。
```bash
nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system
```
### 添加自动对象到期策略
```bash
mc ilm rule add s3/nix-cache --expire-days "DAYS"
# 例如mc ilm rule add s3/nix-cache --expire-days "7"
```
### 参考资料
以下是我在编写本文档时使用的一些来源:
- [Jeff 的博客文章Nix 二进制缓存](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html)
- [NixOS wiki 上的二进制缓存](https://nixos.wiki/wiki/Binary_Cache)
- [NixOS 手册中关于通过 S3 提供 Nix 存储](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html)
- [NixOS 手册中关于通过 HTTP 提供 Nix 存储](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html)

View File

@ -226,7 +226,7 @@ nix-repl> pkgs.pkgsCross.riscv64.stdenv.cc
system = "x86_64-linux";
modules = [
{
nigpkgs.crossSystem = {
nixpkgs.crossSystem = {
config = "riscv64-unknown-linux-gnu";
};
@ -276,7 +276,7 @@ nix-repl> pkgs.pkgsCross.riscv64.stdenv.cc
};
modules = [
{
nigpkgs.crossSystem = {
nixpkgs.crossSystem = {
config = "riscv64-unknown-linux-gnu";
};
}

View File

@ -257,7 +257,7 @@ session可以在其中正常使用 `node` `pnpm` 命令.
过 NixOS 的 `environment.systemPackages` 来安装这个 wrapper然后直接执行它
```nix
{pkgs, lib, ...}{
{pkgs, lib, ...}:{
environment.systemPackages = [
# 将 dev-shell 安装到系统环境中
@ -276,7 +276,7 @@ session可以在其中正常使用 `node` `pnpm` 命令.
mkdir -p $out/bin/
ln -s ${pkgs.nushell}/bin/nu $out/bin/dev-shell
wrapProgram $out/bin/dev-shell --prefix PATH : ${pkgs.lib.makeBinPath packages}
'';)
'')
];
}
```

View File

@ -8,72 +8,14 @@ Nix 有多种安装方式:
本书主要介绍 NixOS 与 Flakes 的使用,因此不展开讨论。
NixOS 的安装步骤很简单,这里不多介绍,仅列一下我觉得比较有用的参考资料:
NixOS 的安装不难,与许多传统发行版类似,它提供了一个对新手非常友好的 GUI 安装程序。请移步
[NixOS-CN 的系统安装教程](https://nixos-cn.org/tutorials/installation/) 查看详细的安装步
骤。
其他可能有用的参考资料:
1. [NixOS 官网](https://nixos.org/download.html)
1. [NixOS-CN 社区的保姆级安装教程](https://nixos-cn.org/tutorials/installation/)
1. [复用 flake 管理 NixOS WSL](https://zhuanlan.zhihu.com/p/627073511): 使用 WSL 的用户可
以参考下这篇文章
1. [ryan4yin/nix-darwin-kickstarter](https://github.com/ryan4yin/nix-darwin-kickstart):
macOS 用户可以通过这个模板仓库结合本书的内容来学习使用 Nix.
## 对中国大陆用户流畅使用 NixOS 的建议
国内用户在使用 NixOS 时会存在一些网络问题,一是 NixOS 高度依赖 GitHub 作为 channel/flake
数据源——在国内访问 GitHub 相当的慢,二是 NixOS 官方的包缓存服务器在国内访问速度较慢。
为了解决这些问题,你可以使用国内的镜像源,或者使用代理工具来加速访问。
这里我先介绍几个比较简单的配置方法。
### 1. 包缓存服务器的加速访问
首先,在执行后面给出的任何 `nix` 相关命令时,你都可以通过 `--option` 选项来指定镜像源,例
如:
```bash
# 使用上海交通大学的镜像源
# 官方文档: https://mirror.sjtu.edu.cn/docs/nix-channels/store
nixos-rebuild switch --option substituters "https://mirror.sjtu.edu.cn/nix-channels/store"
# 使用中国科学技术大学的镜像源
# 官方文档: https://mirrors.ustc.edu.cn/help/nix-channels.html
nixos-rebuild switch --option substituters "https://mirrors.ustc.edu.cn/nix-channels/store"
# 使用清华大学的镜像源
# 官方文档: https://mirrors.tuna.tsinghua.edu.cn/help/nix-channels/
nixos-rebuild switch --option substituters "https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store"
# 其他 nix 命令同样可以使用 --option 选项,例如 nix shell
nix shell nixpkgs#cowsay --option substituters "https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store"
```
你可以自己测试下上述几个镜像源的速度,选速度最快的一个。
### 2. Channel/Flake 的加速访问
对于 Flake Inputs 跟 Channels 的加速访问,这个就需要使用代理工具加速访问。
可以通过如下命令设置代理环境变量,实现使用 socks5/http 代理加速 nix 的网络访问:
```bash
sudo mkdir /run/systemd/system/nix-daemon.service.d/
cat << EOF >/run/systemd/system/nix-daemon.service.d/override.conf
[Service]
Environment="https_proxy=socks5h://localhost:7891"
EOF
sudo systemctl daemon-reload
sudo systemctl restart nix-daemon
```
**但请注意,系统重启后 `/run/` 目录下的内容会被清空,所以每次重启后都需要重新执行上述命
令**
更详细的说明与其他用法介绍,参见
[添加自定义缓存服务器](../nixos-with-flakes/add-custom-cache-servers.md) 一节,注意这部分
内容可能需要一定的 NixOS 使用经验才能理解。
> 注意:使用一些商用代理或公共代理时你可能会遇到 GitHub 下载时报 HTTP 403 错误
> [nixos-and-flakes-book/issues/74](https://github.com/ryan4yin/nixos-and-flakes-book/issues/74)
> 可尝试通过更换代理服务器或者设置
> [access-tokens](https://github.com/NixOS/nix/issues/6536) 来解决。

View File

@ -8,17 +8,11 @@ Nix 提供了官方缓存服务器 <https://cache.nixos.org>,它缓存了 nixp
## 为什么要添加自定义缓存服务器 {#why-add-custom-cache-servers}
> 注意:这里介绍的手段只能加速部分包的下载,许多 inputs 数据源仍然会从 Github 拉取。另外如
> 果找不到缓存,会执行本地构建,这通常仍然需要从国外下载源码与构建依赖,因此仍然会很慢。为
> 了完全解决速度问题,仍然建议使用旁路网关或 TUN 等全局代理方案。
两个原因:
1. 添加一些第三方项目的缓存服务器,例如 nix-community 的缓存服务器
<https://nix-community.cachix.org>,这可以大大提升这些第三方项目的构建速度。
1. 添加离用户最近的缓存服务器镜像站,用于加速下载。
1. 官方缓存服务器在中国的访问速度非常慢,如果没有局域网全局代理的话,基本上是无法使用
的。添加国内的 ustc/sjtu/tuna 等 Nix 缓存镜像源可以缓解此问题。
## 如何添加自定义缓存服务器 {#how-to-add-custom-cache-servers}
@ -270,35 +264,11 @@ Nix 提供了
> 参考了 Issue:
> [roaming laptop: network proxy configuration - NixOS/nixpkgs](https://github.com/NixOS/nixpkgs/issues/27535#issuecomment-1178444327)
虽然前面提到了,旁路网关可以完全解决 NixOS 的包下载速度问题,但是旁路网关的配置比较麻烦,
而且经常需要额外的硬件支持。
更多的用户可能会希望能直接通过 HTTP/Socks5 代理来加速包下载,这里介绍下怎么设置。
有些用户可能会希望能直接通过 HTTP/Socks5 代理来加速包下载,这里介绍下怎么设置。
直接在 Terminal 中使用 `export HTTPS_PROXY=http://127.0.0.1:7890` 这类方式是无法生效的,因
为 nix 实际干活的是一个叫 `nix-daemon` 的后台进程,而不是直接在 Terminal 中执行的命令。
nix-daemon 的实现代码是
[nixpkgs/nixos/modules/services/system/nix-daemon.nix](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/modules/services/system/nix-daemon.nix#L184-L191)
它通过 `systemd.services.nix-daemon.environment` 选项设置了环境变量,我们也能通过同样的手
段来往 nix-daemon 的运行环境中添加代理相关的环境变量,一个示例 Module 如下:
```nix
{
systemd.services.nix-daemon.environment = {
# socks5h mean that the hostname is resolved by the SOCKS server
https_proxy = "socks5h://localhost:7891";
# https_proxy = "http://localhost:7890"; # or use http prctocol instead of socks5
};
}
```
部署此配置后,可通过 `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'` 查看
nix-daemon 进程的所有环境变量,确认环境变量的设置是否生效。
**但是要注意当代理服务器不可用时nix-daemon 将无法访问任何缓存服务器**!所以我还是更建
议使用旁路网关等透明代理方案。
如果你只是临时需要使用代理,可以通过如下命令设置代理环境变量:
```bash
@ -311,9 +281,20 @@ sudo systemctl daemon-reload
sudo systemctl restart nix-daemon
```
部署此配置后,可通过 `sudo cat /proc/$(pidof nix-daemon)/environ | tr '\0' '\n'` 查看
nix-daemon 进程的所有环境变量,确认环境变量的设置是否生效。
位于 `/run/systemd/system/nix-daemon.service.d/override.conf` 的设置会在系统重启后被自动删
除,或者你可以手动删除它并重启 nix-daemon 服务来恢复原始设置。
如果你希望永久设置代理,建议将上述命令保存为 shell 脚本,在每次启动系统时运行一下。或者也
可以使用旁路网关或 TUN 等全局代理方案。
> 社区也有人通过 `systemd.services.nix-daemon.environment` 以声明式的方式为 nix-daemon 永
> 久设置代理但这种做法下一旦代理出了问题会非常麻烦nix-daemon 将无法正常工作,进而导致
> 大多数 nix 命令无法正常运行,而且 systemd 自身的配置被设置了只读保护,无法简单地修改配置
> 删除代理设置。因此不建议使用这种方式。
> 使用一些商用代理或公共代理时你可能会遇到 GitHub 下载时报 HTTP 403 错误
> [nixos-and-flakes-book/issues/74](https://github.com/ryan4yin/nixos-and-flakes-book/issues/74)
> 可尝试通过更换代理服务器或者设置

View File

@ -35,15 +35,6 @@ Flakes[^3],因此我们可以相当确定 Flakes 不会被废弃。
总的来说,我仍然推荐大家使用 Flakes毕竟这本书本身也是围绕 NixOS 与 Flakes 编写的,但是也
要做好准备——未来可能需要解决一些不兼容变更带来的问题。
## Flakes 入门
我就不多介绍了,请直接参考如下文档:
- [Flakes - nix.dev](https://nix.dev/concepts/flakes)
- [Nix Flakes, Part 1: An introduction and tutorial (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-05-25-flakes/)
- [Nix Flakes, Part 2: Evaluation caching (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-06-25-eval-cache/)
- [Nix Flakes, Part 3: Managing NixOS systems (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-07-31-nixos-flakes/)
- [Practical Nix Flakes](https://serokell.io/blog/practical-nix-flakes)
## Flakes 何时会成为稳定特性? {#when-will-flakes-stablized}

View File

@ -0,0 +1,269 @@
# Flakes 的组合能力与 Nixpkgs Module 系统
## Nixpkgs Module 结构的简单介绍 {#simple-introduction-to-nixpkgs-module-structure}
> 在后面的 [模块化 NixOS 配置](./modularize-the-configuration.md) 一节中会详细介绍这套模块
> 系统的工作方式,这里只介绍些基础知识。
为什么 `/etc/nixos/configuration.nix` 这个配置文件会符合 Nixpkgs Module 定义,从而能直接在
`flake.nix` 中引用它呢?可能会有读者觉得这有点出乎意料。
这实际是因为 Nixpkgs 中包含了大量 NixOS 的实现源码,这些源码大都使用 Nix 语言编写。为了编
写维护如此多的 Nix 代码,并且使用户能灵活地自定义其 NixOS 系统的各项功能,就必须要有一套
Nix 代码的模块化系统。
这套 Nix 代码的模块系统的实现也同样在 Nixpkgs 仓库中,它主要被用于 NixOS 系统配置的模块
化,但也有其他的应用,比如 nix-darwin 跟 home-manager 都大量使用了这套模块系统。
既然 NixOS 是基于这套模块系统构建的,那它的配置文件(包括 `/etc/nixos/configuration.nix`
是一个Nixpkgs Module也就显得非常自然了。
在学习后面的内容之前,我们需要先简单了解下这套模块系统的工作方式。
一个简化的 Nixpkgs Module 结构如下:
```nix
{lib, config, options, pkgs, ...}:
{
# 导入其他 Modules
imports = [
# ......
# ./xxx.nix
];
for.bar.enable = true;
# other options declarations
# ...
}
```
可以看到它的定义实际是一个 Nix 函数,该函数有 5 个**由模块系统自动生成、自动注入、无需额外
声明的参数**
1. `lib`: **nixpkgs 自带的函数库,提供了许多操作 Nix 表达式的实用函数**
- 详见 <https://nixos.org/manual/nixpkgs/stable/#id-1.4>
2. `config`: 包含了当前环境中所有 option 的值,在后面学习模块系统时会大量使用它
3. `options`: 当前环境中所有 Modules 中定义的所有 options 的集合
4. `pkgs`: **一个包含所有 nixpkgs 包的集合,它也提供了许多相关的工具函数**
- 入门阶段可以认为它的默认值为 `nixpkgs.legacyPackages."${system}"`,可通过
`nixpkgs.pkgs` 这个 option 来自定义 `pkgs` 的值
5. `modulesPath`: 一个只在 NixOS 中可用的参数,是一个 Path指向
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/modules)
- 它在
[nixpkgs/nixos/lib/eval-config-minimal.nix#L43](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/lib/eval-config-minimal.nix#L43)
中被定义
- 通常被用于导入一些额外的 NixOS 模块NixOS 自动生成的 `hardware-configuration.nix`
基本都能看到它
## 传递非默认参数到模块系统中 {#pass-non-default-parameters-to-submodules}
而如果你需要将其他非默认参数传递到子模块,就需要使用一些特殊手段手动指定这些非默认参数。
Nixpkgs 的模块系统提供了两种方式来传递非默认参数:
1. `nixpkgs.lib.nixosSystem` 函数的 `specialArgs` 参数
1. 在任一 Module 中使用 `_module.args` 这个 option 来传递参数
这两个参数的官方文档藏得很深,而且语焉不详、晦涩难懂。读者感兴趣的话我把链接放在这里:
1. `specialArgs`: NixOS Manual 跟 Nixpkgs Manual 中分别有与它有关的只言片语
- Nixpkgs Manual: [Module System - Nixpkgs]
- NixOS Manual:
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]
1. `_module.args`:
- NixOS Manual:
[Appendix A. Configuration Options](https://nixos.org/manual/nixos/stable/options#opt-_module.args)
- Source Code: [nixpkgs/nixos-23.11/lib/modules.nix - _module.args]
总之,`specialArgs` 与 `_module.args` 需要的值都是一个 attribute set它们的功能也相同
是将其 attribute set 中的所有参数传递到所有子模块中。这两者的区别在于:
1. 在任何 Module 中都能使用 `_module.args` 这个 option通过它互相传递参数这要比只能在
`nixpkgs.lib.nixosSystem` 函数中使用的 `specialArgs` 更灵活。
1. `_module.args` 是在 Module 中声明使用的,因此必须在所有 Modules 都已经被求值后,才能使
用它。这导致**如果你在 `imports = [ ... ];` 中使用 `_module.args` 传递的参数,会报错
`infinite recursion`,这种场景下你必须改用 `specialArgs` 才行**。
NixOS 社区比较推荐优先使用 `_module.args` 这个 options仅在无法使用 `_module.args` 时才改
`specialArgs`
假设你想将某个依赖项传递到子模块中使用,可以使用 `specialArgs` 参数将 `inputs` 传递到所有
子模块中:
```nix{13}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# 将所有 inputs 参数设为所有子模块的特殊参数,
# 这样就能直接在子模块中使用 inputs 中的所有依赖项了
specialArgs = { inherit inputs;};
modules = [
./configuration.nix
];
};
};
}
```
或者使用 `_module.args` 这个 option 也能达成同样的效果:
```nix{15}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
# 将所有 inputs 参数设为所有子模块的特殊参数,
# 这样就能直接在子模块中使用 inputs 中的所有依赖项了
_module.args = { inherit inputs; };
}
];
};
};
}
```
选择上述两种方式之一修改你的配置,然后在 `/etc/nixos/configuration.nix` 中就可以使用
`inputs` 这个参数了,模块系统会自动匹配到 `specialArgs` 中定义的 `inputs`,并将其注入到所
有需要该参数的子模块中:
```nix{3}
# Nix 会通过名称匹配,
# 自动将 specialArgs/_module.args 中的 inputs 注入到此函数的第三个参数
{ config, pkgs, inputs, ... }:
# 然后我们就能在这下面使用 inputs 这个参数了
{
# ......
}
```
下一节将演示如何使用 `specialArgs`/`_module.args` 来从其他 flake 来源安装系统软件。
## 从其他 flakes 安装系统软件 {#install-system-packages-from-other-flakes}
管系统最常见的需求就是装软件,我们在上一节已经见识过如何通过 `environment.systemPackages`
来安装 `pkgs` 中的包,这些包都来自官方的 nixpkgs 仓库。
现在我们学习下如何安装其他 flake 来源的软件包,这比直接从 nixpkgs 安装要灵活很多,最主要的
用途是安装 Nixpkgs 中还未添加或未更新的某软件的最新版本。
以 [helix](https://github.com/helix-editor/helix) 编辑器为例,这里演示下如何直接编译安装
helix 的 master 分支。
首先在 `flake.nix` 中添加 helix 这个 inputs 数据源:
```nix{6,12,18}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
# helix editor, use the master branch
helix.url = "github:helix-editor/helix/master";
};
outputs = inputs@{ self, nixpkgs, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs;};
modules = [
./configuration.nix
# 如下 Module 与前面的 `specialArgs` 参数功能完全一致
# 选择其中一种即可
# { _module.args = { inherit inputs; };}
];
};
};
}
```
接下来在 `configuration.nix` 中就能引用这个 flake input 数据源了:
```nix{1,10}
{ config, pkgs, inputs, ... }:
{
# 省略无关配置......
environment.systemPackages = with pkgs; [
git
vim
wget
curl
# 这里从 helix 这个 inputs 数据源安装了 helix 程序
inputs.helix.packages."${pkgs.system}".helix
];
# 省略其他配置......
}
```
改好后再 `sudo nixos-rebuild switch` 部署,就能安装好 Helix 程序了。这次部署用时会比以往长
挺多,因为 Nix 会从源码编译整个 Helix 程序。
部署完毕后,可直接在终端使用 `hx` 命令测试验证。
另外,如果你只是想尝试一下 Helix 的最新版本,再决定要不要真正地将它安装到系统里,有更简单
的办法,一行命令就行(但如前所述,源码编译会很费时间):
```bash
nix run github:helix-editor/helix/master
```
我们会在后面的 [新一代 Nix 命令行工具的使用](../other-usage-of-flakes/the-new-cli.md) 中详
细介绍 `nix run` 的用法。
## 使用其他 Flakes 包提供的功能
其实这才是 Flakes 最主要的功能,一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能——就
如同我们在写 TypeScript/Go/Rust 等程序时使用其他 Library 提供的功能一样。
上面使用 Helix 的官方 Flake 中提供的最新版本就是一个例子,其他更多的用例会在后面提到,这里
引用几个后面会讲的例子:
- [Getting Started with Home Manager](./start-using-home-manager.md): 这里引入了社区的
Home-Manager 作为依赖项,从而能直接使用该 Flake 提供的功能。
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): 这里引入了不同
版本的 Nixpkgs 作为依赖项,从而能很灵活地选用不同版本的 Nixpkgs 中的包。
## 其他 Flakes 学习资料
到此为止,我们已经学习了如何使用 Flakes 来配置 NixOS 系统。
如果你对 Flakes 还有更多的疑问,或者想深入学习,请直接参考如下官方/半官方的文档。
- Nix Flakes 的官方文档:
- [Nix flakes - Nix Manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake)
- [Flakes - nix.dev](https://nix.dev/concepts/flakes)
- Eelco Dolstra Nix 的创造者)的一系列关于 Flakes 的文章:
- [Nix Flakes, Part 1: An introduction and tutorial (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-05-25-flakes/)
- [Nix Flakes, Part 2: Evaluation caching (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-06-25-eval-cache/)
- [Nix Flakes, Part 3: Managing NixOS systems (Eelco Dolstra, 2020)](https://www.tweag.io/blog/2020-07-31-nixos-flakes/)
- 其他可能有用的文档:
- [Practical Nix Flakes](https://serokell.io/blog/practical-nix-flakes)
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix
[Module System - Nixpkgs]:
https://github.com/NixOS/nixpkgs/blob/23.11/doc/module-system/module-system.chapter.md
[nixpkgs/nixos-23.11/lib/modules.nix - _module.args]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/lib/modules.nix#L122-L184
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/doc/manual/development/option-types.section.md?plain=1#L237-L244

View File

@ -0,0 +1,171 @@
# `flake.nix` 配置详解 {#flake-nix-configuration-explained}
上面我们创建了一个 `flake.nix` 文件并通过它来管理系统配置,但你对它的结构还是一头雾水,下
面我们来详细解释一下这个文件的内容。
### 1. flake inputs
首先看看其中的 `inputs` 属性,它是一个 attribute set其中定义了这个 flake 的所有依赖项,
这些依赖项会在被拉取后,作为参数传递给 `outputs` 函数:
```nix{2-5,7}
{
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# 省略掉前面的配置......
};
}
```
`inputs` 中的每一项依赖有许多类型与定义方式,可以是另一个 flake也可以是一个普通的 Git 仓
库,又或者一个本地路径。
[Flakes 的其他玩法 - Flake 的 inputs](../other-usage-of-flakes/inputs.md) 中详细介绍了常见
的依赖项类型与定义方式。
这里我们只定义了 `nixpkgs` 这一个依赖项,使用的是 flake 中最常见的引用方式,即
`github:owner/name/reference`,这里的 `reference` 可以是分支名、commit-id 或 tag。
`nixpkgs``inputs` 中被定义后,就可以在后面的 `outputs` 函数的参数中使用此依赖项中的内
容了,我们的示例中正是这么干的。
### 2. flake outputs
再来看看 `outputs`,它是一个以 `inputs` 中的依赖项为参数的函数,函数的返回值是一个
attribute set这个返回的 attribute set 即为该 flake 的构建结果:
```nix{10-18}
{
description = "A simple NixOS flake";
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# hostname 为 my-nixos 的主机会使用这个配置
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
flake 有很多的用途,也可以有很多不同类型的
outputs[Flake 的 outputs](../other-usage-of-flakes/outputs.md) 一节有更详细的介绍。这里
我们只用到了 `nixosConfigurations` 这一类型的 outputs它用于配置 NixOS 系统。
在我们运行 `sudo nixos-rebuild switch` 命令时,它会从 `/etc/nixos/flake.nix``outputs`
函数返回值中查找 `nixosConfigurations.my-nixos` (其中的 `my-nixos` 将会是你当前系统的
hostname这一属性并使用其中的定义来配置你的 NixOS 系统。
实际我们也可以自定义 flake 的位置与 NixOS 配置的名称,而不是使用默认值。只需要在
`nixos-rebuild` 命令后面添加 `--flake` 参数即可,一个例子:
```nix
sudo nixos-rebuild switch --flake /path/to/your/flake#your-hostname
```
上述命令中的 `--flake /path/to/your/flake#your-hostname` 参数简要说明如下:
1. `/path/to/your/flake` 为目标 flake 的位置,默认会使用 `/etc/nixos/` 这个路径。
2. `#` 是一个分隔符,其后的 `your-hostname` 则是 NixOS 配置的名称。`nixos-rebuild` 默认会
以你当前系统的 hostname 为配置名称进行查找。
你甚至能直接引用一个远程的 GitHub 仓库作为你的 flake 来源,示例如下:
```nix
sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
```
### 3. `outputs` 函数的特殊参数 `self` {#special-parameter-self-of-outputs-function}
虽然我们前面并未提到,但是前面的所有示例代码中,`outputs` 函数都还有一个特殊的参数
`self`,这里我们简单介绍一下它的作用。
[nix flake - Nix Manual] 对其的描述是:
> The special input named `self` refers to the outputs and source tree of this flake.
所以说 `self` 是当前 flake 的 `outputs` 函数的返回值,同时也是当前 flake 源码的文件夹路径
source tree
这里我们并未使用到 `self` 这个参数,在后面一些更复杂的例子(或者你网上搜
到的一些配置)中,我们会看到 `self` 的用法。
> 注意:你可能会在一些代码中看到,有人会使用 `self.outputs` 来引用当前 flake 的输出,这
> 确实是可行的,但 Nix Manual 并未对其做任何说明,属于是 flake 的内部实现细节,不建议在
> 你自己的代码中使用!
### 4. `nixpkgs.lib.nixosSystem` 函数的简单介绍 {#simple-introduction-to-nixpkgs-lib-nixos-system}
**一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能**。
默认情况下,一个 flake 会在其每个依赖项(即 `inputs` 中的每一项)的根目录下寻找
`flake.nix` 文件并**懒惰求值**lazy evaluation它们的 `outputs` 函数,接着将这些函数返回
的 attribute sets 作为参数传递给它自身的 `outputs` 函数,这样我们就能在当前 flake 中使用它
所依赖的其他 flakes 提供的功能了。
更精确地说,对每个依赖项的 `outputs` 函数的求值都是懒惰lazy也就是说一个 flake 的
`outputs` 函数只有在被真正使用到的时候才会被求值,这样就能避免不必要的计算,从而提高效率。
上面的描述可能有点绕,我们还是结合本节中使用的 `flake.nix` 示例来看看这个过程。我们的
`flake.nix` 声明了 `inputs.nixpkgs` 这个依赖项,因此 [nixpkgs/flake.nix] 会在我们执行
`sudo nixos-rebuild switch` 这个命令时被求值。从 Nixpkgs 仓库的源码中能看到它的 flake
outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `lib` 属性中的 `nixosSystem`
个函数来配置我们的 NixOS 系统:
```nix{8-13}
{
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
`nixpkgs.lib.nixosSystem` 后面跟的 attribute set 就是该函数的参数,我们这里只设置了两个参
数:
1. `system`: 这个很好懂,就是系统架构参数。
2. `modules`: 此函数是一个 modules 的列表NixOS 的实际系统配置都定义在这些 modules 中。
`/etc/nixos/configuration.nix` 这个配置文件本身就是一个 Nixpkgs Module因此可以直接将其添
加到 `modules` 列表中使用。
新手阶段了解这些就足够了,探究 `nixpkgs.lib.nixosSystem` 函数的具体实现需要对 Nixpkgs 的模
块系统有一定的了解。读者可以在学习了
[模块化 NixOS 配置](./modularize-the-configuration.md) 一节后,再回过头来从
[nixpkgs/flake.nix] 中找到 `nixpkgs.lib.nixosSystem` 的定义,跟踪它的源码,研究其实现方
式。
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix
[Module System - Nixpkgs]:
https://github.com/NixOS/nixpkgs/blob/23.11/doc/module-system/module-system.chapter.md
[nixpkgs/nixos-23.11/lib/modules.nix - _module.args]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/lib/modules.nix#L122-L184
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]:
https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/doc/manual/development/option-types.section.md?plain=1#L237-L244

View File

@ -1,11 +1,14 @@
# 使用 Flakes 来管理你的 NixOS
## 启用 NixOS 的 Flakes 支持 {#enable-nix-flakes}
与 NixOS 当前默认的配置方式相比Flakes 提供了更好的可复现性,同时它清晰的包结构定义原生支
持了以其他 Git 仓库为依赖,便于代码分享,因此本书更建议使用 Flakes 来管理系统配置。
目前 Flakes 作为一个实验特性,仍未被默认启用,我们需要手动修改
本节我们介绍如何使用 Flakes 来管理 NixOS 系统配置,**阅读本节内容不需要提前对 Flakes 有任
何了解**。
## 启用 NixOS 的 Flakes 支持 {#enable-nix-flakes}
目前 Flakes 作为一个实验特性,仍未被默认启用,因此我们需要先手动修改
`/etc/nixos/configuration.nix` 文件,启用 Flakes 特性以及配套的船新 nix 命令行工具:
```nix{12,15}
@ -103,9 +106,14 @@ cat flake.nix
- `/etc/nixos/hardware-configuration.nix`: 这是系统硬件配置文件,由 NixOS 生成,描述了系统
的硬件信息
到这里为止, `/etc/nixos/flake.nix` 仅仅是 `/etc/nixos/configuration.nix` 的一个 thin
wrapper它自身并没有提供任何新的功能也没有引入任何破坏性的变更。在本书后面的内容中
们会逐渐看到这样一个 wrapper 带来了哪些好处。
## 总结 {#conclusion}
本节中我们添加了一个非常简单的配置文件 `/etc/nixos/flake.nix`,它仅仅是
`/etc/nixos/configuration.nix` 的一个 thin wrapper它自身并没有提供任何新的功能也没有引
入任何破坏性的变更。
在本书后面的内容中,我们会学习了解 `flake.nix` 的结构与功能,并逐渐看到这样一个 wrapper 能
为我们带来哪些好处。
> 注意:**本书描述的配置管理方式并非「Everything in a single file」更推荐将配置内容分门
> 别类地存放到不同的 nix 文件中**,然后在 `flake.nix``modules` 参数列表中引入这些配置
@ -114,384 +122,8 @@ wrapper它自身并没有提供任何新的功能也没有引入任何破
> 你的 NixOS 配置,[其他实用技巧 - 使用 Git 管理 NixOS 配置](./other-useful-tips.md) 将会
> 介绍几种使用 Git 管理 NixOS 配置的最佳实践。
## `flake.nix` 配置详解 {#flake-nix-configuration-explained}
上面我们创建了一个 `flake.nix` 文件并通过它来管理系统配置,但你对它的结构还是一头雾水,下
面我们来详细解释一下这个文件的内容。
### 1. flake inputs
首先看看其中的 `inputs` 属性,它是一个 attribute set其中定义了这个 flake 的所有依赖项,
这些依赖项会在被拉取后,作为参数传递给 `outputs` 函数:
```nix{2-5,7}
{
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
# 省略掉前面的配置......
};
}
```
`inputs` 中的每一项依赖有许多类型与定义方式,可以是另一个 flake也可以是一个普通的 Git 仓
库,又或者一个本地路径。
[Flakes 的其他玩法 - Flake 的 inputs](../other-usage-of-flakes/inputs.md) 中详细介绍了常见
的依赖项类型与定义方式。
这里我们只定义了 `nixpkgs` 这一个依赖项,使用的是 flake 中最常见的引用方式,即
`github:owner/name/reference`,这里的 `reference` 可以是分支名、commit-id 或 tag。
`nixpkgs``inputs` 中被定义后,就可以在后面的 `outputs` 函数的参数中使用此依赖项中的内
容了,我们的示例中正是这么干的。
### 2. flake outputs
再来看看 `outputs`,它是一个以 `inputs` 中的依赖项为参数的函数,函数的返回值是一个
attribute set这个返回的 attribute set 即为该 flake 的构建结果:
```nix{10-18}
{
description = "A simple NixOS flake";
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
# 这里的 `self` 是个特殊参数,它指向 `outputs` 函数返回的 attribute set 自身,即自引用
outputs = { self, nixpkgs, ... }@inputs: {
# hostname 为 my-nixos 的主机会使用这个配置
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
flake 有很多的用途,也可以有很多不同类型的
outputs[Flake 的 outputs](../other-usage-of-flakes/outputs.md) 一节有更详细的介绍。这里
我们只用到了 `nixosConfigurations` 这一类型的 outputs它用于配置 NixOS 系统。
在我们运行 `sudo nixos-rebuild switch` 命令时,它会从 `/etc/nixos/flake.nix``outputs`
函数返回值中查找 `nixosConfigurations.my-nixos` (其中的 `my-nixos` 将会是你当前系统的
hostname这一属性并使用其中的定义来配置你的 NixOS 系统。
实际我们也可以自定义 flake 的位置与 NixOS 配置的名称,而不是使用默认值。只需要在
`nixos-rebuild` 命令后面添加 `--flake` 参数即可,一个例子:
```nix
sudo nixos-rebuild switch --flake /path/to/your/flake#your-hostname
```
上述命令中的 `--flake /path/to/your/flake#your-hostname` 参数简要说明如下:
1. `/path/to/your/flake` 为目标 flake 的位置,默认会使用 `/etc/nixos/` 这个路径。
2. `#` 是一个分隔符,其后的 `your-hostname` 则是 NixOS 配置的名称。`nixos-rebuild` 默认会
以你当前系统的 hostname 为配置名称进行查找。
你甚至能直接引用一个远程的 GitHub 仓库作为你的 flake 来源,示例如下:
```nix
sudo nixos-rebuild switch --flake github:owner/repo#your-hostname
```
### 3. `nixpkgs.lib.nixosSystem` 函数的简单介绍 {#simple-introduction-to-nixpkgs-lib-nixos-system}
**一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能**。
默认情况下,一个 flake 会在其每个依赖项(即 `inputs` 中的每一项)的根目录下寻找
`flake.nix` 文件并**懒惰求值**lazy evaluation它们的 `outputs` 函数,接着将这些函数返回
的 attribute sets 作为参数传递给它自身的 `outputs` 函数,这样我们就能在当前 flake 中使用它
所依赖的其他 flakes 提供的功能了。
更精确地说,对每个依赖项的 `outputs` 函数的求值都是懒惰lazy也就是说一个 flake 的
`outputs` 函数只有在被真正使用到的时候才会被求值,这样就能避免不必要的计算,从而提高效率。
上面的描述可能有点绕,我们还是结合本节中使用的 `flake.nix` 示例来看看这个过程。我们的
`flake.nix` 声明了 `inputs.nixpkgs` 这个依赖项,因此 [nixpkgs/flake.nix] 会在我们执行
`sudo nixos-rebuild switch` 这个命令时被求值。从 Nixpkgs 仓库的源码中能看到它的 flake
outputs 定义中有返回 `lib` 这个属性,我们的例子中就使用了 `lib` 属性中的 `nixosSystem`
个函数来配置我们的 NixOS 系统:
```nix{8-13}
{
inputs = {
# NixOS 官方软件源,这里使用 nixos-23.11 分支
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
];
};
};
}
```
`nixpkgs.lib.nixosSystem` 后面跟的 attribute set 就是该函数的参数,我们这里只设置了两个参
数:
1. `system`: 这个很好懂,就是系统架构参数。
2. `modules`: 此函数是一个 modules 的列表NixOS 的实际系统配置都定义在这些 modules 中。
`/etc/nixos/configuration.nix` 这个配置文件本身就是一个 Nixpkgs Module因此可以直接将其添
加到 `modules` 列表中使用。
新手阶段了解这些就足够了,探究 `nixpkgs.lib.nixosSystem` 函数的具体实现需要对 Nixpkgs 的模
块系统有一定的了解。读者可以在学习了
[模块化 NixOS 配置](./modularize-the-configuration.md) 一节后,再回过头来从
[nixpkgs/flake.nix] 中找到 `nixpkgs.lib.nixosSystem` 的定义,跟踪它的源码,研究其实现方
式。
## Nixpkgs Module 结构的简单介绍 {#simple-introduction-to-nixpkgs-module-structure}
> 在后面的 [模块化 NixOS 配置](./modularize-the-configuration.md) 一节中会详细介绍这套模块
> 系统的工作方式,这里只介绍些基础知识。
为什么 `/etc/nixos/configuration.nix` 这个配置文件会符合 Nixpkgs Module 定义,从而能直接在
`flake.nix` 中引用它呢?可能会有读者觉得这有点出乎意料。
这实际是因为 Nixpkgs 中包含了大量 NixOS 的实现源码,这些源码大都使用 Nix 语言编写。为了编
写维护如此多的 Nix 代码,并且使用户能灵活地自定义其 NixOS 系统的各项功能,就必须要有一套
Nix 代码的模块化系统。
这套 Nix 代码的模块系统的实现也同样在 Nixpkgs 仓库中,它主要被用于 NixOS 系统配置的模块
化,但也有其他的应用,比如 nix-darwin 跟 home-manager 都大量使用了这套模块系统。
既然 NixOS 是基于这套模块系统构建的,那它的配置文件(包括 `/etc/nixos/configuration.nix`
是一个Nixpkgs Module也就显得非常自然了。
在学习后面的内容之前,我们需要先简单了解下这套模块系统的工作方式。
一个简化的 Nixpkgs Module 结构如下:
```nix
{lib, config, options, pkgs, ...}:
{
# 导入其他 Modules
imports = [
# ......
# ./xxx.nix
];
for.bar.enable = true;
# other options declarations
# ...
}
```
可以看到它的定义实际是一个 Nix 函数,该函数有 5 个**由模块系统自动生成、自动注入、无需额外
声明的参数**
1. `lib`: **nixpkgs 自带的函数库,提供了许多操作 Nix 表达式的实用函数**
- 详见 <https://nixos.org/manual/nixpkgs/stable/#id-1.4>
2. `config`: 包含了当前环境中所有 option 的值,在后面学习模块系统时会大量使用它
3. `options`: 当前环境中所有 Modules 中定义的所有 options 的集合
4. `pkgs`: **一个包含所有 nixpkgs 包的集合,它也提供了许多相关的工具函数**
- 入门阶段可以认为它的默认值为 `nixpkgs.legacyPackages."${system}"`,可通过
`nixpkgs.pkgs` 这个 option 来自定义 `pkgs` 的值
5. `modulesPath`: 一个只在 NixOS 中可用的参数,是一个 Path指向
[nixpkgs/nixos/modules](https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/modules)
- 它在
[nixpkgs/nixos/lib/eval-config-minimal.nix#L43](https://github.com/NixOS/nixpkgs/blob/nixos-23.11/nixos/lib/eval-config-minimal.nix#L43)
中被定义
- 通常被用于导入一些额外的 NixOS 模块NixOS 自动生成的 `hardware-configuration.nix`
基本都能看到它
## 传递非默认参数到模块系统中 {#pass-non-default-parameters-to-submodules}
而如果你需要将其他非默认参数传递到子模块,就需要使用一些特殊手段手动指定这些非默认参数。
Nixpkgs 的模块系统提供了两种方式来传递非默认参数:
1. `nixpkgs.lib.nixosSystem` 函数的 `specialArgs` 参数
1. 在任一 Module 中使用 `_module.args` 这个 option 来传递参数
这两个参数的官方文档藏得很深,而且语焉不详、晦涩难懂。读者感兴趣的话我把链接放在这里:
1. `specialArgs`: NixOS Manual 跟 Nixpkgs Manual 中分别有与它有关的只言片语
- Nixpkgs Manual: [Module System - Nixpkgs]
- NixOS Manual:
[nixpkgs/nixos-23.11/nixos/doc/manual/development/option-types.section.md#L237-L244]
1. `_module.args`:
- NixOS Manual:
[Appendix A. Configuration Options](https://nixos.org/manual/nixos/stable/options#opt-_module.args)
- Source Code: [nixpkgs/nixos-23.11/lib/modules.nix - _module.args]
总之,`specialArgs` 与 `_module.args` 需要的值都是一个 attribute set它们的功能也相同
是将其 attribute set 中的所有参数传递到所有子模块中。这两者的区别在于:
1. 在任何 Module 中都能使用 `_module.args` 这个 option通过它互相传递参数这要比只能在
`nixpkgs.lib.nixosSystem` 函数中使用的 `specialArgs` 更灵活。
1. `_module.args` 是在 Module 中声明使用的,因此必须在所有 Modules 都已经被求值后,才能使
用它。这导致**如果你在 `imports = [ ... ];` 中使用 `_module.args` 传递的参数,会报错
`infinite recursion`,这种场景下你必须改用 `specialArgs` 才行**。
NixOS 社区比较推荐优先使用 `_module.args` 这个 options仅在无法使用 `_module.args` 时才改
`specialArgs`
假设你想将某个依赖项传递到子模块中使用,可以使用 `specialArgs` 参数将 `inputs` 传递到所有
子模块中:
```nix{13}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# 将所有 inputs 参数设为所有子模块的特殊参数,
# 这样就能直接在子模块中使用 inputs 中的所有依赖项了
specialArgs = { inherit inputs;};
modules = [
./configuration.nix
];
};
};
}
```
或者使用 `_module.args` 这个 option 也能达成同样的效果:
```nix{15}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
another-input.url = "github:username/repo-name/branch-name";
};
outputs = inputs@{ self, nixpkgs, another-input, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
# 将所有 inputs 参数设为所有子模块的特殊参数,
# 这样就能直接在子模块中使用 inputs 中的所有依赖项了
_module.args = { inherit inputs; };
}
];
};
};
}
```
选择上述两种方式之一修改你的配置,然后在 `/etc/nixos/configuration.nix` 中就可以使用
`inputs` 这个参数了,模块系统会自动匹配到 `specialArgs` 中定义的 `inputs`,并将其注入到所
有需要该参数的子模块中:
```nix{3}
# Nix 会通过名称匹配,
# 自动将 specialArgs/_module.args 中的 inputs 注入到此函数的第三个参数
{ config, pkgs, inputs, ... }:
# 然后我们就能在这下面使用 inputs 这个参数了
{
# ......
}
```
下一节将演示如何使用 `specialArgs`/`_module.args` 来从其他 flake 来源安装系统软件。
## 从其他 flake 来源安装系统软件 {#install-system-packages-from-other-flakes}
管系统最常见的需求就是装软件,我们在上一节已经见识过如何通过 `environment.systemPackages`
来安装 `pkgs` 中的包,这些包都来自官方的 nixpkgs 仓库。
现在我们学习下如何安装其他 flake 来源的软件包,这比直接从 nixpkgs 安装要灵活很多,最主要的
用途是安装 Nixpkgs 中还未添加或未更新的某软件的最新版本。
以 [helix](https://github.com/helix-editor/helix) 编辑器为例,这里演示下如何直接编译安装
helix 的 master 分支。
首先在 `flake.nix` 中添加 helix 这个 inputs 数据源:
```nix{6,12,18}
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
# helix editor, use the master branch
helix.url = "github:helix-editor/helix/master";
};
outputs = inputs@{ self, nixpkgs, ... }: {
nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs;};
modules = [
./configuration.nix
# 如下 Module 与前面的 `specialArgs` 参数功能完全一致
# 选择其中一种即可
# { _module.args = { inherit inputs; };}
];
};
};
}
```
接下来在 `configuration.nix` 中就能引用这个 flake input 数据源了:
```nix{1,10}
{ config, pkgs, inputs, ... }:
{
# 省略无关配置......
environment.systemPackages = with pkgs; [
git
vim
wget
curl
# 这里从 helix 这个 inputs 数据源安装了 helix 程序
inputs.helix.packages."${pkgs.system}".helix
];
# 省略其他配置......
}
```
改好后再 `sudo nixos-rebuild switch` 部署,就能安装好 Helix 程序了。这次部署用时会比以往长
挺多,因为 Nix 会从源码编译整个 Helix 程序。
部署完毕后,可直接在终端使用 `hx` 命令测试验证。
另外,如果你只是想尝试一下 Helix 的最新版本,再决定要不要真正地将它安装到系统里,有更简单
的办法,一行命令就行(但如前所述,源码编译会很费时间):
```bash
nix run github:helix-editor/helix/master
```
我们会在后面的 [新一代 Nix 命令行工具的使用](../other-usage-of-flakes/the-new-cli.md) 中详
细介绍 `nix run` 的用法。
## 使用其他 Flakes 包提供的功能
其实这才是 Flakes 最主要的功能,一个 Flake 可以依赖其他 Flakes从而使用它们提供的功能——就
如同我们在写 TypeScript/Go/Rust 等程序时使用其他 Library 提供的功能一样。
上面使用 Helix 的官方 Flake 中提供的最新版本就是一个例子,其他更多的用例会在后面提到,这里
引用几个后面会讲的例子:
- [Getting Started with Home Manager](./start-using-home-manager.md): 这里引入了社区的
Home-Manager 作为依赖项,从而能直接使用该 Flake 提供的功能。
- [Downgrading or Upgrading Packages](./downgrade-or-upgrade-packages.md): 这里引入了不同
版本的 Nixpkgs 作为依赖项,从而能很灵活地选用不同版本的 Nixpkgs 中的包。
[nix flake - Nix Manual]:
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#flake-inputs
[nixpkgs/flake.nix]: https://github.com/NixOS/nixpkgs/tree/nixos-23.11/flake.nix
[nixpkgs/nixos/lib/eval-config.nix]:
https://github.com/NixOS/nixpkgs/tree/nixos-23.11/nixos/lib/eval-config.nix

View File

@ -6,8 +6,8 @@ Nix 语言是 Nix 包管理器的基础,要想玩得转 NixOS 与 Nix Flakes
Nix 是一门比较简单的函数式语言,在已有一定编程基础的情况下,过一遍这些语法用时应该在 2 个
小时以内。
NixOS-CN 社区已经有了一份不错的 Nix 语言教程,我不打算重复造轮子,请直接阅
[**Nix 语言概览 - NixOS-CN**](https://nixos-cn.org/tutorials/lang) 来快速入门。
NixOS-CN 社区已经有了一份不错的 Nix 语言教程,我不打算重复造轮子,请直接阅
[**Nix 语言概览 - NixOS-CN**](https://nixos-cn.org/tutorials/lang) 来快速入门。
先把语法过一遍,有个大概的印象就行,后面需要用到时再边用边复习语法知识。

View File

@ -1,7 +1,7 @@
{
"type": "module",
"devDependencies": {
"vitepress": "1.0.0-rc.44",
"vitepress": "1.0.2",
"vitepress-export-pdf": "1.0.0-beta.0"
},
"scripts": {
@ -11,6 +11,7 @@
"export-pdf": "press-export-pdf export ./docs --outFile ./nixos-and-flakes-book.pdf"
},
"dependencies": {
"markdown-it-cjk-breaks": "^2.0.0",
"markdown-it-footnote": "^4.0.0",
"sitemap-ts": "^1.6.1",
"vitepress-plugin-comment-with-giscus": "^1.1.12"

1034
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff