The Nixos Guide

wall from official nixos repo

Intro

Nixos is not your regular distro. Its "immutable" and "reproducible" and all that, things that most the of arch linux elites ignore before jumping into the nixos hype train. Instead of configuring your programs through ~/.config/appname files, we do it through .nix files. Instead of starting and stopping services with systemctl, we do it through .nix files. Instead of installing programs via a package manager, we do it through, you guessed it, .nix files. Most of the linux youtubers and average users do not fully explore nixos before going back to their old distro. They just configure their stuff in a big and ugly configuration.nix file. This blog will try to get you working hyprland on a flake powered nixos setup.

Basic Nix Commands

  • Rebuilding Nix System
1$ sudo nixos-rebuild switch
2# with flakes 
3$ sudo nixos-rebuild switch --flake '/etc/nixos#frostbyte'
  • Rebuilding Home Directory
1$ home-manager switch
2
3# with flakes 
4$ home-manager switch --flake '/etc/nixos#namish' # replace namish with your user
  • Updating nix flake inputs
1$ nix flake update

Flakes

A nix flake is a directory with a flake.nix and flake.lock that returns Nix expressions that can be used to install packages, run programs or in out case, create a whole ass NixOs configuration. Each flake consists of two parts defined in the flake.nix, inputs and outputs

  • Inputs: inputs are the equivalent of dependencies. They are the external resources or Flakes that a particular Flake needs to accomplish its tasks

  • Outpus: outputs refer to the different results or artifacts that a Flake can produce when it's built or evaluated. Think of them as the things that a Flake can create or provide. In this case it provides us with a nixos configuration

Why flakes?

NixOS Flakes is a feature that enhances the Nix package manager's capabilities by providing a structured and reproducible way to define and manage software packages and system configurations. It promotes consistency, immutability, and composability, making it particularly valuable for system administrators and developers who need to manage complex and reliable computing environments

Enabling Flakes

Currently flakes is a beta experimental feature that we have to manually enable

1{ pkgs, ... }: {
2  nix.settings.experimental-features = [ "nix-command" "flakes" ];
3}

Then rebuild system with sudo nixos-rebuild switch

Basic Flake

01{
02  description = "i have no idea how this works";
03
04  inputs = {
05    # Package sources.
06    master.url = "github:nixos/nixpkgs/master";
07    stable.url = "github:nixos/nixpkgs/nixos-22.11";
08    unstable.url = "github:nixos/nixpkgs/nixos-unstable";
09    home-manager.url = "github:nix-community/home-manager";
10    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
11    hyprland.url = "github:hyprwm/Hyprland";
12    spicetify-nix.url = "github:the-argus/spicetify-nix";
13    nixpkgs-f2k.url = "github:fortuneteller2k/nixpkgs-f2k";
14    hyprland-plugins.url = "github:hyprwm/hyprland-plugins";
15
16    # Channel to follow.
17    home-manager.inputs.nixpkgs.follows = "unstable";
18    nixpkgs.follows = "unstable";
19  };
20  outputs = { self, nixpkgs, home-manager, hyprland, hyprland-plugins, ... } @inputs:
21    let
22      inherit (self) outputs;
23      forSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
24      system = "x86_64-linux";
25      pkgs = import nixpkgs {
26        inherit system;
27      };
28    in
29    {
30      # host configurations
31      nixosConfigurations = {
32        frostbyte = nixpkgs.lib.nixosSystem
33          {
34            specialArgs = {
35              inherit inputs outputs home-manager hyprland hyprland-plugins;
36            };
37            modules = [
38              # > Our main nixos configuration file <
39              home-manager.nixosModule
40              ./hosts/frostbyte/configuration.nix
41            ];
42          };
43      };
44      home-manager = home-manager.packages.${nixpkgs.system}."home-manager";
45      # user configurations
46      homeConfigurations = {
47        namish = home-manager.lib.homeManagerConfiguration {
48          pkgs = nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance
49          extraSpecialArgs = { inherit inputs outputs home-manager self; };
50          modules = [
51            ./home/namish/home.nix
52          ];
53        };
54      };
55      frostbyte = self.nixosConfigurations.frostbyte.config.system.build.toplevel;
56    };
57}
58

Now of course this will not work, we still have to configure home manager and our system. Be sure to replace namish with your username.

Deriviations and overlays

This section is very important to understand. These two topics are really important to install programs that are not in the nix repos. In our config derivs will be placed at ./derivs and overlays are at ./overlays/default.nix

Deriviations

At a basic level, deriviations are just a way to build packages on your system. The structure of a Nix derivation is a set of specifications and instructions that define how to build and package a software component within the Nix package management system

Common Pieces of a nix deriviation:

  • Name and Version: A derivation typically starts with specifying the name and version of the software component you want to package.
1name = "example";
2version = "1.0";
  • Source: You specify the source code or binary of the software you're packaging. This could be from a tar, github repo, a url or even local files
1  src = fetchFromGitHub {
2    owner = "ozwaldorf";
3    repo = "lutgen-rs";
4    rev = "621db41b10e5a1a923ef67094ce1fc05c618d6ae";
5    sha256 = "0dwj3cksf62z89ihqnhhxj1wgzjqqwlc40hwdfw18yqwr3byzfxf";
6  };
  • Build Process: This section outlines the steps to compile and build the software. You specify how to configure, compile, and install the software
1buildPhase = ''
2  make
3'';
4
5installPhase = ''
6  make install
7'';
  • Dependencies: You declare the dependencies required to build and run the software. Nix will ensure that these dependencies are available during the build process
