This is the NixOS ❄️ configuration that I use on my machines.
** ⚠️ Using Flake (i.e.: =nixos-rebuild --flake=) :PROPERTIES: :CUSTOM_ID: usage-flake :END:
Configure your setup first by populating a [[file:config.mk]] with at least =TARGET= set to whichever [[file:flake.nix::targets =][target from the Flake configuration]] you want to install. An example config.mk is presented below:
TARGET = .#dell-xps-9360 NIXOS_REBUILD_ARGS = --show-trace --verbose
Run =make= to install the Flakes-based configuration on your setup.
*** Update flakes
Show flake setup with:
nix flake metadata
Update either of the inputs with:
nix flake lock --update-input INPUT
*** Background
[[https://nixos.wiki/wiki/Flakes][Flakes]] provide a mechanism for managing dependencies through a [[file:flake.lock][lock file]] that pins all dependency versions and allows for easier tooling to update these dependencies (using =nix flake lock --update-input= or =nix flake update=).
The use of =nixos-rebuild= without flakes would require one to prepare the configurations directory (/etc/nixos) while also explicitly pointing towards the nixpkgs rev to build against.
Within the Flake, multiple configurations are defined to correspond to the different build targets (devices) and one can specify one of these configurations build a NixOS system for through the =--flake= CLI option.
nixos-rebuild test --flake '.#dell-precision-5560' --use-remote-sudo --show-trace --verbose --impure
For convenience, the Make rule =all=, =test= or =switch= are defined to avoid us having to type too much whenever we want to roll an update, the rules mean the following:
*** Installing Flakes on NixOS :PROPERTIES: :CUSTOM_ID: usage-flake-nixos-install :END:
See https://nixos.wiki/wiki/Flakes for more information.
Update =/etc/nixos/configuration.nix= to specify the installation of =nixFlakes= and enable the necessary experimental feature flag.
{ pkgs, ... }: { nix = { package = pkgs.bleeding.nix_2_13; extraOptions = '' experimental-features = nix-command flakes ''; }; }
Then simply execute =sudo nixos-rebuild test= to apply the configuration change.
In our [[#usage-nixos-rebuild][pre-Flakes previous approach]], we copied the needed nix files into =/etc/nixos= through the =make nonflake-setup= rule before applying these changes through a =nixos-rebuild= run. With the use of Flakes, we don't have to think about explicitly managing =/etc/nixos= anymore and the change proposed to =/etc/nixos= in this section should be the only change you'd ever need to make to =/etc/nixos=. If you have never worked with the previous configuration (with the different make rules that we cooked up), consider yourself fortunate and just forget that we even mentioned it. 😌
*** Using a nix-shell to use the experimental features :PROPERTIES: :CUSTOM_ID: usage-flake-experimental-with-nix-shell :END:
The more reliable way to get flakes working on a system is to follow the instructions for Installing Flakes on NixOS. This section documents my toying around with different ways to get flake capability in my setup, but these didn't ever work without pain on my end -- which is only due to my lack of understanding of Nix and not nix itself obviously. Here be dragons. :dragon: Everything here may feel like bandaids.
Flakes are experimental as of time of writing (2021.11.05). Use nix inside of a nix-shell to access the flakes feature and then prefix your nix commands with =sudo= to deal with the trusted user issue in case you encounter it.
nix-shell --packages nixUnstable sudo nix --experimental-features 'nix-command flakes' flake check
Use of nixos-rebuild within a nix-shell may be problematic and when attempts to run nixos-rebuild outside of a nix-shell results to =error: unrecognised flag '--extra-experimental-features'=, one may consider installing =nixUnstable= with =nix-env= as follows:
nix-env -f '
Note that this section already outlined two ways to try to get access to nixUnstable. If you simply want to be able to run a =nix flake check= on a machine that doesn't have the flakes feature flag enabled, it may be more than fine to just fire this up inside of a nix-shell. Calling =nixos-rebuild= from the shell, however; proved tricky to me and I am too lazy to really figure out why. My hunch is that there are some components of the ecosystem that are now installed from the nixFlakes pacakge, while there are still some tools in the call-chain that have no idea how to properly deal with the experimental-features flag. As such, following the instructions from the wiki and (in the previous section) will guarantee you considerably less pain. Trust me, it's worth it.
*** Debug, Tweaking, Tuning or Screwing Around :PROPERTIES: :CUSTOM_ID: usage-flake-troubleshoot :END: For debug purposes, it helps to fire up a nix repl to inspect the configuration a bit.
f = builtins.getFlake (toString ./.)
f.inputs.nixpkgs
f.outputs.nixosConfiguration
** Traditional =nixos-rebuild= :PROPERTIES: :CUSTOM_ID: usage-nixos-rebuild :END:
💡 The last time I actually used the non-Flake approach, I tested it with =make nonflake-setup && make nonflake-remote-test= and applied the configuration with =make nonflake-setup && make nonflake-remote-switch= (actually, =make nonflake-setup= only needs to be run once so if you have run it before firing either of the test stages, then a subsequent switch may be triggered without firing the setup stage again). This approach just copies the nixos-configuration into =/etc/nixos= in the setup stage and then builds it against a nixpkgs target specified by the repo URI =MY_NIXPKGS_REPO= and the commit hash =MY_NIXPKGS_COMMIT= in the test and switch stages.
The difference between the test and switch stages is that the switch stage actually commits a new generation to your system. The design assumes that the GitHub archive URL scheme applies so this may break if =MY_NIXPKGS_REPO= points to a non-GitHub-like forge.
This NixOS configuration comes packages with a collection of make rules that are intended to make your life a little bit easier. The installation process for a NixOS configuration requires the making of changes to =/etc/nixos= followed by the installation of the given configuration by =nixos-rebuild test= or =nixos-rebuild switch=. Testing a configuration is convenient to test changes that you aren't sure you may want to persist yet. After playing around with the new configuration for a while one can persist the changes by running a switch operations. 😉
In both =nixos-rebuild test= as in =nixos-rebuild switch= all changes are actually installed. The only difference is that =nixos-rebuild switch= persists the changes by adding a new generation that shows up in the bootlist. In the test case the installed changes are in your nixos store until you clean it up. This is why subsequent =nixos-rebuild= operations are much faster -- a lot of data is cached from a previous build. 😉
*** The easy way :PROPERTIES: :CUSTOM_ID: usage-nixos-rebuild-easy :END: Run
*** The harder way :PROPERTIES: :CUSTOM_ID: usage-nixos-rebuild-hard :END: Run
*** Details :PROPERTIES: :CUSTOM_ID: usage-nixos-rebuild-details :END: The Makefile essentially describes 3 types of make rules:
of which "test" and "switch" rules take the following form:
Note that the vanilla rules (=nonflake-test= and =nonflake-switch=) are basically performed against the nixpkgs version of your system's selected channel (see =nixos-version --revision=). This could be updated by executing =sudo nix-channel --upgrade nixos=.
Conversely, if you don't want to think about manually upgrading, you could use the =nonflake-upgrade-test= and =nonflake-upgrade-switch= rules which will install against the latest version of your selected channel. Between different nixos-rebuild operations, one should expect that occasionally packages can be removed or renamed thus resulting to failing builds, but this should be simple to fix.
The =nonflake-local-test= and =nonflake-local-switch= rules are useful if you need to build against a local clone of nixpkgs. This comes in handy when you've added, altered or removed modules or packages in nixpkgs. When using this approach, one should occasionally consider consolidating the updated upstream branch for the given channel with the local repository (either through a merge and/or rebase).
Finally, =nonflake-remote-test= and =nonflake-remote-switch= could be used to build against a remote nixpkgs archive. This is convenient if you want to build your configuration against a known endpoint between different machines. If the remote endpoint is that of a branch, and you are not the maintainer of that endpoint you will have to exercise the same caution that you practiced when invoking =noflake-upgrade-test= or =nonflake-upgrade-switch= rules since packages could be removed or renamed between revisions.
The optional [[file:personal.nix][personal.nix]] file is included if it exists.
Use it to capture personal details of your configuration that are not as interesting or too sensitive to track into version control. Observe the following snippet for a sense of what I decided to track in this file:
{ config, pkgs, ... }:
{
<
** TODO Figure out a clean way to import personal.nix
Currently the [[file:personal.nix]] is imported by referring to the full path [[file:~/home/vidbina/nixos-configuration/personal.nix]] which will warrant changes on your system. Since Flake-based configurations don't allow referring to files that aren't tracked in git, I had to refer to [[file:personal.nix]] by its full path and enable the impure flag when invoking =nixos-rebuild= which is a design smell. ☹️
** Timezone :PROPERTIES: :CUSTOM_ID: config-timezone :END:
Time zones can be configured on a NixOS level through the =time.timeZone= variable.
time.timeZone = "Europe/Berlin";
You can use the =time.timeZone= setting above to manage the time zones or edit the ~/.profile file to export the =TZ= variable as demonstrated in the statement below.
TZ='America/Puerto_Rico'; export TZ
Configuring time as part of the system configuration may require you to produce a new [NixOS] generation simply to apply a timezone change. I've looked for ways to make time zone changes through home-manager or in a manner less "intrusive" but it seems that the NixOS configuration is the way to do this for now. 🤷🏿♂️
Obtaining valid values for timezones can be interactively solved using the =tzselect= command which, through a series of interactive prompts, obtains the information about your time zone and provides the correct TZ value as a response.
Getting a glimpse of the date or time in a particular region or timezone can be accomplished by setting =TZ= prior to calling date as in the examples below:
TZ='America/Puerto_Rico' date TZ='CEST' date
** OpenVPN :PROPERTIES: :CUSTOM_ID: config-openvpn :END:
In order to configure OpenVPN, override the =openvpn= configuration in [[file:net.nix]] to comply with the following format:
{ servers = { tcp-config-one = { autoStart = false; updateResolvConf = true; config = '' config /home/user/path/to/openvpn-config-for-one.ovpn auth-user-pass /path/to/myprovider-pass-file.txt ''; }; }; }
where the paths for config and auth-user-pass are updated to reflect the paths of the files on your system.
Alternatively, leave the helpers defined in the =let= block of the openvpn attribute in [[file:net.nix]] as is and provide a config/openvpn.nix file with the configuration as follows:
{ toUpper }: let regions = [ ["de" "Germany"] ["nl" "Netherlands"] ["us-nyc" "USA-NEW-YORK"] ]; builder = { region ? [], kind ? "tcp" }: let locationIdentifier = builtins.elemAt region 0; locationName = builtins.elemAt region 1; in { handle = "${toUpper(kind)}-${toUpper(locationIdentifier)}"; configFile = "/home/user/path/to/${kind}-openvpn-config-for-${locationName}.ovpn"; passFile = "/path/to/myprovider-pass-file.txt"; }; in builtins.foldl' (acc: val: acc ++ [(builder { region = val; kind = "tcp"; })]) [] regions ++ builtins.foldl' (acc: val: acc ++ [(builder { region = val; kind = "udp"; })]) [] regions
in order to dynamically generate your configuration in case you have many configurations that share some common properties.
The example above, generates a configuration of the following OpenVPN configurations with their corresponding .ovpn files:
In summary, =config/openvpn.nix= contains a function that receives some functions needed for the internal housekeeping and simply returns a list of attrsets. In the provided example, we just needed to provide the =toUpper= helper and then just fold over a list of regions to generate the list for the helper in net.nix. In case this is just too messy for you, revert to the instructions at the head of this paragraph for a much easier but possibly more verbose setup. 😉
** Enable Overlays :PROPERTIES: :CUSTOM_ID: enable-overlays :END:
By simlinking the overlays directory to file:~/.config/nixpkgs/overlays.
** Debugging with =nixos-options=
For convenience, you may use the =nixos-options= tool to introspect the actual configuration of your current system.
The following command demonstrates how one could go about recursively checking all options within time =time= option.
nixos-option -r time
While debugging my TLP configuration, I often looked up the =services.tlp= to figure out how the nixos-hardware along with my own settings merged in my actual config.
nixos-option -r services.tlp
** OpenGL Drivers
Note that OpenGL drivers are listed in file:/run/opengl-driver/lib/dri.