Kami level power? Take my root
and show me the code...
Nix is More Powerful Pkg Manger!
In pkg managing universe, if Conda is miniverse, Nix would be, let's take one step further, perhaps the real universe, or the last step before entering hybrid simulation world like docker etc. In package management and dev env setup, Nix is already system-level (God/Kami) isolation for *nix system! Though it doesn't isolate process and memory like docker does, which is still in my boundary of package manager. Setting up reproducible, reliable and efficient dev environment is what it can do best. Conda and others cannot do that because many pkg manager's dependencies are runtime and sensitive to envs and paths.
Note the price to pay: root permission, but you can do without permission if namespace supported or rebuild everything from scratch if you set store in different location than /nix/store
. For linux, you have to use NixOS to enable the full power manipulating hardware and services.
Features Summary
Immutable Dependencies
Means it freezes the deps during build, and it hardcodes the tool-chain (e.g. GCC) to ignore any global system wide variable (/usr/include) and version of lib. Therefore, it can handle many different version independently for even the same app and make your life easier.
Versions
When you install, Nix uses Sqlite to persist your version and prepares for rolling back! It also creates symlink as in:
$HOME/.nix-profile/
Because of hardcoding, rolling back or version control is blasting fast just similar to how git hand files.
Easy Going as Other Managers
If you are familar with other manager tool, interacting with nix user environment is similar, e.g.:
Install -> nix-env -i pkgname
Uninstall -> nix-env -e pkgname
List -> nix-env -q
Easy Share
You can create your own environment .nix
and share it easily to your colleagues. You can also build it to docker or virtual hard drive image!
Nix Expression & Language
It adapts functional programming so everything is an expression/lambda/function and variables are immutable. You can use nix repl
to play with language.
You can compile expression to create derivatives which describe how to build a package. .nix
is like .c
in C; while .drv
is like .o
. Many people are scared by it?
Build Your Own Pkgs
It's all about writing a .nix
expression. Find a nice example in pkgs and copy/paste!
If you want to know the configs available in home-manager, just look into code.
Install Nix
It's very easy to follow here if you have root. Otherwise, you either need namespace and do:
wget https://github.com/nix-community/nix-user-chroot/releases/download/1.0.3/nix-user-chroot-bin-1.0.3-x86_64-unknown-linux-musl -O nix-user-chroot
chmod +x nix-user-chroot
mkdir -m 0755 ~/.nix
./nix-user-chroot ~/.nix bash -c "curl -L https://nixos.org/nix/install | bash"
After you can run nix-user-chroot ~/.nix bash
to start bash where /nix
is mounted with namespace. You might also want to put nix-user-chroot
to your $PATH
.
You can play with nix-env -i
to install any packages.
Install Home-Manager
You can define your nix environment and quickly switch it.
# install home-manager
nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
nix-channel --update
nix-shell '<home-manager>' -A install
It's must-have if you like Nix to manage your dotfiles and home folder etc.
Setup Pkgs At Home
Here's I did some env var trick to switch different profiles, like "base", etc. to point to a cli.nix
file that contains the actual code of pkgs:
{ config, pkgs, ... }:
let profiles = {
# Base includes shell and utility related install
base = [
./profiles/cli.nix
];
...
};
envProfile = builtins.getEnv "MY_NIX_PROFILE";
# I'm sure here can do better, now tolerate nix baby language 😂
profile = if ("${envProfile}" == "") then "base" else "${envProfile}" ;
in
{
nixpkgs.config.allowUnfree = true;
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
imports = if (builtins.hasAttr "${profile}" profiles) then profiles."${profile}" else [ "${profile}" ];
#
home.username = "Zeng Dai";
home.homeDirectory = builtins.getEnv "HOME";
# Starting ver
home.stateVersion = "20.09";
}
Then in cli.nix
for "base" profile, it mainly install the CLI tools (my precious pick):
{ config, lib, pkgs, ... }:
let
comma = import ( pkgs.fetchFromGitHub {
owner = "Shopify";
repo = "comma";
rev = "4a62ec17e20ce0e738a8e5126b4298a73903b468";
sha256 = "0n5a3rnv9qnnsrl76kpi6dmaxmwj1mpdd2g0b4n1wfimqfaz6gi1";
}) {};
in
{
home.packages = with pkgs; [
fzf
vim
direnv # how to setup nix-shell? https://nixos.org/guides/declarative-and-reproducible-developer-environments.html#declarative-reproducible-envs
httpie
tmux
ag
fasd
ripgrep
tree
broot
ranger
trash-cli
rsync
fd
exa
comma
bat
htop
navi
tldr
neofetch
tokei
];
}
Customization Home
How about the dotfiles? The Nix language provides tons of flexibility to do whatever you need to do. Mostly you can check the options of customization, for example.
Basically, it allows you to put all dotfiles into the nix scripts.
Desktop App
For Linux, you simply install it in Nix. To make it appear in your e.g. Gnome launchpad, you need to create .profile
like:
# Makesure gnome know these path
export NIX_HOME_PATH=$HOME/.nix-profile
if [ -e $NIX_HOME_PATH/etc/profile.d/nix.sh ]; then . $NIX_HOME_PATH/etc/profile.d/nix.sh; fi # added by Nix installer
export XDG_DATA_DIRS=$HOME/.nix-profile/share:$HOME/.share:"${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}"
Logout and in to restart the desktop.
For some application, it requires GL e.g. alacritty
. You need to install nixGL:
let
nixGL = import ( pkgs.fetchFromGitHub {
owner = "guibou";
repo = "nixGL";
rev = "fad15ba09de65fc58052df84b9f68fbc088e5e7c";
sha256 = "1wc5gfj5ymgm4gxx5pz4lkqp5vxqdk2njlbnrc1kmailgzj6f75h";
}) {};
in
{
home.packages = with pkgs; [
...
nixGL.nixGLDefault
...
];
}
Then run your GL application as nixGL alacritty
.
HOWEVER, desktop support for non NixOS is limited. Basically you need many knowledge regarding your X window environment and build many things from scratch, while only NixOS has native support. If you are interested how I achieved that under Centos, please check out my nix files and .profile. Each X window desktop have its own implementation that not necessarily follows statndard. You can enable home-manager
's xserver support but without NixOS, you have to know and hookup everything on your own. On Mac most app e.g. firefox won't be available, you have to write some wrapper to download/unpack dmg to cheat. At least you don't need to stick to brew cask.
Systemd Service
If you want to start daemon service in Linux, for example, a cloud folder, you can use home-manager to setup these service like this:
systemd.user = {
services.dropbox_rclone = let
mountdir="${config.home.homeDirectory}/cloud/dropbox";
in
{
Unit = {
Description = "dropbox rclone mount";
};
Install.WantedBy = [ "multi-user.target" ];
Service = {
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountdir}";
ExecStart = ''
${pkgs.rclone}/bin/rclone mount dropbox: ${mountdir}
'';
ExecStop = "${pkgs.fuse}/bin/fusermount -uz ${mountdir}";
Type = "notify";
Restart = "always";
RestartSec = "10s";
};
};
Development
This is the area that Nix really shines. With Nix you basically could dump all other package managers. For example, you could nail down the particular version of main source nixpkgs
like:
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/3590f02e7d5760e52072c1a729ee2250b5560746.tar.gz") {}
}:
{
...
}
to pin a specific python package:
{ pkgs ? import<nixpkgs>{} }:
let
myranger = pkgs.python3.pkgs.buildPythonPackage rec {
pname = "ranger-fm";
version = "1.9.0";
src = pkgs.python3.pkgs.fetchPypi {
inherit pname version;
#format = "wheel"; # tar.gz is default
# python = "cp38";
# abi = "cp38";
# platform = "manylinux1_x86_64";
sha256 = "1ay8lhgkqzmvavinbmzdgh8pgkddpx72pygi4d9ac01jbbggibkg";
};
#format = "wheel"; # tar.gz is default
meta = {
homepage = "...";
description = "...";
};
};
pythonEnv = pkgs.python3.withPackages (ps: [
myranger
]);
in pkgs.mkShell {
buildInputs = [
pythonEnv
];
}
For general packages, you can always take a look at the default.nix
for the package you want to pin.
Plus the power of direnv
and .envrc
you can do auto shell env change when enter the folder!
NixOS
Under NixOS, this idea is pushed further. Everything in OS is configurable in Nix language. I like the idea of using language to define your machine in a uniform way, but unfortunately so far only NixOS is supported. It means not transferrable to other OS. Probably I'll try it out in the future post.