1buildInputs = with pkgs;[ gcc autoconf ];
  • Meta Info: You can include metadata about the package, such as its description and license information.
1meta = with lib; {
2  description = "An example software package";
3  license = licenses.mit;
4};

Example Deriviations

01{ lib, buildPythonPackage, fetchFromGitHub, pkgs, ... }\:
02
03buildPythonPackage rec {
04  pname = "imagecolorizer";
05  version = "git";
06  preBuild = ''
07    cat > setup.py << EOF
08    from setuptools import setup
09    setup(
10        name='ImageColorizer',
11        version='1.2',
12        packages=['ImageColorizer'],
13        entry_points = {
14            'console_scripts': ['ImageColorizer = ImageColorizer.__main__:main']
15        }
16    )
17    EOF
18  '';
19  propagatedBuildInputs = with pkgs;[
20    python310Packages.pillow
21  ];
22  src = fetchFromGitHub {
23    repo = "ImageColorizer";
24    owner = "kiddae";
25    rev = "48623031e3106261093723cd536a4dae74309c5d";
26    sha256 = "0ai4i3qmk55z3zc2gd8nicgx04pmfxl5wcq43ryy6l4c6gj2ik5r";
27  };
28  meta = {
29    description = "ImageColorizer is a Python module and a CLI tool that you can easily use to colorize wallpapers for them to fit a terminal colorscheme.";
30    homepage = "https://github.com/kiddae/ImageColorizer";
31    license = lib.licenses.mit;
32    platforms = lib.platforms.unix;
33  };
34}
35
01{ lib, fetchFromGitHub, rustPlatform, pkgs }:
02rustPlatform.buildRustPackage rec {
03  pname = "lutgen";
04  name = "lutgen";
05
06  src = fetchFromGitHub {
07    owner = "ozwaldorf";
08    repo = "lutgen-rs";
09    rev = "621db41b10e5a1a923ef67094ce1fc05c618d6ae";
10    sha256 = "0dwj3cksf62z89ihqnhhxj1wgzjqqwlc40hwdfw18yqwr3byzfxf";
11  };
12  nativeBuildInputs = with pkgs;[
13    cargo
14    rustc
15  ];
16  cargoSha256 = "sha256-s5ejGEFMxDg+ENLg0Y1ZXgk2bDyy4H5C7tNMjVEp8kY=";
17}

Overlays

Nix overlays are a mechanism in the Nix package manager that allows you to extend or modify the package set provided by the Nixpkgs repository. They are a way to add, replace, or customize packages and configurations without altering the global Nixpkgs repository. For example to use a custom fork of st we can make an overlay like this

01{ inputs }:
02{
03  additions = final: _prev: import ../pkgs { pkgs = final; inherit inputs; };
04  modifications = final: prev: {
05    # WE WILL ADD OUR OVERLAYS HERE
06    st = prev.st.overrideAttrs (oldAttrs: {
07      buildInputs = oldAttrs.buildInputs ++ [ prev.harfbuzz ];
08      src = prev.fetchFromGitHub {
09        owner = "chadcat7";
10        repo = "st";
11        rev = "3d9eb51d43981963638a1b5a8a6aa1ace4b90fbb";
12        sha256 = "007pvimfpnmjz72is4y4g9a0vpq4sl1w6n9sdjq2xb2igys2jsyg";
13      };
14    });
15  };
16}

For this to work, you need ./nixpkgs.nix and ./pkgs/default.nix

1# A nixpkgs instance that is grabbed from the pinned nixpkgs commit in the lock file
2# This is useful to avoid using channels when using legacy nix commands
3let lock = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.nixpkgs.locked;
4in
5import (fetchTarball {
6  url = "https://github.com/nixos-unstable/nixpkgs/archive/${lock.rev}.tar.gz";
7  sha256 = lock.narHash;
8})
9
1# Custom packages, that can be defined similarly to ones from nixpkgs
2# You can build them using 'nix build .#example' or (legacy) 'nix-build -A example'
3{ pkgs ? (import ../nixpkgs.nix) { }, inputs }: {
4  # example = pkgs.callPackage ./example { };
5}

We can even use our deriviations to create an overlay

1  modifications = final: prev: {
2    imgclr = prev.callPackage ../derivs/imagecolorizer.nix {
3      buildPythonPackage = prev.python310Packages.buildPythonPackage;
4    };
5    lutgen = prev.callPackage ../derivs/lutgen.nix { };
6  };

Some more examples

