Sometimes people don’t want to hear the truth because they don’t want their illusions destroyed, Friedrich Nietzsche.
NixOS is a unique, innovative, and powerful Linux distribution that leverages the Nix package manager. Unlike traditional Linux distributions that update packages and system configurations in-place, NixOS uses a purely functional and declarative approach to define the system state, reducing the risk of system breakage.
NixOS is a Linux distribution that uses the Nix package manager to handle packages and system configuration in a purely functional manner. This approach ensures that builds are reproducible and that the system state can be reliably replicated or rolled back.
sudo nix-env ‐‐rollback is a command used to revert your system to the previous configuration. It’s a powerful tool for undoing unintended changes or recovering from failed package installations. Before rolling back, consider if there’s a more targeted solution, like uninstalling specific packages or reverting configuration files.
Hyprland is a modern, dynamic tiling Wayland compositor that offers a highly customizable and modern desktop experience. In NixOS, you can install and configure Hyprland declaratively, allowing you to manage your system configuration (such as display managers, NVIDIA drivers, user permissions, and sound servers) in a reproducible manner from a single or a few .nix files.
We need to modify our main configuration NixOS file, vim /etc/nixos/configuration.nix.
boot = {
[...]
# Kernel parameters for NVIDIA driver support
kernelParams = [
"nvidia-drm.modeset=1" # Enables kernel modesetting for the proprietary NVIDIA driver.
"nouveau.modeset=0" # Disables modesetting for the open-source Nouveau driver, preventing conflicts with proprietary NVIDIA drivers.
];
};
The GNOME Keyring is a secure storage for passwords, encryption keys, and other secrets. By enabling it, users can automatically unlock their keyring upon logging in, allowing seamless access to saved credentials (like Wi-Fi passwords, application passwords, etc.) without needing to enter them again.
# Enable GNOME Keyring for managing secrets
services.gnome.gnome-keyring.enable = true;
security.pam.services.greetd.enableGnomeKeyring = true;
The second line integrates GNOME Keyring with the greetd login manager via PAM (Pluggable Authentication Module, a framework that manages authentication on Unix-like systems) configuration. It ensures that the keyring is unlocked automatically when a user logs in, providing seamless access to stored credentials without needing to re-enter them.
greetd is a minimal, agnostic, and flexible login manager. It is particularly suitable for Wayland compositors like Hyprland.
# Configure Greetd for managing the display manager
services.greetd = {
enable = true; # Enables the greetd display manager service
settings = {
# This block defines the default session settings for greetd.
default_session = {
# This line specifies the command that greetd will run.
# Tells greetd to launch tuigreet, a visually appealing login screen with customizable themes and colors, ...
# display current time (--time) and start Hyprland when a user logs in (--cmd Hyprland).
command = "${pkgs.greetd.tuigreet}/bin/tuigreet --time --cmd Hyprland";
user = "YOUR-USERNAME"; # This specifies the username that will be used to log into the session by default.
};
};
};
# NVIDIA hardware configuration
hardware.nvidia = {
modesetting.enable = true; # Enable modesetting for NVIDIA
# It matches the earlier kernelParams for NVIDIA modesetting.
open = false; # Indicates that proprietary NVIDIA drivers should be used instead of the open-source alternatives
nvidiaSettings = true;
# Enable the NVIDIA settings utility (nvidia-settings) for advanced driver configuration
};
# Enable Bluetooth support
hardware.bluetooth.enable = true;
# Set the LD_LIBRARY_PATH for OpenGL applications
hardware.opengl.setLdLibraryPath = true;
# Specify the video drivers for the X server
services.xserver.videoDrivers = [ "nvidia" ]; # Use the NVIDIA video driver
Pipewire is a versatile multimedia framework designed to handle audio and video streams on Linux. We are going to enable sound support via PipeWire and disable PulseAudio to avoid conflicts.
# Enable sound support in NixOS
sound.enable = true;
# Disable PulseAudio, as we will use PipeWire instead
hardware.pulseaudio.enable = false;
hardware.pulseaudio.support32Bit = false; # Disable 32-bit support for PulseAudio
# Configure PipeWire for sound management
services.pipewire = {
enable = true; # Enable PipeWire as the main audio server
alsa.enable = true; # Enable ALSA support for low-level audio device handling
pulse.enable = true; # Enable PulseAudio compatibility layer
wireplumber.enable = true; # Enable WirePlumber, a session manager for PipeWire
};
The input group provides the necessary permissions for users to interact with input devices like keyboards, mouse, and touchscreens. Hyprland, being a Wayland compositor, directly interacts with these devices to handle input events. Without the user being in the “input” group and lack appropriate permissions, Hyprland won’t be able to receive input from these devices, rendering the system useless.
users.users.YOUR-USERNAME = {
isNormalUser = true; # Set this user as a normal user
description = "YOUR-USERNAME"; # Description for the user account
extraGroups = [ "networkmanager" "wheel" "docker" "input" ]; # Add user to additional groups for permissions
shell = "${pkgs.zsh}/bin/zsh"; # Set the default shell for the user to Zsh
packages = with pkgs; [ # Additional packages installed only for this user, e.g., keepassxc, cowsay, etc.
];
};
# Define system-wide packages to be installed
environment.systemPackages = with pkgs; [
[...]
pavucontrol # GUI for controlling PulseAudio settings
kitty # A GPU-accelerated terminal emulator
rofi # Application launcher
rofi-calc # Calculator integration for Rofi
rofi-power-menu # Power menu for Rofi
gnupg # GNU Privacy Guard for encryption
pinentry # PIN entry dialog for GnuPG
hyprland # The Hyprland compositor
xorg.xkeyboardconfig # Keyboard configuration for X11
hyprpaper # Wallpaper manager for Hyprland
polkit_gnome # PolicyKit integration for GNOME
waybar # A bar for Wayland (like Polybar but for Wayland)
psmisc # Package includes utilities like 'killall'
xorg.xkill # Utility to kill X11 clients
swww # Simple wallpaper setter
networkmanagerapplet # Applet for NetworkManager
xwayland # X compatibility layer for Wayland
mesa # OpenGL implementation
gnome.adwaita-icon-theme # Default GNOME icon theme (so flatpak may play "nice")
gtk3 # GTK 3 libraries
power-profiles-daemon # Manage power profiles
];
# Enable RTKit for real-time audio processing, ...
# improving audio performance and reducing dropouts.
security.rtkit.enable = true;
# Enable D-Bus for inter-process communication
services.dbus.enable = true;
D-Bus is an inter-process communication system that allows applications to communicate with one another, e.g., xdg-desktop-portal-hyprland is a D-Bus service that provides portals for applications to interact with the desktop environment in a standardized way.
Home Manager is a separate tool that manages user-specific configuration (e.g., dotfiles, user-level services and packages, user environment) within NixOS. You typically define user-based settings in something like ~/dotfiles/home-manager.nix.
We need to configure our home-manager.nix configuration file, vim ~/dotfiles/home-manager.nix.
{ config, pkgs, ... }:
let
username = "nmaximo7"; # Define the username for the Home Manager configuration
dotfiles = "/home/${username}/dotfiles"; # Path to the user's dotfiles directory
in
{
# Enable Home Manager features
home-manager.useGlobalPkgs = true; # Allows usage of globally defined packages in your user environment.
home-manager.useUserPackages = true;
# Tell Home Manager to handle user-specific packages
home-manager.backupFileExtension = "backup";
# Set the file extension for backups
# If Home Manager sees conflicting files when deploying new dotfiles,...
# it appends the extension .backup to old configuration files instead of overwriting them silently.
When things go terrible wrong, our system break, and we cannot rebuild our system, there are a few tools to our disposal:
sudo nixos-rebuild switch ‐‐show-trace
journalctl -u home-manager-your-user.service -b
is used to view (or filter) logs related to the home-manager-your-user service from the current boot (boot) or journalctl -u hyprland.service -b
, filter logs specifically for Hyprland’s systemd service from the current boot session.journalctl -xe
command is used to view the system logs, specifically focusing on the most recent and relevant entries and providing detailed information about errors and warnings.
Mako is a lightweight, customizable, notification daemon for GNU/Linux that is designed to display notifications in a modern and visually appealing manner. It is often used in tiling window managers —like Hyprland, sway, or i3— where users prefer a simple and effective way to receive notifications without the overhead of larger desktop environments such as Gnome or Plasma.
First install the packages: mako (mako notification daemon) and (libnotify, a library that provides a way for applications to send notifications to Mako).
To configure Mako using Home Manager (a framework for managing user-specific configuration, including user services like Mako), you can use the following settings in your Home Manager configuration file (home.nix):
# Let Home Manager install and manage itself
programs.home-manager.enable = true;
services.mako = {
enable = true; # Enables the Mako notification service
defaultTimeout = 5000; # Default timeout for notifications in milliseconds (5 seconds)
extraConfig = '' # A multi-line string that defines additional settings for Mako’s appearance.
background-color=#2e3440 # Sets the background color of notifications
width=300 # Sets the width of the notifications
height=110 # Sets the height of the notifications
border-size=2 # Sets the border size of notifications
border-color=#88c0d0 # Sets the border color of notifications
border-radius=15 # Rounds the corners of the notification
# These settings are for styling a neat border around the notifications with rounded corners
icons=1 # Enables icons in notifications
max-icon-size=64 # Sets the maximum size for icons
'';
};
Open a terminal, rebuild your system (using home-manager switch
or nixox-rebuild switch
), and send a “hello world” notification: notify-send "Testing" "hello world!"
# User-specific Home Manager configuration
home-manager.users.nmaximo7 = { pkgs, ... }:
{
# Let Home Manager install and manage itself
programs.home-manager.enable = true;
[...]
# Include other configuration files
imports = [
"${dotfiles}/shell.nix"
"${dotfiles}/kitty.nix"
"${dotfiles}/zsh.nix"
"${dotfiles}/alacritty.nix"
"${dotfiles}/rofi.nix"
"${dotfiles}/vscode.nix"
"${dotfiles}/nvim.nix"
"${dotfiles}/Hyprland/Hyprland.nix"
# Import Hyprland specific configuration
# Add other configuration files as needed
];
# Define a global override for Flatpak applications to see (and share) fonts from your Nix store
home.file.".local/share/flatpak/overrides/global".text = ''
[Context]
filesystems=/run/current-system/sw/share/X11/fonts:ro;/nix/store:ro
'';
};
}
Let’s create a new configuration file for our tiling Wayland compositor, vim ~/dotfiles/Hyprland.nix. This file defines our main Hyprland configuration module in NixOS. It ensures that Hyprland is enabled, sets up XWayland support, and references additional modular files like keybindings, window rules, environment variables, decorations, and animations.
{ config, pkgs, lib, ... }:
let
username = "YOUR-USERNAME"; # Define the username for the configuration
browser = "${pkgs.firefox}/bin/firefox"; # Path to the browser executable (Firefox)
terminal = "${pkgs.kitty}/bin/kitty"; # Path to the terminal executable (Kitty)
home = config.home; # Reference to the home configuration
scriptsDir = "/home/${username}/dotfiles/scripts"; # Directory containing user's scripts
powerMenuCommand = "rofi -show power-menu -modi power-menu:rofi-power-menu"; # Command for power menu
# Import keybindings from a separate file for modularity
keybindings = import /home/${username}/dotfiles/keybindings.nix {
modifier = "SUPER"; # Modifier key for keybindings
terminal = terminal; # Reference to the terminal
browser = browser; # Reference to the browser
scriptsDir = scriptsDir; # Reference to the scripts directory
powerMenuCommand = powerMenuCommand; # Reference to power menu command
};
in
{
# Enables Hyprland with XWayland support and specifies the package to use.
wayland.windowManager.hyprland = {
enable = true; # Enable the Hyprland window manager under Wayland.
xwayland = {
enable = true; # Enable XWayland support for X11 applications
};
systemd.enable = true; # Enable systemd integration
package = pkgs.hyprland; # Specify the package to use
extraConfig = let
modifier = "SUPER"; # Define the modifier key (usually the Windows key)
in ''
# Monitor configuration
monitor=DVI-I-1,1920x1080@60,auto,1
❯ hyprctl monitors
# This command provides detailed information about all available monitors
# This information includes identifiers, resolutions, positions, and other properties.
Monitor DVI-I-1 (ID 0):
1920x1080@60.00000 at 0x0
description: LG Electronics LG TV SSCR2 0x01010101
[...]
availableModes: 3840x2160@30.00Hz ... 1920x1080@50.00Hz 1920x1080@29.97Hz ... 640x480@59.94Hz
We continue editing our file, vim ~/dotfiles/Hyprland.nix.
# Import environment variables on session start
${import /home/${username}/dotfiles/Hyprland/env.nix {}}
# It updates the systemd user environment with variables needed for Wayland and your desktop environment.
# Ensure that applications and services running in your user session have the necessary environment variables set.
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
# It runs the NetworkManager tray icon.
exec-once = nm-applet --indicator
# It launches Waybar after a 1-second delay.
exec-once = sleep 1 && waybar
# It opens Alacritty on workspace 1 with a custom message.
exec-once = [workspace 1 silent] alacritty -e sh -c 'cowsay "Welcome master! Type inicio"; exec $SHELL'
# Each ${import ...} statement loads a separate Nix file, which allows you to keep different parts of your configuration modular. This is beneficial for organization and ease of maintenance.
# Window rules
${import /home/${username}/dotfiles/Hyprland/windowrules.nix {}}
# Visual customization
${import /home/${username}/dotfiles/Hyprland/visual.nix {}}
# Import animations
${import /home/${username}/dotfiles/Hyprland/animations.nix {}}
# Decorations
${import /home/${username}/dotfiles/Hyprland/decorations.nix {}}
# Importing your keybindings. It allows you to manage your keybindings independently.
${keybindings}
'';
};
}
env.nix: This file sets environment variables required for proper Wayland operation, specifically for certain applications to be able to detect my environment, basically they should run in Wayland mode.
{ }:
''
# Sets necessary environment variables for Wayland and application compatibility.
env = XDG_CURRENT_DESKTOP,Hyprland
# Tells apps you're running Hyprland. Some applications look for XDG_CURRENT_DESKTOP to detect your environment and adapt behavior.
env = XDG_SESSION_TYPE,wayland # Indicates a Wayland session
env = GDK_BACKEND,wayland # It ensures GTK applications use Wayland.
env = QT_QPA_PLATFORM,wayland # It ensures Qt applications use Wayland.
env = MOZ_ENABLE_WAYLAND,1 # It helps Firefox and other Mozilla-based apps run natively on Wayland.
env = GDK_SCALE,1
env = QT_SCALE_FACTOR,1
# GDK_SCALE and QT_SCALE_FACTOR help scale apps correctly on high DPI or scaled displays.
''
windowrules.nix. The rules for how specific applications are handled, such as assigning them to certain workspaces or making them float. The file references windowrulev2, which is a Hyprland directive for handling windows.
{ }:
''
# Assign applications to specific workspaces
windowrulev2 = workspace 1, class:^(Alacritty)$
# It places Alacritty windows on workspace 1, firefox on workspace 2, and so on.
windowrulev2 = workspace 2, class:^(firefox)$
windowrulev2 = workspace 3, class:^(obsidian)$
windowrulev2 = workspace 4, class:^(code-oss)$
windowrulev2 = workspace 4, class:^(Google-chrome)$
windowrulev2 = workspace 5, class:^(pcmanfm)$
windowrulev2 = workspace special:geany, class:^(Geany)$
# Float VLC and pavucontrol, meaning VLC and pavucontrol...
# will always be a floating window, rather than tiling with other apps.
windowrulev2 = float, class:^(vlc)$
windowrulev2 = float, class:^(pavucontrol)$
# Float Rofi windows
windowrulev2 = float, class:^(Rofi)$
''
This instruction has three elements:
Hyprland allows you the ability to set what is called a “scratchpad” or “special workspace”. This is a special, nifty little workspace, effectively acting like a scratchpad (virtual notepad) that can be called from any workspace that you’re currently on. It is basically a workspace that you can toggle on/off on any monitor or workspace. This could be useful for organizing your workflow by keeping an application, such as code editors (Geany), communication tools (Ferdium), or note-taking apps (Obsidian), readily accessible without cluttering your main workspace and enhancing your productivity.
windowrulev2 = workspace special:geany, class:^(Geany)$
. It specifies that geany (window’s class matching the regex ^(Geany)$) should be directed or routed to the special workspace named “geany.” This allows you to isolate Geany in its own workspace.bind = ${modifier},G,togglespecialworkspace,geany
. It allows you to toggle the visibility of the special workspace “geany” on and off using a combination of a modifier key (e.g., Ctrl, Alt) and the key “G.” This makes it convenient to switch to the workspace quickly and efficiently.launch_app_in_workspace "special:geany" "geany"
.visual.nix. This file specifies general aesthetics and input settings for Hyprland, such as gaps, borders, input sensitivity, and more.
{ }:
''
# General Settings
general {
gaps_in = 6
# gaps_in and gaps_out control the space within and around tiled windows.
gaps_out = 8
# This specifies the thickness of the window borders.
border_size = 2
layout = dwindle # uses a spiral/dwindle tiling layout.
# The dwindle layout is a dynamic tiling layout that allows windows to dynamically resize, move, and reorganize, providing a flexible workspace.
resize_on_border = true
# It allows you to resize windows by dragging their borders, which can enhance usability.
}
# Input Settings
input {
kb_layout = es, us
# It sets Spanish and US English keyboard layouts, allowing for quick switching.
kb_options = "grp:alt_shift_toggle,caps:super"
# The options specify how to toggle keyboard layouts (by pressing Alt + Shift) and set the Caps Lock key to act as a Super key
sensitivity = 0.0
# Setting the sensitivity to 0.0 might indicate that you want to disable any pressure sensitivity for input devices, such as touchpads or styluses.
accel_profile = "flat"
# This setting indicates a linear acceleration profile for mouse movement, providing consistent pointer speed, which can be beneficial for precision tasks.
}
# Misc settings
misc {
initial_workspace_tracking = 0
# This controls the initial tracking of the workspace. A value of 0 mean no initial tracking is set, allowing for a fresh start.
mouse_move_enables_dpms = true
# Setting this to true means that moving the mouse will wakes the display after power-saving mode,...
# which can help save energy when the display is not in use.
key_press_enables_dpms = false
# This setting disables DPMS activation (power-saving mode) on key presses.
}
''
animations.nix. Controls how windows appear, disappear, or move with animations. Greatly affects the look-and-feel of Hyprland.
# -----------------------------------------------------
# Animations Configuration
# -----------------------------------------------------
{ ... }:
''
animations {
enabled = true; # Enable animations
bezier = myBezier, 0.05, 0.9, 0.1, 1.05;
# Defines a custom bezier curve to shape the animation’s movement for windows and transitions.
# Define different window animations
animation = windows, 1, 7, myBezier; # Transitions for windows appearing.
# The numeric parameters (1, 7, etc.) affect timing and animation speed.
animation = windowsOut, 1, 7, default, popin 80%; # Transitions for windows disappearing
animation = border, 1, 10, default; # Animation for border changes
animation = borderangle, 1, 8, default; # Animation for border angle changes
animation = fade, 1, 7, default; # Fade animation
animation = workspaces, 1, 6, default; # Animation for switching workspaces
}
''
keybinding.nix: Your custom bindings to launch apps, manage windows and workspaces, take screenshots, etc. This file references inputs from the main Hyprland.nix.
{ modifier, terminal, browser, scriptsDir, powerMenuCommand, ... }:
let
screenshotDir = "$HOME/Downloads";
in
''
# Keybindings for launching applications, moving windows, and managing workspaces.
# ${modifier} usually references SUPER or the "Windows" key.
# Launching Apps ------------------------------
bind = ${modifier},Return,exec,${terminal} # Open terminal with Windows (Modifier) + Return.
bind = ${modifier},W,exec,${browser} # Open browser (Firefox) with Windows + W
bind = ${modifier},F1,exec,alacritty # Launch Alacritty terminal with Windows + F1
bind = ${modifier},F2,exec,firefox # Launch Firefox with Windows + F2
bind = ${modifier},E,exec,sh /home/${username}/dotfiles/scripts/empezar.sh # Custom script
# Window/Session Management ------------------------------
bind = ${modifier},M,exit # forcibly closes the entire Hyprland session
bind = ${modifier},V,togglefloating # It switches the current window between tiled (normal) and floating (free-moving) modes.
# Rofi ------------------------------
bind = ${modifier},D,exec,rofi -combi-modi window,drun,ssh ,emoji -font "hack 10" -show combi -show-icons
# Launch Rofi with the combined modes specified (window, drun, ssh, emoji)...
# for launching programs, switching windows, or picking emojis and displays icons next to the entries.
bind = ${modifier}+SHIFT,D,exec,rofi -show power-menu --choices=shutdown/reboot/logout/lockscreen --confirm=logout/lockscreen
# Launch Rofi power menu. It prompts for confirmation ONLY before executing the logout or lock screen actions.
bind = ${modifier},Q,killactive # Kill active window
bind = ${modifier}+CTRL,Q,exec,hyprctl dispatch exit # Exit Hyprland
# Screenshots ------------------------------
bind = ${modifier},P,exec,sh -c 'grim "${screenshotDir}/$(date +%Y-%m-%d_%H-%M-%S).png"'
# It takes a full screenshot with Grim, saving it to $HOME/Downloads.
bind = ${modifier}+ALT,P,exec,sh -c 'grim -g "$(slurp)" "${screenshotDir}/$(date +%Y-%m-%d_%H-%M-%S).png"'
# It takes a region screenshot, letting you select an area with Slurp.