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)
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.
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:
> 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,
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