01{ lib, buildPythonPackage, fetchFromGitHub, pkgs, ... }:
02
03buildPythonPackage rec {
04  pname = "imagecolorizer";
05  version = "git";
06  preBuild = ''
07    cat > setup.py << EOF
08    from setuptools import setup
09    setup(
10        name='ImageColorizer',
11        version='1.2',
12        packages=['ImageColorizer'],
13        entry_points = {
14            'console_scripts': ['ImageColorizer = ImageColorizer.__main__:main']
15        }
16    )
17    EOF
18  '';
19  propagatedBuildInputs = with pkgs;[
20    python310Packages.pillow
21  ];
22  src = fetchFromGitHub {
23    repo = "ImageColorizer";
24    owner = "kiddae";
25    rev = "48623031e3106261093723cd536a4dae74309c5d";
26    sha256 = "0ai4i3qmk55z3zc2gd8nicgx04pmfxl5wcq43ryy6l4c6gj2ik5r";
27  };
28  meta = {
29    description = "ImageColorizer is a Python module and a CLI tool that you can easily use to colorize wallpapers for them to fit a terminal colorscheme.";
30    homepage = "https://github.com/kiddae/ImageColorizer";
31    license = lib.licenses.mit;
32    platforms = lib.platforms.unix;
33  };
34}
01{ lib, fetchFromGitHub, rustPlatform, pkgs }:
02rustPlatform.buildRustPackage rec {
03  pname = "lutgen";
04  name = "lutgen";
05
06  src = fetchFromGitHub {
07    owner = "ozwaldorf";
08    repo = "lutgen-rs";
09    rev = "621db41b10e5a1a923ef67094ce1fc05c618d6ae";
10    sha256 = "0dwj3cksf62z89ihqnhhxj1wgzjqqwlc40hwdfw18yqwr3byzfxf";
11  };
12  nativeBuildInputs = with pkgs;[
13    cargo
14    rustc
15  ];
16  cargoSha256 = "sha256-s5ejGEFMxDg+ENLg0Y1ZXgk2bDyy4H5C7tNMjVEp8kY=";
17}

Configuring the system

First we will make two directories, ./hosts/frostbyte and ./hosts/shared. The first one would be for the main system itself and shared would be for setttings that would be common for each system (if you have more than 1 devices)

Also copy your existing hardware-configuration.nix to ./hosts/frostbyte/hardware-configuration.nix

Shared Settings

This will consist of:

  • Setting the timezone
  • Enabling network, bluetooth, sudo, polkit
  • User Config
  • Installing Fonts
  • Nix Settings
  • Actually Installing the overlays
001{ pkgs, outputs, overlays, lib, inputs, ... }:
002let
003  # DEFINING VARIABLES
004  flake-compat = builtins.fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz";
005in
006{
007  # USING SYSTEMD-BOOT INSTEAD OF GRUB
008  boot.loader.systemd-boot.enable = true;
009  boot.loader.efi.canTouchEfiVariables = true;
010  boot.loader.efi.efiSysMountPoint = "/boot/efi";
011  programs.zsh.enable = true;
012
013  # REQUIRED TO ENABLE SWAYLOCK TO AUTHENTICATE PASSWORDS
014  security.pam.services.swaylock = {
015    text = ''
016      auth include login
017    '';
018  };
019  networking = {
020    networkmanager.enable = true;
021    firewall.enable = false;
022  };
023  security = {
024    sudo.enable = true;
025  };
026  services.blueman = {
027    enable = true;
028  };
029
030  # SETTING TIME ZONE
031  time = {
032    hardwareClockInLocalTime = true;
033    timeZone = "Asia/Kolkata";
034  };
035  i18n.defaultLocale = "en_US.UTF-8";
036  console = {
037    font = "Lat2-Terminus16";
038    useXkbConfig = true;
039  };
040  users = {
041    users.namish = {
042      isNormalUser = true;
043      extraGroups = [ "wheel" "networkmanager" "audio" "video" "libvirtd" ];
044      packages = with pkgs; [ ];
045    };
046    defaultUserShell = pkgs.zsh;
047  };
048  fonts.packages = with pkgs; [
049    inter
050    dosis
051    rubik
052    ibm-plex
053    (nerdfonts.override { fonts = [ "Iosevka" "CascadiaCode" "JetBrainsMono" ]; })
054  ];
055  # STILL USING PULSEAUDIO (SOWWY)
056  sound.enable = true;
057  hardware.pulseaudio.enable = true;
058  hardware.pulseaudio.extraConfig = "load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1";
059  security.rtkit.enable = true;
060  virtualisation = {
061    libvirtd.enable = true;
062  };
063  services.dbus.enable = true;
064  xdg.portal = {
065    enable = true;
066    wlr.enable = true;
067    # gtk portal needed to make gtk apps happy
068    extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
069  };
070
071  environment.systemPackages = with pkgs; [
072    nodejs
073    lutgen
074    home-manager
075    blueman
076    inotify-tools
077    udiskie
078    rnix-lsp
079    xorg.xwininfo
080    pulseaudio
081    libnotify
082    xdg-utils
083    gtk3
084    jq
085    st
086    spotdl
087    discord
088    firefox
089    unzip
090    imgclr
091    grim
092    eww-wayland
093    wayland
094    swaylock-effects
095    swaybg
096    git
097    pstree
098    mpv
099    xdotool
100    spotify
101    brightnessctl
102    pamixer
103    nix-prefetch-git
104    python3
105    brillo
106    slop
107    ripgrep
108    maim
109    wirelesstools
110    xorg.xf86inputevdev
111    xorg.xf86inputsynaptics
112    xorg.xf86inputlibinput
113    xorg.xorgserver
114    xorg.xf86videoati
115  ];
116  # SETTING THE DEFAULT SHELL
117  environment.shells = with pkgs; [ zsh ];
118
119  programs.dconf.enable = true;
120  qt = {
121    enable = true;
122    platformTheme = "gtk2";
123    style = "gtk2";
124  };
125
126  # PRINTING  // BLUETOOTH
127  services.printing.enable = true;
128  hardware.bluetooth = {
129    enable = true;
130    powerOnBoot = false;
131  };
132  services.xserver = {
133    layout = "us";
134    xkbVariant = "us,";
135  };
136  security.polkit.enable = true;
137  nix = {
138    settings = {
139      experimental-features = [ "nix-command" "flakes" ];
140      trusted-users = [ "root" "@wheel" ];
141      auto-optimise-store = true;
142      warn-dirty = false;
143    };
144    gc = { ## AUTOMATICALLY DELETEING OLD BUILDS AFTER 5 DAYS
145      automatic = true;
146      options = "--delete-older-than 5d";
147    };
148    optimise.automatic = true;
149  };
150  system = {
151    copySystemConfiguration = false;
152    stateVersion = "22.11";
153  };
154}

