As a passionate Linux enthusiast, I've always been drawn to the freedom and customization that Linux offers. Choosing my desktop environment, selecting the perfect software stack, and tailoring my system to my preferences – that's what Linux is all about. However, managing packages on Linux can sometimes be quite a challenge.
Introducing Nix
That's where Nix, the package manager for NixOS, comes into play. It revolutionizes package management by introducing a modular approach, allowing me to fine-tune every aspect of my system.
What is Nix?
Nix is an open-source package manager and configuration management system initially developed by Eelco Dolstra in the early 2000s. Unlike traditional package managers, such as APT, YUM, or Homebrew, Nix takes a unique approach to package management by focusing on functional package management. This means that packages are built and managed in an isolated, reproducible environment, ensuring that they don't interfere with one another and can be rolled back or upgraded with ease.
Beyond package management, NixOS extends Nix's capabilities to system configuration. In NixOS, you define your entire system's configuration using Nix expressions. This declarative approach makes it easier to manage and reproduce system configurations.
Declarative Configuration with NixOS
NixOS uses a declarative approach to system configuration. Instead of manually configuring your system through various configuration files, you describe your system's entire configuration in a single, well-structured NixOS configuration file. This file specifies everything from package installations to system services, users, and hardware settings.
# configuration.nix
{ config, pkgs, ... }:
{
imports = [
/etc/nixos/hardware-configuration.nix
./neeraj.nix # Note this!
];
boot.loader.grub.enable = true;
networking.firewall.enable = true;
services.sshd.enable = true;
environment.systemPackages = with pkgs; [
vim
wget
];
}
Managing Packages
In the example provided above, we demonstrated how to specify a list of packages for system-wide installation within our configuration.nix
file. However, if you intend to install software for a specific user, you can follow these steps:
Create a new file named
<username>.nix
. In this instance, I will useneeraj.nix
as the filename. Don't forget to import this file in theconfiguration.nix
file.Within the
neeraj.nix
file, import the necessary modules and define the user. Users can be defined in theusers.users
attribute, as illustrated below:
# neeraj.nix
{ config, lib, pkgs, ... }:
# Your configuration options go here
users.users.neeraj = {
isNormalUser = true;
description = "Neeraj";
extraGroups = [ "networkmanager" "wheel" "libvirtd" ];
packages = with pkgs; [
vim
thunar
];
};
Grouping Applications
Organize your applications logically. You can group them based on categories like development tools, utilities, or graphical applications:
users.users.neeraj = with pkgs; [
# Development Tools
git
python
nodejs
# Utilities
htop
tmux
# Graphical Applications
gimp
inkscape
];
This type of grouping helps to streamline your system configuration and package management in several ways:
Ease of Maintenance: By organizing your applications logically, you make it easier to update and manage your system. When you want to add or remove packages, you can quickly find them within their respective categories. This reduces the chances of overlooking essential tools during system updates.
Modularity: Grouping packages by function or purpose promotes modularity. You can easily reuse or share specific categories of packages with others who have similar needs. For instance, you can share your development tools category with fellow developers, making it convenient for them to set up their development environments.
Enhanced Modularity
Let us take a step further. I've organized packages into modular sections. This approach enhances code readability, maintainability, and flexibility. It's like building with Lego blocks – I can mix and match to create my perfect package set.
{
/* Modular Package Lists */
# Unstable apps
unstableApps = with pkgs; with unstable; [
nodejs_20
];
# Common stable apps
commonStableApps = with pkgs; [
brave
];
}
Now, we have the flexibility to create combinations of these lists and append them to the user or environment settings for installation on our system. This modular approach simplifies customization and allows us to tailor our package selection precisely to our requirements.
Final Configuration
# neeraj.nix
{ config, lib, pkgs, ... }:
let
/*
sudo nix-channel --add https://nixos.org/channels/nixos-unstable nixos-unstable
sudo nix-channel --update
*/
unstable = import <nixos-unstable> { config = { allowUnfree = true; }; };
# Qtile specific packages
qtileApps = with pkgs; [
alacritty
blueman
dmenu
dunst
gnome.gnome-keyring
libnotify
libsecret
nitrogen
xfce.thunar
];
# Gnome specific packages
gnomeApps = with pkgs; [
gnome.gnome-tweaks
];
# XFCE Specific packages
xfceApps = with pkgs; with xfce; [
xfce4-pulseaudio-plugin
];
# KDE Specific packages
kdeApps = with pkgs; with libsForQt5; [
plasma-browser-integration
];
# Unstable apps
unstableApps = with pkgs; with unstable; [
nodejs_20
virt-manager
virtualenv
vlc
vscode
zoom-us
];
# Apps to be installed irrespective of desktop env
commonStableApps = with pkgs; [
brave
firefox
gh
git
gparted
htop
micro
neofetch
onlyoffice-bin
parted
python3
ventoy-full
];
appendApps = apps: kdeApps ++ unstableApps ++ commonStableApps ++ apps;
in {
users.users.neeraj = {
isNormalUser = true;
description = "Neeraj";
extraGroups = [ "networkmanager" "wheel" "libvirtd" ];
packages = with pkgs; appendApps [];
};
}
Explanation
This script, in particular, is part of a NixOS configuration for a user named Neeraj.
Variables
{ config, lib, pkgs, ... }:
: This line introduces the Nix expression and specifies the variables (config
, lib
, pkgs
, and more) that will be available for use within the script.
Package Channels
The next section of the script deals with package channels. It appears to be commented out with /* ... */
, but it suggests adding and updating channels for Nix packages. Channels are sources of Nix packages that you can use to install software. In this case, it's showing how to add the nixos-unstable
channel, which contains potentially less stable but more bleeding-edge packages.
Importing Unstable Packages
The script proceeds to import packages from the nixos-unstable
channel using the import
statement. These packages are intended to be more experimental or less stable than the ones available in the standard NixOS channel.
unstable = import <nixos-unstable> { config = { allowUnfree = true; }; };
unstable
: This variable stores the imported packages from thenixos-unstable
channel, allowing them to be used later in the script.allowUnfree = true;
: This setting allows the use of packages that might contain proprietary or non-free software, which might not be suitable for all users.
Defining Package Lists
The script categorizes packages into different lists based on their purpose. These lists include packages for various desktop environments (Qtile, Gnome, XFCE, KDE), unstable packages, and common stable packages.
qtileApps
,gnomeApps
,xfceApps
, andkdeApps
: These variables store lists of packages specific to different desktop environments (Qtile, Gnome, XFCE, and KDE).unstableApps
: This variable contains packages from thenixos-unstable
channel.commonStableApps
: This variable contains packages that are stable and common across different desktop environments.
Combining Packages
The appendApps
variable combines all the package lists into a single list using the ++
operator.
appendApps = apps: kdeApps ++ unstableApps ++ commonStableApps ++ apps;
appendApps
: This variable represents the combined list of packages that will be installed for Neeraj's user.
User Configuration
Finally, the script defines the user configuration for Neeraj. It specifies user-related settings such as groups, description, and the list of packages to be installed.
in {
users.users.neeraj = {
isNormalUser = true;
description = "Neeraj";
extraGroups = [ "networkmanager" "wheel" "libvirtd" ];
packages = with pkgs; appendApps [];
};
}
users.users.neeraj
: This part of the script defines a user named Neeraj.isNormalUser = true;
: It indicates that Neeraj is a normal user.description = "Neeraj";
: This describes the user.extraGroups
: These are additional groups that Neeraj belongs to.packages
: It specifies the list of packages to be installed for Neeraj, using theappendApps
list.
Conclusion
In this blog post, we've taken a deep dive into a Nix script that sets up a NixOS environment for a user named Neeraj. This script makes use of Nixpkgs, package channels, and different package lists to tailor the user's desktop environment and software packages. Nix offers a highly structured and repeatable system configuration, making it a robust tool for handling your system's software and preferences.
As you start setting up your computer and handling your software, bear in mind the importance of careful organization. Whether you're a software developer seeking a tidy workspace or just an ordinary computer user striving to improve your computer's performance, categorizing your apps into logical groups and enhancing their flexibility will undoubtedly lead to a smoother and more efficient computer experience.
This is it for this blog; now it's time for you to embark on your experimentation. Till then, keep reading, keep exploring, and keep learning."