A Minimal NixOS Config That Still Feels Premium

The design goal

The best minimal NixOS config is not the one with the fewest lines. It is the one that keeps your system calm. You should be able to look at it six months later and still understand why each line exists.

That means a few practical decisions:

Use flakes for structure

Pin your inputs, keep one obvious entry point, and make rebuilding feel mechanical instead of mysterious.

Use Home Manager for user polish

Keep shell, Git, prompt, and editor setup out of the system layer unless they truly belong there.

Prefer defaults with intent

Install fewer packages, but choose the ones you touch constantly: terminal, shell, Git, fonts, and editors.

Leave room to grow

A small module layout today becomes the clean base for multiple machines later.

Idea
Premium is not about adding twenty visual tweaks. It is about removing friction so the machine feels coherent.

Project structure

Keep the repository small. One flake file, one system module, one Home Manager module. That already buys you a lot.

            
directory layout
. ├── flake.nix ├── hosts/ │ └── laptop.nix └── home/ └── piotr.nix

This is enough to separate machine-level settings from your personal workflow. Once you need shared modules, you can add a modules/ directory later. Do not start with one unless you already feel the need.

The flake

The flake is the entry point. It pins Nixpkgs, pulls in Home Manager, and defines the machine you want to build.

            
flake.nix
{ description = "Minimal NixOS config that still feels premium"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; home-manager = { url = "github:nix-community/home-manager/release-25.05"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { self, nixpkgs, home-manager, ... }: let system = "x86_64-linux"; in { nixosConfigurations.laptop = nixpkgs.lib.nixosSystem { inherit system; modules = [ ./hosts/laptop.nix home-manager.nixosModules.home-manager { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; home-manager.users.piotr = import ./home/piotr.nix; } ]; }; }; }

Why this is enough

  • One pinned Nixpkgs input keeps the system predictable.
  • Home Manager follows the same Nixpkgs so your user packages do not drift onto a different package set.
  • One named host keeps rebuild commands simple and memorable.
Tip
Use a release branch for your base machine unless you have a real reason to track unstable. Premium setups are stable first.

The system module

This file should do the heavy lifting for the machine itself: bootloader, networking, desktop, fonts, and a very small core package set.

            
hosts/laptop.nix
{ config, pkgs, ... }: { imports = [ ./hardware-configuration.nix ]; networking.hostName = "laptop"; time.timeZone = "America/Chicago"; i18n.defaultLocale = "en_US.UTF-8"; boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.auto-optimise-store = true; networking.networkmanager.enable = true; services.xserver.enable = true; services.xserver.displayManager.gdm.enable = true; services.xserver.desktopManager.gnome.enable = true; services.printing.enable = true; services.pipewire = { enable = true; pulse.enable = true; alsa.enable = true; alsa.support32Bit = true; }; hardware.bluetooth.enable = true; services.blueman.enable = true; users.users.piotr = { isNormalUser = true; description = "Piotr"; extraGroups = [ "wheel" "networkmanager" ]; shell = pkgs.zsh; }; programs.zsh.enable = true; programs.git.enable = true; programs.steam.enable = false; fonts.packages = with pkgs; [ inter jetbrains-mono noto-fonts noto-fonts-cjk-sans noto-fonts-emoji ]; environment.systemPackages = with pkgs; [ curl git wget vim htop fastfetch ]; environment.gnome.excludePackages = with pkgs; [ gnome-tour epiphany geary ]; system.stateVersion = "25.05"; }

What makes this feel premium instead of plain

Intentional fonts. Good typography changes the feel of a machine immediately. Inter for UI and JetBrains Mono for terminal/editor is a clean starting point.

Small package surface. The machine gets a tiny set of universal tools. Personal workflow tools move to Home Manager.

Minor desktop trimming. You are not “debloating” for sport. You are just removing the pieces you know you will never use.

Zsh declared once. The shell is part of the machine identity, so it belongs in the system layer.

Watch out
Do not copy system.stateVersion casually from blog posts. Keep the value appropriate for the release you installed, then read release notes before changing it.

The Home Manager module

This is where the setup starts to feel personal without becoming messy. Keep it focused on tools you use every day.

            
home/piotr.nix
{ config, pkgs, ... }: { home.username = "piotr"; home.homeDirectory = "/home/piotr"; home.stateVersion = "25.05"; home.packages = with pkgs; [ eza fd ripgrep bat zoxide fzf firefox alacritty vscode ]; programs.home-manager.enable = true; programs.git = { enable = true; userName = "Piotr"; userEmail = "[email protected]"; extraConfig = { init.defaultBranch = "main"; pull.rebase = false; }; }; programs.zsh = { enable = true; autosuggestion.enable = true; syntaxHighlighting.enable = true; shellAliases = { ll = "eza -la --icons=auto"; cat = "bat"; update = "nix flake update && sudo nixos-rebuild switch --flake .#laptop"; }; initExtra = '' eval "$(zoxide init zsh)" ''; }; programs.fzf.enable = true; programs.bat.enable = true; programs.eza.enable = true; programs.alacritty = { enable = true; settings = { window = { padding = { x = 12; y = 12; }; opacity = 0.96; }; font = { normal.family = "JetBrains Mono"; size = 11.5; }; }; }; programs.starship = { enable = true; settings = { add_newline = false; character = { success_symbol = "[➜](bold green)"; error_symbol = "[➜](bold red)"; }; }; }; gtk = { enable = true; theme = { name = "adw-gtk3"; package = pkgs.adw-gtk3; }; iconTheme = { name = "Papirus-Dark"; package = pkgs.papirus-icon-theme; }; }; }

Why this layer matters

This is where “minimal” usually breaks. People either stop too early and end up with a machine that feels generic, or they keep stacking plugins and packages until the config turns into a scrapbook.

The middle path is better:

  • Choose a terminal and set its padding, font, and opacity once.
  • Choose a shell prompt that gives structure without turning every command into a Christmas tree.
  • Pick better everyday CLI tools: eza, fd, ripgrep, bat, zoxide.
  • Keep aliases few and memorable.
Tip
A premium machine is one where the obvious tools are already there. You should not have to “finish setting it up” every week.

Commands that matter

NixOS feels premium when the maintenance loop is simple. These are the commands worth memorizing first.

            
basic workflow
# apply the current config sudo nixos-rebuild switch --flake .#laptop # test a config without making it the boot default sudo nixos-rebuild test --flake .#laptop # update pinned inputs nix flake update # inspect what changed in the lock file git diff flake.lock # clean old generations after you are comfortable sudo nix-collect-garbage -d

The premium part is not that these commands are powerful. It is that they are repeatable. You can teach them to your future self.

Rollback mindset
Make changes in small steps. Rebuild. Test. Commit. That is how NixOS becomes relaxing instead of intimidating.

Small touches that feel expensive

The nicest setups are rarely the most complicated. They just make a series of small, coherent choices.

Consistent typography

One UI font, one code font, emoji coverage, and no weird fallback chaos.

A clean prompt

Minimal status information, no giant multi-line block unless you really need it.

Thoughtful defaults

Browser, terminal, Git, and search tools available immediately after install.

Safe updates

Inputs pinned, rebuilds explicit, rollback always nearby.

Three upgrades worth making later

  1. Add secrets management carefully. This is where the setup starts becoming a real long-term machine.
  2. Split shared pieces into modules. Only after you have two machines or obvious repetition.
  3. Add per-project dev shells. Keep language toolchains out of the base system when possible.

What not to overdo

The easiest way to ruin a minimal NixOS config is to confuse “declarative” with “everything must be in here immediately.”

  • Do not add five theming systems at once.
  • Do not turn your shell into a plugin museum.
  • Do not create ten modules before you have any repetition.
  • Do not fill environment.systemPackages with every app you might someday try.
  • Do not update blindly without skimming the lockfile diff and release notes.

Minimalism works when it reduces maintenance, not when it becomes another performance of self-control.