System Specific configurations

Now this is used for:

  • Overlays
  • Networking
  • Services

BONUS -- also learn how to install awesomewm

01{ inputs, outputs, config, pkgs, lib, self, ... }:
02{
03
04  imports = [
05    ./hardware-configuration.nix
06    ../shared
07  ];
08
09  nixpkgs = {
10    overlays = [
11      outputs.overlays.modifications
12      outputs.overlays.additions
13      inputs.nixpkgs-f2k.overlays.stdenvs
14      inputs.nixpkgs-f2k.overlays.compositors
15      (final: prev:
16        {
17          awesome = inputs.nixpkgs-f2k.packages.${pkgs.system}.awesome-git;
18        })
19    ];
20    config = {
21      # Disable if you don't want unfree packages
22      allowUnfreePredicate = _: true;
23      allowUnfree = true;
24    };
25  };
26
27  networking.hostName = "frostbyte";
28  networking.useDHCP = false;
29  networking.interfaces.wlo1.useDHCP = true;
30  boot.kernelPackages = pkgs.linuxPackages_5_15;
31  environment.systemPackages = lib.attrValues {
32    inherit (pkgs)
33      brightnessctl
34      wayland
35      android-tools;
36  };
37
38  services = {
39    gvfs.enable = true;
40    power-profiles-daemon.enable = false;
41    tlp.enable = true;
42    upower.enable = true;
43    xserver = {
44      enable = true;
45      videoDrivers = [ "amdgpu" ];
46      libinput = {
47        enable = true;
48        touchpad = {
49          tapping = true;
50          middleEmulation = true;
51          naturalScrolling = true;
52        };
53      };
54      displayManager = {
55        defaultSession = "none+awesome";
56        startx.enable = true;
57      };
58      windowManager.awesome = {
59        enable = true;
60
61      };
62      desktopManager.gnome.enable = false;
63    };
64  };
65}

If you are wondering, we will be installing hyprland via home-manager

Home Manager

Home Manager gives you control over the user's home environment. Home-Manager fosters user-specific profiles, accommodating distinct configurations for different users on the same system. Each user can have their own tailored environment, making it versatile and adaptable for both personal and multi-user systems

For this too, we will make two directories ./home/USER (replace USER with your username) and ./home/shared. The first one would be for user itself and shared would be for setttings that would be common for each user

Handling Colors

01{}:
02rec {
03  wallpaper = "birdseye.jpg";
04  foreground = "d7e0e0";
05  cursorColor = "d7e0e0";
06  background = "0a1011";
07  darker = "080c0d";
08
09  color0 = "0d1617";
10  color8 = "253336";
11
12  color1 = "df5b61";
13  color9 = "f16269";
14
15  color2 = "6ec587";
16  color10 = "8dd5a0";
17
18  color3 = "de8c6a";
19  color11 = "e59575";
20
21  color4 = "659bdb";
22  color12 = "739bdf";
23
24  color5 = "c167d9";
25  color13 = "d16ee0";
26
27  color6 = "6fd1d5";
28  color14 = "7bd3e0";
29
30  color7 = "c5d7d7";
31  color15 = "cedcd9";
32
33  bg2 = "101a1b";
34  mbg = "0e1718";
35
36  contrast = "111a1b";
37  cursorline = "111a1b";
38
39  comment = "505758";
40  name = "wave";
41  neofetchpic = "verycool.png";
42}

This is how the theme looks on awesome


rec is the programming equivalent of return. Now we can use this file to get the colors, whereever we want

01{ inputs, config, pkgs, lib, ... }:
02let
03  spicetify-nix = inputs.spicetify-nix;
04  colors = import ../shared/cols/wave.nix { };
05  hyprland = inputs.hyprland;
06  hyprland-plugins = inputs.hyprland-plugins;
07  unstable = import
08    (builtins.fetchTarball "https://github.com/nixos/nixpkgs/archive/master.tar.gz")
09    {
10      config = config.nixpkgs.config;
11    };
12  nixpkgs-f2k = inputs.nixpkgs-f2k;
13in
14{
15  # some general info
16  home.username = "namish";
17  home.homeDirectory = "/home/namish";
18  home.stateVersion = "22.11";
19  programs.home-manager.enable = true;
20  home.file.".icons/default".source =
21    "${pkgs.phinger-cursors}/share/icons/phinger-cursors";
22
23
24  nixpkgs.overlays = [
25  ];
26  imports = [
27  ];
28  ## THIS CODE IS RUN WHENEVER HOME MANAGER REBUILDS THE HOME DIRECTORY
29  home = {
30    activation = {
31      installConfig = ''
32        if [ ! -d "${config.home.homeDirectory}/.config/nvim" ]; then
33          ${pkgs.git}/bin/git clone --depth 1 https://github.com/chadcat7/kodo ${config.home.homeDirectory}/.config/nvim
34        fi
35      ''; ## if there is no ~/.config/nvim, git clone my nvim config
36    };
37    packages = with pkgs; [
38      bc
39      chromium
40      dunst
41      wl-clipboard
42      sway-contrib.grimshot
43      xss-lock
44      htop
45      recode
46      gcc
47      go
48      gopls
49      playerctl
50      scc
51      cinnamon.nemo
52      neofetch
53      rust-analyzer
54      hsetroot
55      notion-app-enhanced
56      mpc-cli
57      pfetch
58      ffmpeg_5-full
59      neovim
60      xdg-desktop-portal
61      imagemagick
62      procps
63      killall
64      moreutils
65      cava
66      mpdris2
67      socat
68      pavucontrol
69      fzf
70      feh
71      exa
72    ];
73  };
74
75  ## IMPORTANT
76  nixpkgs.config = {
77    allowUnfree = true;
78    allowBroken = true;
79    allowUnfreePredicate = _: true;
80  };
81}

