When I was learning Nix a few months back, I found the community often suggesting flakes as an improvements to some of the rough edges that were encountered, yet the flakes documentation is fragemented and largely targeted at audiences that already know basic Nix, so is detailed as differences from the current model.
So I wrote a guide that starts from the basics with the new CLI and builds up to flakes, you can see it here:
There’s 8 parts so far and it now covers enough I feel comfortable sharing it, as it now covers the basics of Nix and flakes, but I do plan to add more in the future to cover things like NixOS, overlays, home-manager etc. (Also maybe NixOps, but I need to learn NixOps myself first).
Upon including (in the same guide) NixOS & al. you branch off the general Nix use case.
This makes the documentation unsuitable for a subset of the current audience.
An alternative would be to make separate guides on NixOS, home-manager from first principles, and thereby marking the different target use cases.
NixOps is a bit of a special case and there isn’t really a consensus about it’s design choices and target use cases as evidenced by the emergence of: morph, krops, colmena, deploy-rs, nixus, terraform-nixos, etc.
I’d personally say about nixops: maybe it’s eating up the world a little too much. But on the bright side, I feel the first principle of interoperability with other ecosystems is raising in the Nix ecosystem.
and the ability to have a config file declare the entire state of a system without storing huge docker container artifacts for every permutation.
I read this as a reference to NixOS, and as such it might be actually a hidden layer violation.
What caracterizes Nix is the ability to program huge configuration data trees just like any other config management language (cue, dhall , jsonnet, nickel, etc).
But it also has a built-in API into the filesystem via a derivation (it somewhat lacks a built-in method to mutate remote state though, after all local state is just a special case).
Combinig these two capabilities and enriching them with plain bash for the init script called by the bootmanager can give you an operating system with interesting properties. Swapping the init script with a file that is relevant to a shell’s init sequence (e.g. .bashrc) gives you home-manager, and so forth.
Unfortunately, as stated, you can’t mutate a remote state (i.e. API) from the same built-ins and in my opinion this is actually one of the biggest shortcomings: it completely blurs the vision for the fact that local state is only a special case.
{ config, pkgs, ... }: {
nix.settings.experimental-features = [ "nix-command" "flakes" ];
}
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
# Bootloader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.efi.efiSysMountPoint = "/boot/efi";
networking.hostName = "nixos"; # Define your hostname.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Enable networking
networking.networkmanager.enable = true;
# Set your time zone.
time.timeZone = "Europe/Paris";
# Select internationalisation properties.
i18n.defaultLocale = "en_GB.utf8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "fr_FR.utf8";
LC_IDENTIFICATION = "fr_FR.utf8";
LC_MEASUREMENT = "fr_FR.utf8";
LC_MONETARY = "fr_FR.utf8";
LC_NAME = "fr_FR.utf8";
LC_NUMERIC = "fr_FR.utf8";
LC_PAPER = "fr_FR.utf8";
LC_TELEPHONE = "fr_FR.utf8";
LC_TIME = "fr_FR.utf8";
};
# Enable the X11 windowing system.
services.xserver.enable = true;
# Enable the GNOME Desktop Environment.
services.xserver.displayManager.gdm.enable = true;
services.xserver.desktopManager.gnome.enable = true;
# Configure keymap in X11
services.xserver = {
layout = "fr";
xkbVariant = "azerty";
};
# Configure console keymap
console.keyMap = "fr";
# Enable CUPS to print documents.
services.printing.enable = true;
# Enable sound with pipewire.
sound.enable = true;
hardware.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
#media-session.enable = true;
};
# Enable touchpad support (enabled default in most desktopManager).
# services.xserver.libinput.enable = true;
# Define a user account. Don't forget to set a password with ‘passwd’.
users.users.marcus = {
isNormalUser = true;
description = "Marcus";
extraGroups = [ "networkmanager" "wheel" ];
packages = with pkgs; [
firefox
vim
neovim
gimp
python3Full
hugo
atom
gparted
libreoffice
tilix
# thunderbird
];
};
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
# wget
];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
# services.openssh.enable = true;
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. It‘s perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "22.05"; # Did you read the comment?
}
With an argument of your entire configuration. Only problem is, that isn’t a function, but an attribute set, and therefore can’t be called, so it gives you that error.
In short, just do this:
{ config, pkgs, ... }: {
nix.settings.experimental-features = [ "nix-command" "flakes" ];
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
The nix language basics tutorial might help avoid things like this, maybe it would be good to link in the blog series?
Thank you TLATER. That was a great help.
Unfortunately I need to ‘brute force’ an essential package onto Nixos, as I rather like Nixos and want to stay with it, even if I am a rank amateur with zero skills.
Most of us have, at some point! The Nixpkgs docs are a great place to start, as is the Nixpkgs codebase a source of examples. If you feel stuck, you can get packaging help here, on Matrix, on Reddit, or on GitHub (by asking for feedback on a partially working package that you open a PR for).
Did you get your packaging thing taken care of? Would you like to open a thread for the package you need to add?
Yes, I think so. This really isn’t child’s play. Do you think I should read “Learn You a Haskell for Great Good” to get a good grounding in fun_lingos? (Sorry I have to gee myself up).
Created an account just to say thank you, I’ve been banging my head against a wall for quite a while and I found your series illuminating. I look forward to your next posts!