mirror of
https://github.com/ryan4yin/nixos-and-flakes-book.git
synced 2025-01-03 20:48:56 +01:00
352 lines
14 KiB
Markdown
352 lines
14 KiB
Markdown
|
||
Nix is powerful and flexible, it provides a lot of ways to do things, making it difficult to find the most suitable way to do your job.
|
||
Here are some best practices that I've learned from the community, hope it can help you.
|
||
|
||
## 1. Run downloaded binaries on NixOS
|
||
|
||
NixOS does not follow the FHS standard, so the binaries you download from the Internet will not likely work on NixOS. But there are some ways to make it work.
|
||
|
||
Here is a detailed guide which provides 10 ways to run downloaded binaries on NixOS: [Different methods to run a non-nixos executable on Nixos](https://unix.stackexchange.com/questions/522822/different-methods-to-run-a-non-nixos-executable-on-nixos), I recommend you to read it.
|
||
|
||
Among these methods, I prefer creating a FHS environment to run the binary, which is very convenient and easy to use.
|
||
|
||
To create such an environment, add the following code to one of your nix modules:
|
||
|
||
```nix
|
||
{ config, pkgs, lib, ... }:
|
||
|
||
{
|
||
# ......omit many configurations
|
||
|
||
environment.systemPackages = with pkgs; [
|
||
# ......omit many packages
|
||
|
||
# create a fhs environment by command `fhs`, so we can run non-nixos packages in nixos!
|
||
(pkgs.buildFHSUserEnv (base // {
|
||
name = "fhs";
|
||
targetPkgs = pkgs: (
|
||
# pkgs.buildFHSUserEnv provides only a minimal fhs environment,
|
||
# it lacks many basic packages needed by most softwares.
|
||
# so we need to add them manually.
|
||
#
|
||
# pkgs.appimageTools provides basic packages needed by most softwares.
|
||
(pkgs.appimageTools.defaultFhsEnvArgs.targetPkgs pkgs) ++ with pkgs; [
|
||
pkg-config
|
||
ncurses
|
||
# feel free to add more packages here, if you need
|
||
]
|
||
);
|
||
profile = "export FHS=1";
|
||
runScript = "bash";
|
||
extraOutputsToInstall = ["dev"];
|
||
}))
|
||
];
|
||
|
||
# ......omit many configurations
|
||
}
|
||
```
|
||
|
||
after applying the updated configuration, you can run `fhs` to enter the FHS environment, and then run the binary you downloaded, e.g.
|
||
|
||
```shell
|
||
# Activating FHS drops me in a shell which looks like a "normal" Linux
|
||
$ fhs
|
||
# check what we have in /usr/bin
|
||
(fhs) $ ls /usr/bin
|
||
# try to run a non-nixos binary downloaded from the Internet
|
||
(fhs) $ ./bin/code
|
||
```
|
||
|
||
## 2. check the source code and debug with `nix repl`
|
||
|
||
We've used `nix repl '<nixpkgs>'` many times to check the source code in this guide, it's really a powerful tool to help us understand how things work in Nix.
|
||
|
||
Better take a look at the help message of `nix repl`:
|
||
|
||
```
|
||
› nix repl -f '<nixpkgs>'
|
||
Welcome to Nix 2.13.3. Type :? for help.
|
||
|
||
Loading installable ''...
|
||
Added 17755 variables.
|
||
nix-repl> :?
|
||
The following commands are available:
|
||
|
||
<expr> Evaluate and print expression
|
||
<x> = <expr> Bind expression to variable
|
||
:a <expr> Add attributes from resulting set to scope
|
||
:b <expr> Build a derivation
|
||
:bl <expr> Build a derivation, creating GC roots in the working directory
|
||
:e <expr> Open package or function in $EDITOR
|
||
:i <expr> Build derivation, then install result into current profile
|
||
:l <path> Load Nix expression and add it to scope
|
||
:lf <ref> Load Nix flake and add it to scope
|
||
:p <expr> Evaluate and print expression recursively
|
||
:q Exit nix-repl
|
||
:r Reload all files
|
||
:sh <expr> Build dependencies of derivation, then start nix-shell
|
||
:t <expr> Describe result of evaluation
|
||
:u <expr> Build derivation, then start nix-shell
|
||
:doc <expr> Show documentation of a builtin function
|
||
:log <expr> Show logs for a derivation
|
||
:te [bool] Enable, disable or toggle showing traces for errors
|
||
```
|
||
|
||
Some expressions that I use frequently: `:lf <ref>`, `:e <expr>`.
|
||
|
||
`:e <expr>` is very intuitive, so I won't repeat it. let's take a look at `:lf <ref>`:
|
||
|
||
```nix
|
||
# cd into my nix-config repo(you should replace it with your own nix-config repo)
|
||
› cd ~/nix-config/
|
||
|
||
# enter nix repl
|
||
› nix repl
|
||
Welcome to Nix 2.13.3. Type :? for help.
|
||
|
||
# load my nix flake and add it to scope
|
||
nix-repl> :lf .
|
||
Added 16 variables.
|
||
|
||
# press <TAB> to see what we have in scope
|
||
nix-repl><TAB>
|
||
# ......omit some outputs
|
||
__isInt nixosConfigurations
|
||
__isList null
|
||
__isPath outPath
|
||
__isString outputs
|
||
__langVersion packages
|
||
# ......omit some outputs
|
||
|
||
|
||
# check what's in inputs
|
||
nix-repl> inputs.<TAB>
|
||
inputs.agenix inputs.nixpkgs
|
||
inputs.darwin inputs.nixpkgs-darwin
|
||
inputs.home-manager inputs.nixpkgs-unstable
|
||
inputs.hyprland inputs.nixpkgs-wayland
|
||
inputs.nil
|
||
inputs.nixos-generators
|
||
|
||
# check what's in inputs.nil
|
||
nix-repl> inputs.nil.packages.
|
||
inputs.nil.packages.aarch64-darwin
|
||
inputs.nil.packages.aarch64-linux
|
||
inputs.nil.packages.x86_64-darwin
|
||
inputs.nil.packages.x86_64-linux
|
||
|
||
# check the outputs of my nix flake
|
||
nix-repl> outputs.nixosConfigurations.<TAB>
|
||
outputs.nixosConfigurations.ai
|
||
outputs.nixosConfigurations.aquamarine
|
||
outputs.nixosConfigurations.kana
|
||
outputs.nixosConfigurations.ruby
|
||
|
||
nix-repl> outputs.nixosConfigurations.ai.<TAB>
|
||
outputs.nixosConfigurations.ai._module
|
||
outputs.nixosConfigurations.ai._type
|
||
outputs.nixosConfigurations.ai.class
|
||
outputs.nixosConfigurations.ai.config
|
||
outputs.nixosConfigurations.ai.extendModules
|
||
outputs.nixosConfigurations.ai.extraArgs
|
||
outputs.nixosConfigurations.ai.options
|
||
outputs.nixosConfigurations.ai.pkgs
|
||
outputs.nixosConfigurations.ai.type
|
||
|
||
nix-repl> outputs.nixosConfigurations.ai.config.
|
||
outputs.nixosConfigurations.ai.config.age
|
||
outputs.nixosConfigurations.ai.config.appstream
|
||
outputs.nixosConfigurations.ai.config.assertions
|
||
outputs.nixosConfigurations.ai.config.boot
|
||
outputs.nixosConfigurations.ai.config.console
|
||
outputs.nixosConfigurations.ai.config.containers
|
||
# ......omit other outputs
|
||
|
||
nix-repl> outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.<TAB>
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.activation
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.activationPackage
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.emptyActivationPath
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.enableDebugInfo
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.enableNixpkgsReleaseCheck
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.extraActivationPath
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.extraBuilderCommands
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.extraOutputsToInstall
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.extraProfileCommands
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file
|
||
# ......omit other outputs
|
||
|
||
|
||
nix-repl> outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.<TAB>
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.BROWSER
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.DELTA_PAGER
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.EDITOR
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.GLFW_IM_MODULE
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.MANPAGER
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.QT_IM_MODULE
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.TERM
|
||
# ......omit other outputs
|
||
|
||
# check the value of `TERM`
|
||
nix-repl> outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.sessionVariables.TERM
|
||
"xterm-256color"
|
||
|
||
|
||
# check all files defined by `home.file`
|
||
nix-repl> outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file.<TAB>
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..bash_profile
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..bashrc
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/fcitx5/profile
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/fcitx5/profile-bak
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/config
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/i3blocks.conf
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/keybindings
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/layouts
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/scripts
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/i3/wallpaper.png
|
||
outputs.nixosConfigurations.ai.config.home-manager.users.ryan.home.file..config/rofi
|
||
#......
|
||
```
|
||
|
||
As you can see, we can check every attribute of my flake in the REPL after loading it, which is very convenient for debugging.
|
||
|
||
## 3. Remote deployment
|
||
|
||
Some tools like [NixOps](https://github.com/NixOS/nixops), [deploy-rs](https://github.com/serokell/deploy-rs), and [colmena](https://github.com/zhaofengli/colmena) can all be used to deploy NixOS configuration to remote hosts, but they are all too complicated for me, so skip them all.
|
||
|
||
`nixos-rebuild`, the tool we use to deploy NixOS configuration, also supports remote deployment through ssh protocol, which is very convenient and simple.
|
||
|
||
But `nixos-rebuild` does not support deploying with password authentication, so to use it for remote deployment, we need to:
|
||
|
||
1. Configure ssh public key authentication for the remote hosts.
|
||
2. To avoid sudo password verification failures, we need to use the `root` user to deploy, or grant the user sudo permission without password verification.
|
||
1. related issue: <https://github.com/NixOS/nixpkgs/issues/118655>
|
||
|
||
After the above configuration is completed, we can deploy the configuration to the server through the following command:
|
||
|
||
```bash
|
||
# 1. add the ssh key to ssh-agent first
|
||
ssh-add ~/.ssh/ai-idols
|
||
|
||
# 2. deploy the configuration to the remote host, using the ssh key we added in step 1
|
||
# and the username defaults to `$USER`, it's `ryan` in my case.
|
||
nixos-rebuild --flake .#aquamarine --target-host 192.168.4.1 --build-host 192.168.4.1 switch --use-remote-sudo --verbose
|
||
```
|
||
|
||
The commands above will build & deploy the configuration to aquamarine, the build process will be executed on aquamarine too,
|
||
and the `--use-remote-sudo` option indicates that we need to use sudo permission on the remote server to deploy the configuration.
|
||
|
||
If you want to build the configuration locally and deploy it to the remote server, just replace `--build-host aquamarinr` with `--build-host localhost`.
|
||
|
||
Instead of use ip address directly, we can also define some host aliases in `~/.ssh/config` or `/etc/ssh/ssh_config`, for example:
|
||
|
||
> ssh's config can be generated completely through Nix configuration, and this task is left to you.
|
||
|
||
```bash
|
||
› cat ~/.ssh/config
|
||
|
||
# ......
|
||
|
||
Host ai
|
||
HostName 192.168.5.100
|
||
Port 22
|
||
|
||
Host aquamarine
|
||
HostName 192.168.5.101
|
||
Port 22
|
||
|
||
Host ruby
|
||
HostName 192.168.5.102
|
||
Port 22
|
||
|
||
Host kana
|
||
HostName 192.168.5.103
|
||
Port 22
|
||
```
|
||
|
||
Then we can use the host alias to deploy the configuration:
|
||
|
||
```bash
|
||
nixos-rebuild --flake .#aquamarine --target-host aquamarine --build-host aquamarine switch --use-remote-sudo --verbose
|
||
```
|
||
|
||
## 4. Work with Makefile
|
||
|
||
> NOTE: Makefile's target name should not be the same as one of the file or directory in the current directory, otherwise the target will not be executed!
|
||
|
||
I use Makefile to manage the commands of my flake, which is very convenient.
|
||
|
||
For example, my Makefile looks like this:
|
||
|
||
```makefile
|
||
#
|
||
# NOTE: Makefile's target name should not be the same as one of the file or directory in the current directory,
|
||
# otherwise the target will not be executed!
|
||
#
|
||
|
||
|
||
############################################################################
|
||
#
|
||
# Nix commands related to the local machine
|
||
#
|
||
############################################################################
|
||
|
||
deploy:
|
||
nixos-rebuild switch --flake . --use-remote-sudo
|
||
|
||
debug:
|
||
nixos-rebuild switch --flake . --use-remote-sudo --show-trace --verbose
|
||
|
||
update:
|
||
nix flake update
|
||
|
||
history:
|
||
nix profile history --profile /nix/var/nix/profiles/system
|
||
|
||
gc:
|
||
# remove all generations older than 7 days
|
||
sudo nix profile wipe-history --profile /nix/var/nix/profiles/system --older-than 7d
|
||
|
||
# garbage collect all unused nix store entries
|
||
sudo nix store gc --debug
|
||
|
||
############################################################################
|
||
#
|
||
# Idols, Commands related to my remote distributed building cluster
|
||
#
|
||
############################################################################
|
||
|
||
add-idols-ssh-key:
|
||
ssh-add ~/.ssh/ai-idols
|
||
|
||
aqua: add-idols-ssh-key
|
||
nixos-rebuild --flake .#aquamarine --target-host aquamarine --build-host aquamarine switch --use-remote-sudo
|
||
|
||
aqua-debug: add-idols-ssh-key
|
||
nixos-rebuild --flake .#aquamarine --target-host aquamarine --build-host aquamarine switch --use-remote-sudo --show-trace --verbose
|
||
|
||
ruby: add-idols-ssh-key
|
||
nixos-rebuild --flake .#ruby --target-host ruby --build-host ruby switch --use-remote-sudo
|
||
|
||
ruby-debug: add-idols-ssh-key
|
||
nixos-rebuild --flake .#ruby --target-host ruby --build-host ruby switch --use-remote-sudo --show-trace --verbose
|
||
|
||
kana: add-idols-ssh-key
|
||
nixos-rebuild --flake .#kana --target-host kana --build-host kana switch --use-remote-sudo
|
||
|
||
kana-debug: add-idols-ssh-key
|
||
nixos-rebuild --flake .#kana --target-host kana --build-host kana switch --use-remote-sudo --show-trace --verbose
|
||
|
||
idols: aqua ruby kana
|
||
|
||
idols-debug: aqua-debug ruby-debug kana-debug
|
||
```
|
||
|
||
Save the above Makefile to the root directory of the flake, and then we can use `make deploy` to deploy the configuration to the local machine, and use `make idols` to deploy the configuration to all my remote servers.
|
||
|
||
|
||
## References
|
||
|
||
- [Tips&Tricks for NixOS Desktop - NixOS Discourse][Tips&Tricks for NixOS Desktop - NixOS Discourse]
|
||
|
||
[Tips&Tricks for NixOS Desktop - NixOS Discourse]: https://discourse.nixos.org/t/tips-tricks-for-nixos-desktop/28488
|