Xresources

01{ colors }:
02
03with colors; {
04  xresources = {
05    path = ".Xresources";
06    extraConfig = ''
07    '';
08    properties = {
09      "*.background" = "#${background}";
10      "*.darker" = "#${darker}";
11      "*.color0" = "#${color0}";
12      "*.color8" = "#${color8}";
13      "*.color7" = "#${color7}";
14      "*.color15" = "#${color15}";
15      "*.foreground" = "#${foreground}";
16      "*.color1" = "#${color1}";
17      "*.color9" = "#${color9}";
18      "*.color2" = "#${color2}";
19      "*.color10" = "#${color10}";
20      "*.color3" = "#${color3}";
21      "*.color11" = "#${color11}";
22      "*.color4" = "#${color4}";
23      "*.color12" = "#${color12}";
24      "*.color5" = "#${color5}";
25      "*.color13" = "#${color13}";
26      "*.color6" = "#${color6}";
27      "*.color14" = "#${color14}";
28      "*.contrast" = "#${contrast}";
29      "*.cursorline" = "#${cursorline}";
30      "*.comment" = "#${comment}";
31      "st.borderpx" = 32;
32    };
33  };
34}

I think this piece of code pretty self explanantory

1imports = [
2    (import ../shared/xresources.nix { inherit colors; })
3];

And then also import it

Hyprland

The only thing we have to do for installing hyprland is enable it. (now that i read this sentence again, it feels really dumb). Anyways create file ./home/USER/conf/ui/hyprland/default.nix

01{ config, lib, pkgs, hyprland, colors, ... }:
02
03{
04  systemd.user.targets.hyprland-session.Unit.Wants = [ "xdg-desktop-autostart.target" ];
05  wayland.windowManager.hyprland = with colors; {
06    enable = true;
07    package = hyprland.packages.${pkgs.system}.hyprland;
08    systemdIntegration = true;
09    extraConfig = ''
10      .....
11      exec = swaybg -i ~/.wallpapers/${name}/${wallpaper} &
12      .....
13    '';
14  };
15}

This was done considering you have wallpapers in .wallpapers/wave/wallpaper.png or something like that. And also do not forget to add in your extra config duh. 😑

And then import this file in your home.nix

1imports = [
2   ...
3   (import ./conf/ui/hyprland/default.nix { inherit config pkgs lib hyprland colors; })
4];

Waybar

Instead of using the waybar in official nix packages repositories, we will use the one available from hyprland flake input we used.

01{ config, lib, pkgs, hyprland, colors, ... }:
02
03{
04  programs.waybar =
05    with colors; {
06      enable = true;
07      package = hyprland.packages.${pkgs.system}.waybar-hyprland;
08      systemd = {
09        enable = false;
10        target = "graphical-session.target";
11      };
12      style = ''
13        window#waybar {
14          background-color: #${background};
15          color: #${foreground};
16          border-bottom: none;
17        }
18        * {
19          font-size: 16px;
20          min-height: 0;
21          font-family: "Iosevka Nerd Font", "Material Design Icons Desktop";
22        }
23        ....
24      '';
25      settings = [{
26        height = 35;
27        layer = "top";
28        position = "top";
29        tray = { spacing = 10; };
30        modules-center = [ "clock" ];
31        modules-left = [ "hyprland/workspaces" ];
32        modules-right = [
33          "network"
34          "tray"
35        ];
36        "hyprland/workspaces" = {
37          on-click = "activate";
38          all-outputs = true;
39          format = "{icon}";
40          disable-scroll = true;
41          active-only = false;
42          format-icons = {
43            default = "󰊠 ";
44            persistent = "󰊠 ";
45            focused = "󰮯 ";
46          };
47          persistent_workspaces = {
48            "1" = [ ];
49            "2" = [ ];
50            "3" = [ ];
51            "4" = [ ];
52            "5" = [ ];
53          };
54        };
55        clock = {
56          format = "{:%d %A %H:%M}";
57          tooltip-format = "{:%Y-%m-%d | %H:%M}";
58        };
59        network = {
60          interval = 1;
61          on-click = "eww open --toggle control";
62          format-disconnected = "󰤮 ";
63          format-wifi = "󰤨 ";
64        };
65        "custom/power" = {
66          on-click = "powermenu &";
67          format = " ";
68        };
69      }];
70    };
71}

and ofc import this too

1imports = [
2    ...
3    (import ./conf/utils/dunst/default.nix { inherit colors pkgs; })
4];

Wezterm

Wezterm may not be the most minimal terminal, but is still better than that bloated piece of shit. Yes I have changed my terminal yet again. (future blog probably soon). Wezterms font rendering is the most alike to ST and it can also change colorschemes instantly, also has ligature support, and much more. (and i am not even using the git version). Create a directory ./home/USER/conf/term/wezterm and create two files default.nix and colors.nix

01{ colors, ... }:
02with colors; {
03  followSystem = {
04    # basic colors
05    background = "#${background}";
06    cursor_bg = "#${foreground}";
07    cursor_border = "#${foreground}";
08    cursor_fg = "#${color8}";
09    foreground = "#${foreground}";
10    selection_bg = "#${color15}";
11    selection_fg = "#${background}";
12    split = "#${mbg}";
13
14    # base16
15    ansi = [
16      "#${color0}"
17      "#${color1}"
18      "#${color2}"
19      "#${color3}"
20      "#${color4}"
21      "#${color5}"
22      "#${color6}"
23      "#${color7}"
24    ];
25    brights = [
26      "#${color8}"
27      "#${color9}"
28      "#${color10}"
29      "#${color11}"
30      "#${color12}"
31      "#${color13}"
32      "#${color14}"
33      "#${color15}"
34    ];
35
36    # tabbar
37    tab_bar = {
38      background = "#${color8}";
39      active_tab = {
40        bg_color = "#${background}";
41        fg_color = "#${foreground}";
42      };
43      inactive_tab = {
44        bg_color = "#${color8}";
45        fg_color = "#${foreground}";
46      };
47      inactive_tab_hover = {
48        bg_color = "#${color0}";
49        fg_color = "#${foreground}";
50      };
51      inactive_tab_edge = "#${color0}";
52      new_tab = {
53        bg_color = "#${color8}";
54        fg_color = "#${color7}";
55      };
56      new_tab_hover = {
57        bg_color = "#${color0}";
58        fg_color = "#${foreground}";
59      };
60    };
61  };
62}
01{ pkgs, colors, ... }:
02
03with colors; {
04  programs.wezterm = {
05    enable = true;
06    colorSchemes = import ./colors.nix {
07      inherit colors;
08    };
09    extraConfig = ''
10      local wez = require('wezterm')
11      return {
12        default_prog     = { 'zsh' },
13        cell_width = 0.85,
14        front_end        = "OpenGL",
15        enable_wayland   = true,
16        scrollback_lines = 1024,
17        font         = wez.font_with_fallback({ 
18          "Iosevka Nerd Font",
19          "Material Design Icons",
20        }),
21        dpi = 96.0,
22        bold_brightens_ansi_colors = true,
23        font_rules    = {
24          {
25            italic = true,
26            font   = wez.font("Iosevka Nerd Font", { italic = true })
27          }
28        },
29        font_size         = 14.0,
30        line_height       = 1.15,
31        harfbuzz_features = { 'calt=1', 'clig=1', 'liga=1' },
32        color_scheme   = "followSystem",
33        window_padding = {
34          left = "24pt", right = "24pt",
35          bottom = "24pt", top = "24pt"
36        },
37        default_cursor_style = "SteadyUnderline",
38        enable_scroll_bar    = false,
39        warn_about_missing_glyphs = false,
40        enable_tab_bar               = true,
41        use_fancy_tab_bar            = false,
42        hide_tab_bar_if_only_one_tab = true,
43        show_tab_index_in_tab_bar    = false,
44        window_close_confirmation = "NeverPrompt",
45        inactive_pane_hsb         = { 
46          saturation = 1.0, brightness = 0.8
47        },
48        check_for_updates = false,
49      }
50    '';
51  };
52}
1imports = [
2    ...
3    (import ./conf/term/wezterm/default.nix { inherit pkgs colors; })
4];

Dunst

For notifications, there are now many options for wayland but I am still going to be using dunst because I had the least trouble configuring it. Nix considers dunst to be service instead of program, therefore, programs.dunst becomes services.dunst. The configuration file for dunst will be at ./home/USER/conf/utils/dunst/default.nix

01{ colors, pkgs }: with colors;{
02  services.dunst = {
03    enable = true;
04    settings = {
05      global = {
06        follow = "mouse";
07        width = 500;
08        origin = "top-center";
09        alignment = "left";
10        vertical_alignment = "center";
11        ellipsize = "middle";
12        offset = "15x15";
13        padding = 15;
14        horizontal_padding = 15;
15        text_icon_padding = 15;
16        icon_position = "left";
17        min_icon_size = 48;
18        max_icon_size = 64;
19        progress_bar = true;
20        progress_bar_height = 8;
21        progress_bar_frame_width = 1;
22        progress_bar_min_width = 150;
23        progress_bar_max_width = 300;
24        separator_height = 2;
25        frame_width = 2;
26        frame_color = "#${mbg}";
27        separator_color = "frame";
28        corner_radius = 8;
29        transparency = 0;
30        gap_size = 8;
31        line_height = 0;
32        notification_limit = 0;
33        idle_threshold = 120;
34        history_length = 20;
35        show_age_threshold = 60;
36        markup = "full";
37        font = "Iosevka Nerd Font 12";
38        word_wrap = "yes";
39        sort = "yes";
40        shrink = "no";
41        indicate_hidden = "yes";
42        sticky_history = "yes";
43        ignore_newline = "no";
44        show_indicators = "no";
45        stack_duplicates = true;
46        always_run_script = true;
47        hide_duplicate_count = false;
48        ignore_dbusclose = false;
49        force_xwayland = false;
50        force_xinerama = false;
51        mouse_left_click = "do_action";
52        mouse_middle_click = "close_all";
53        mouse_right_click = "close_current";
54      };
55
56      fullscreen_delay_everything = { fullscreen = "delay"; };
57      urgency_low = {
58        timeout = 3;
59        background = "#${background}";
60        foreground = "#${foreground}";
61        highlight = "#${color4}";
62      };
63      urgency_normal = {
64        timeout = 6;
65        background = "#${background}";
66        foreground = "#${foreground}";
67        highlight = "#${color4}";
68      };
69      urgency_critical = {
70        timeout = 0;
71        background = "#${background}";
72        foreground = "#${foreground}";
73        highlight = "#${color9}";
74      };
75    };
76  };
77
78}

and the tradition continues

1    imports = [
2        ...
3        (import ./conf/ui/waybar/default.nix { inherit config pkgs lib hyprland colors; })
4    ];
5

ZSH

zsh is my preferred shell (sorry fish noobs) so i will cover how to configure use zsh, create a file ./home/USER/conf/shell/zsh/default.nix

01
02{ config, colors, pkgs, lib, ... }:
03
04{
05  programs.zsh = {
06    enable = true;
07    enableAutosuggestions = true;
08    syntaxHighlighting.enable = true;
09    enableCompletion = true;
10    history = {
11      expireDuplicatesFirst = true;
12      save = 512;
13    };
14    initExtra = ''
15      bindkey  "^[[H"   beginning-of-line
16      bindkey  "^[[4\~"   end-of-line
17      bindkey  "^[[3\~"  delete-char
18      export PATH=${config.home.homeDirectory}/.local/bin:${config.home.homeDirectory}/.local/share/nvim/mason/bin:$PATH
19    '';
20  };
21
22}

** ADDING PLUGINS- **


Instead of using something like zplug, zgen, zpm, we will just install them, through, you guessed it, nix. We will use this to install powerlevel10k

01programs.zsh = {
02    plugins = [
03      {
04        name = "powerlevel10k";
05        src = pkgs.zsh-powerlevel10k;
06        file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
07      }
08      {
09        name = "powerlevel10k-config";
10        src = lib.cleanSource ./conf;
11        file = "powerlevel.zsh";
12      }
13    ];
14};

You will also need ./conf/shell/zsh/conf/powerlevel.zsh which tou can get from here

SHELL ALIASES-

Instead of adding aliases in initExtra we will do this

01programs.zsh = {
02    shellAliases = {
03      la = "exa -l";
04      ls = "ls --color=auto";
05      v = "nvim";
06      nf = "neofetch";
07      suda = "sudo -E -s";
08      nix-pkgs = "nix --extra-experimental-features 'nix-command flakes' search nixpkgs";
09    };
10
11}

And in the end:

1    imports = [
2        ...
3        (import ./conf/shell/zsh/default.nix { inherit config colors pkgs lib; })
4    ];

Music

This section will teach you how to set up mpd, ncmpcpp and playerctl on your system. It plays audio files, organizes playlists and maintains a music database, all while using very few resources. In order to interface with it, a separate client is needed. The client here is ncmpcpp

Here is the mpd configuration in nix

01{ config, pkgs }:
02
03{
04  services.mpd = {
05    enable = true;
06    musicDirectory = "${config.home.homeDirectory}/Music";
07    dataDir = "${config.home.homeDirectory}/.config/mpd";
08    extraConfig = ''
09      auto_update           "yes"
10      restore_paused        "yes"
11      audio_output {
12        type "pulse"
13        name "Pulseaudio"
14        server "127.0.0.1" # add this line - MPD must connect to the local sound server
15      }
16
17      audio_output {
18      	type                "fifo"
19      	name                "Visualizer"
20      	format              "44100:16:2"
21      	path                "/tmp/mpd.fifo"
22      }
23      audio_output {
24      	type		            "httpd"
25      	name		            "lossless"
26      	encoder		          "flac"
27      	port		            "8000"
28      	max_client	        "8"
29      	mixer_type	        "software"
30      	format		          "44100:16:2"
31      }
32    '';
33    network.startWhenNeeded = true;
34
35    # Allows mpd to work with playerctl.
36    home.packages = [ pkgs.playerctl ];
37    services.mpdris2.enable = true;
38    services.playerctld.enable = true;
39  };
40}

According to the arch wiki: Ncmpcpp is an mpd client (compatible with mopidy) with a UI very similar to ncmpc, but it provides new useful features such as support for regular expressions for library searches, extended song format, items filtering, the ability to sort playlists, and a local filesystem browser.

For ncmpcpp

01{config, pkgs }:
02{
03  programs.ncmpcpp = {
04    enable = true;
05    package = pkgs.ncmpcpp.override {
06      visualizerSupport = true;
07      clockSupport = true;
08      taglibSupport = true;
09    };
10    mpdMusicDir = "${config.home.homeDirectory}/Music";
11    settings = {
12      # Miscelaneous
13      ncmpcpp_directory = "${config.home.homeDirectory}/.config/ncmpcpp";
14      ignore_leading_the = true;
15      external_editor = "nvim";
16      message_delay_time = 1;
17      playlist_disable_highlight_delay = 2;
18      autocenter_mode = "yes";
19      centered_cursor = "yes";
20      allow_for_physical_item_deletion = "no";
21      lines_scrolled = "0";
22      follow_now_playing_lyrics = "yes";
23      lyrics_fetchers = "musixmatch";
24
25      # visualizer
26      visualizer_data_source = "/tmp/mpd.fifo";
27      visualizer_output_name = "mpd_visualizer";
28      visualizer_type = "ellipse";
29      visualizer_look = "●●";
30      visualizer_color = "blue, green";
31
32      # appearance
33      colors_enabled = "yes";
34      playlist_display_mode = "classic";
35      user_interface = "classic";
36      volume_color = "white";
37
38      # window
39      song_window_title_format = "Music";
40      statusbar_visibility = "no";
41      header_visibility = "no";
42      titles_visibility = "no";
43      # progress bar
44      progressbar_look = "━━━";
45      progressbar_color = "black";
46      progressbar_elapsed_color = "blue";
47
48      # song list
49      song_status_format = "$7%t";
50      song_list_format = "$(008)%t$R  $(247)%a$R$5  %l$8";
51      song_columns_list_format = "(53)[blue]{tr} (45)[blue]{a}";
52
53      current_item_prefix = "$b$2| ";
54      current_item_suffix = "$/b$5";
55
56      now_playing_prefix = "$b$5| ";
57      now_playing_suffix = "$/b$5";
58
59      # colors
60      main_window_color = "blue";
61
62      current_item_inactive_column_prefix = "$b$5";
63      current_item_inactive_column_suffix = "$/b$5";
64
65      color1 = "white";
66      color2 = "blue";
67    };
68  };
69}

Bonus - Creating Files

to write on a file on rebuilding home manager you can use home.file

for eg writing the .xinitrc file

1{}:
2{
3  home.file.".xinitrc".text = ''
4    #!/usr/bin/env bash
5    exec dbus-run-session awesome
6  '';
7}

you can also make bin files with this, just set executable to true

01{ colors }:
02{
03  home.file.".local/bin/lock" = {
04    executable = true;
05    text = ''
06      #!/bin/sh
07      playerctl pause
08      sleep 0.2
09      swaylock -i ~/.config/awesome/theme/wallpapers/${colors.name}/${colors.wallpaper} --effect-blur 10x10
10    '';
11  };
12}

Dynamic GTK Theming

For dynamic themeing, we will just use phocus and then just subsititute the colors with patches. You are recommended to get your patches from here. So this is the derivation we need for it -

01{ stdenvNoCC
02, fetchFromGitHub
03, nodePackages
04, colors
05,
06}:
07stdenvNoCC.mkDerivation rec {
08  pname = "phocus";
09  version = "0cf0eb35a927bffcb797db8a074ce240823d92de";
10
11  src = fetchFromGitHub {
12    owner = "phocus";
13    repo = "gtk";
14    rev = version;
15    sha256 = "sha256-URuoDJVRQ05S+u7mkz1EN5HWquhTC4OqY8MqAbl0crk=";
16  };
17
18  patches = [
19    ../patches/npm.diff
20    ../patches/gradients.diff
21    ../patches/substitute.diff
22  ];
23
24  postPatch = ''
25    substituteInPlace scss/gtk-3.0/_colors.scss \
26      --replace "@bg0@" "#${colors.background}" \
27      --replace "@bg1@" "#${colors.contrast}" \
28      --replace "@bg2@" "#${colors.color8}"\
29      --replace "@bg3@" "#${colors.color0}" \
30      --replace "@bg4@" "#${colors.comment}" \
31      --replace "@red@" "#${colors.color1}" \
32      --replace "@lred@" "#${colors.color9}" \
33      --replace "@orange@" "#${colors.color3}" \
34      --replace "@lorange@" "#${colors.color11}" \
35      --replace "@yellow@" "#${colors.color3}" \
36      --replace "@lyellow@" "#${colors.color11}" \
37      --replace "@green@" "#${colors.color2}" \
38      --replace "@lgreen@" "#${colors.color10}" \
39      --replace "@cyan@" "#${colors.color6}" \
40      --replace "@lcyan@" "#${colors.color15}" \
41      --replace "@blue@" "#${colors.color4}" \
42      --replace "@lblue@" "#${colors.color12}" \
43      --replace "@purple@" "#${colors.color5}" \
44      --replace "@lpurple@" "#${colors.color14}" \
45      --replace "@pink@" "#${colors.color5}" \
46      --replace "@lpink@" "#${colors.color14}" \
47      --replace "@primary@" "#${colors.foreground}" \
48      --replace "@secondary@" "#${colors.color15}"
49  '';
50
51  nativeBuildInputs = [ nodePackages.sass ];
52  installFlags = [ "DESTDIR=$(out)" "PREFIX=" ];
53}

And then we can call it in the home.nix file

1  gtk = {
2    enable = true;
3    gtk3.extraConfig.gtk-decoration-layout = "menu:";
4    theme.name = "phocus";
5  };

Nix Shell

Nix-shell is a command-line tool and concept within the Nix package manager that enables the creation of isolated development environments for software projects. It allows developers to specify the exact dependencies and environment required for a project, ensuring consistency and reproducibility across different systems and projects.

Here is an example shell.nix

1{ pkgs ? import <nixpkgs> {} }:
2  pkgs.mkShell {
3    # nativeBuildInputs is usually what you want -- tools you need to run
4    nativeBuildInputs = with pkgs.buildPackages; [ lua52Packages.lua cmake gcc pam gnumake ];
5}

To enter the shell we will use

1$ nix-shell shell.nix

Conclusion

whew, if you made it there, i hope you learned something from this. if there is an error you can contact me at discord @ chadcat7. For a reference you can check out my dots

Some sites and cool dotfiles

09 Oct 2023
By Namish Pande