hiiiii cara!!!! this is a step-by-step guide 4 installing nd configuring nixos from scratch, using all da silly lil things i like 2 use like flakes, flake-parts, disko, n agenix,, so ur not just startin from weird -ahh defaults!!!
we tried 2 make this nice 4 someone who maybe doesnt install operating systems every day bcuz u have a life,,,,,,, so we added little expandable sections like u asked 4 dat explain what were actually doing at each step nd how the pieces of an operating system fit together in the context of nixos!! :33
ur gonna need:
cd and ls? ur goodOK LETS DO THIS SHIIIT
grab the latest nixos iso from nixos.org/download. u want the minimal iso (not the graphical one,,, we're doing this ourselves! we smart as hell in this bitch)
on linux:
dd if=nixos-25.11-*.iso of=/dev/sdX bs=4M status=progress && sync
replace /dev/sdX with ur actual usb device (lsblk 2 find it. pick right or u go to "i wiped my hard drive" jail). on windows use rufus. its a weird ahh program but it works
plug in the usb, reboot, and pick it from ur boot menu (usually f12 or f2 or del or something depending on ur pc. i just spam all of em as it boots). u should land at a root shell that looks scary n intimidating.
the installer needs internet. if ur on ethernet (heheheh) it probably just works. if ur on wifi:
# scan for networks iwctl # inside iwctl: station wlan0 scan station wlan0 get-networks station wlan0 connect "YourNetworkName" exit
verify it works:
ping -c 3 nixos.org
this is the part where we lay out how ur disk will be organized. traditionally on nixos u'd do this manually with fdisk + mkfs + mount... but we're using disko instead, which lets u declare ur partition layout in a nix file and apply it in one command :3
lsblk
look for the disk u wanna install 2. it'll be something like /dev/nvme0n1 or /dev/sda. remember it 4 the next step.
create a file called disko.nix n dump something like this into it
{
disko.devices = {
disk = {
main = {
# !!! change this 2 ur actual disk !!!
device = "/dev/whhateverrrrrr";
type = "disk";
content = {
type = "gpt";
partitions = {
esp = {
name = "ESP";
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "root";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
from the live environment, run:
# get git so we can use disko! nix-shell (u wont use this ever again) is a command that lets u temporarily install n make available any package Nix can support. nix-shell -p git # now format and mount the disk! i know this command is long n stupid but itll b easier to run things later. # `sudo` runs nix as the root user. # `--extra-experimental-features "nix-command flakes` enables the `nix whatever` command, and "flakes", two nix features that EVERYONE uses but are still TECHNICALLY in beta after being available for yeaaaaaaaaaaaaaars T_T # `run github:nix-community/disko` downloads n runs whatever program is defined in the file `flake.nix` in the github repo `nix-community/disko` # `-- --mode disko ./disko.nix` passes the `--mode disko disko.nix` arg to disko, which tells it to run the file u wrote! sudo nix \ --extra-experimental-features "nix-command flakes" \ run github:nix-community/disko -- \ --mode disko ./disko.nix
this will partition the disk, format each partition with the right filesystem, n mount everything at the right places under /mnt
u can check it worked ezpz by runnin
df -h | grep mnt
u should see /mnt (root) and /mnt/boot (esp).
ok YAY FLAKE TIME!!! ik ur a fish so u dont rly know what a snowflake is but maybe one time u went to the surface n saw one..... while itwa s snowing,,
a flake is the entry point for ur entire nixos system config. it declares where 2 find all ur dependencies (nixpkgs, disko, home-manager, agenix, whatever) and wires them 2 ur system config.
we'll put our flake in /mnt/etc/nixos (cuz this is where the installer expects it).
sudo mkdir -p /mnt/etc/nixos cd /mnt/etc/nixos
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
flake-parts.url = "github:hercules-ci/flake-parts";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
agenix.url = "github:ryantm/agenix";
agenix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = {
flake-parts,
nixpkgs,
...
} @ inputs:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" ]; # idk if u use arm or x86, u can delete one, or not, nix wont build the one u dont need!
flake.nixosConfigurations = {
# replace "fishbox" with ur hostname u get to pick this <3!!
fishbox = nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; }; # magic line i dont rly understand but i think it lets u use ur other flake inputs inside ur modules
modules = [
./configuration.nix
./hardware-configuration.nix
inputs.disko.nixosModules.disko
inputs.home-manager.nixosModules.home-manager
inputs.agenix.nixosModules.default
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
# change this 2 ur username!!
home-manager.users.fishy = import ./home.nix;
}
];
};
};
};
}
now we write the actual nixos system config. this is where u tell nixos what packages 2 install, what services 2 run, what timezone ur in, etc.
{ config, inputs, ... }:
{
#### setup for booting da system
# use systemd-boot (nice n clean :3)
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# limit boot entries so u dont have 8 billion diff versions in ur bootloader
boot.loader.systemd-boot.configurationLimit = 15;
# disable the boot editor (stop someone from editing
# boot params at the boot screen without a password which would let em get in as root lol)
boot.loader.systemd-boot.editor = false;
# copy kernels 2 /boot so the system can boot even if
# /nix/store gets messed up (it happens sometimes,,)
boot.loader.generationsDir.copyKernels = true;
# the kernel,,, usually the default is fine!
# boot.kernelPackages = pkgs.linuxPackages_latest;
#### setup ur network
networking.hostName = "mybox"; # change me to da same thing u set earlier in flake.nix!
networking.networkmanager.enable = true; # easy wifi/ethernet ;3
/
##### timezone n shit
time.timeZone = "America/New_York";
i18n.defaultLocale = "en_US.UTF-8";
##### user setup!
users.users.fishy = { # change me!
isNormalUser = true;
# wheel means u can use sudo, networkmanager means u can config wifi
extraGroups = [ "wheel" "networkmanager" ];
#
# for the first install we use initialPassword.
# after step 8 (agenix) well switch to hashedPasswordFile
# and delete this!
#
initialPassword = "changemelater";
};
#### desktoppp (wayland!)
# yes, its 2026, we use wayland :3
# the "xserver" module name is.. ignore that.,,,
# gnome runs on wayland by default when its available.
# we explicitly tell gdm 2 use wayland just 2 be clear.
services.xserver.enable = true; # needed by the gnome module (legacy name)
services.xserver.desktopManager.gnome.enable = true;
services.xserver.displayManager.gdm.enable = true;
services.xserver.displayManager.gdm.wayland = true; # <- this is the important bit
# wayland portal stuff (screen sharing, file picker, etc)
xdg.portal.enable = true;
xdg.portal.extraPortals = with pkgs; [
xdg-desktop-portal-gnome
];
# sound (pipewire. were enabling backcompat for programs that dont have explicit pipewire support)
hardware.pulseaudio.enable = false;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
#### packagesss
# system-wide packages (available 2 all users)
environment.systemPackages = with pkgs; [
git
vim # or nano or wtf ever
curl
wget
inputs'.agenix.packages.default
];
#### nix settings
# flakes + nix-command: required 4 our setup, this is so u dont have to pass these args every time u run a command
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# no channels! we use flakes 4 everything.
# channels are mutable state that drift over time,
# the opposite of what we want
nix.channel.enable = false;
# dedupe the store on build (saves disk space)
nix.settings.auto-optimise-store = true;
# dont let flakes set nix settings without asking.
# this is a security thing: a malicious flake could
# set accept-flake-config = true in its own flake.nix
# and then do whatever. say no 2 strangers :3
nix.settings.accept-flake-config = false;
# keep building even if one derivation fails.
# nice when ur building a bunch of stuff and walk away,,
# at least the other stuff finishes
nix.settings.keep-going = true;
# auto-free disk space: if less than ~5gb left,
# free up to ~20gb by deleting old stuff
# (values are in bytes: 5*1024*1024*1024 = 5368709120)
nix.settings.min-free = 5368709120;
nix.settings.max-free = 21474836480;
# warn-dirty = false: dont nag us when we edit files
# in our flake directory without committing first
nix.settings.warn-dirty = false;
# garbage collection, free old generations automatically
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
#### ssh (needed 4 agenix)
# this generates host keys at /etc/ssh/ on first boot,
# which agenix uses 2 decrypt secrets.
services.openssh = {
enable = true;
hostKeys = [
{
type = "ed25519";
path = "/etc/ssh/ssh_host_ed25519_key";
}
];
};
#### firewall
networking.firewall.enable = true;
# networking.firewall.allowedTCPPorts = [ 22 ]; # ssh!
#### dont change this! its like which version of config ur config is,
# its a whole thing to update, and u basically never should change this unless u rlylllllll need to
system.stateVersion = "25.11"; # matches ur nixpkgs channel
# let home-manager manage itself
home-manager.extraSpecialArgs = { inherit inputs; };
}
this file is auto-generated by the installer,,, it contains the filesystem mounts and hardware-specific kernel modules. do not write this by hand. the nixos-generate-config command in the next step will create it 4 u. just make sure the file exists after that step.
ur user-level config done thru home-manage!
{ pkgs, ... }:
{
home.username = "fishy"; # change meeee
home.homeDirectory = "/home/fishy"; # change me etoooo
home.stateVersion = "25.11";
# user packages,, just 4 u <3
# if a package is available under programs.whatever, config it there usually
home.packages = with pkgs; [
firefox
ghostty
ripgrep
bat
eza
starship
bottom
];
# git config
programs.git = {
enable = true;
userName = "ur name on github or w/e";
userEmail = "[email protected]";
};
# idk u could do like nice bash aliases, or zsh or whatev erese sleee
programs.bash = {
enable = true;
shellAliases = {
ll = "ls -al";
};
};
# let home manager install and manage itself
programs.home-manager.enable = true;
}
ok we're almost there!!!! 2 more commands 2 go awaaaaaa :333
# this makes /mnt/etc/nixos/hardware-configuration.nix
sudo nixos-generate-config --root /mnt
now check that hardware-configuration.nix exists and looks kinda vaguely sane
cat /mnt/etc/nixos/hardware-configuration.nix
fileSystems entries. if something looks wrong (like missing mountpoints), double-check ur disko.nix was applied correctly.
# build the whole system and install it
sudo nixos-install --root /mnt --flake /mnt/etc/nixos#hostname_u_picked
this will take a while (downloading + building everything). go swim around or something :3
when it asks 4 a password, thats setting the root password. u can also just leave it blank and rely on sudo from ur user account.
after it finishes, reboot:
sudo reboot
pull out th usb when it reboots. if everything went well, u should see a boot menu and then the nixos login screen!!!! u did it!!!!!
yayyyyyy u have nixos now!!!!!! >w< a few things 2 do after ur first boot
u set initialPassword = "changeme" or whatever i told u to make it,, right? change it
passwd
# update flake inputs (this updates flake.lock!) nix flake update /etc/nixos # rebuild the system with new inputs sudo nixos-rebuild switch --flake /etc/nixos
cd /etc/nixos git init git add . git commit -m "initial nixos config"
push it 2 github or wherever so u have a backup and can clone it on other machines :3
k so heres the thing, nixos is declarative, which is great, but that means everything in ur config is in the nix store and the nix store is world-readable. so if u put a password in ur configuration.nix, anyone on the machine can read it. which is cringe as fuck.
agenix fixes this!!! it encrypts ur secrets with age encryption (this is just like cool modern pgp) (using ur machines SSH host keys as the encryption keys), stores the encrypted files in the nix store (which is fine cuz theyre encrypted), and decrypts them at boot time into /run/agenix/ (which is in ram!!! and not world-readable!!).
htheres like a setup catch though, agenix decrypts secrets using the SSH host key at /etc/ssh/ssh_host_ed25519_key. but that key doesnt exist until the machine has booted at least once (its generated on first boot by the openssh service we enabled in configuration.nix). so agenix cant work on the very first install, whic his why were doing it now.
thats why we did initialPassword = "changeme". its a temporary thing! once uv booted and the SSH host keys exist, u can set up agenix and then remove that line.
after first boot, ur machine has generated its SSH host key. grab the public key:
sudo cat /etc/ssh/ssh_host_ed25519_key.pub
itll look something like ssh-ed25519 AAAAC3NzaC1... root@mybox. copy that whole line.
secrets.nix is a mapping file that tells the agenix CLI which public keys 2 encrypt each secret for. its not imported by ur nixos config, its just used by the agenix command-line tool.
mkdir -p /etc/nixos/secrets
create /etc/nixos/secrets/secrets.nix:
let # ur machines SSH host public key (from step 8a) whatever_hostname = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... root@whatever_hostname"; # ur personal SSH key (optional, lets u decrypt on another machine, u should prolyly do it tho) # fishy = "ssh-ed25519 AAAAC3... lalala@whatever"; systems = [ whatever_hostname ]; # users = [ fishy ]; in { # each entry maps an .age file 2 the keys that can decrypt it!!! "fishy-password.age".publicKeys = systems; # "fishy-password.age".publicKeys = systems ++ users; # if u added a user key do dis }
first generate a hashed password. u need the hash, not the plaintext, so..
mkpasswd -m sha-512
itll prompt u 4 a password and print out a long hash starting with $6$. copy that.
now make anencrypted secret:
cd /etc/nixos/secrets
# the nix run command is so cool. just run some piece of code w all its deps from a github repo!!!
nix run github:ryantm/agenix -- -e fishy-password.age
this opens ur editor. paste the hashed password (that long $6$... string) and save. agenix encrypts it with the public keys from secrets.nix and saves it as you-password.age.
add this 2 ur configuration.nix, inside the existing users.users.you block or nearby:
# agenix secrets age.secrets.fishy-password = { file = ./secrets/fishy-password.age; }; users.users.you = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" ]; # use the agenix secret instead of initialPassword! hashedPasswordFile = config.age.secrets.fishy-password.path; };
and now u can remove the initialPassword = "whatever"; line! u dont need it anymore cuz urusing da encyrpted one :3
cd /etc/nixos sudo nixos-rebuild switch --flake .#whatever_ur_hostname_is
after the switch u can check that the secret worked,,
sudo ls /run/agenix/
u should see fishy-password in there.
and verify ur user account still works by opening a new terminal and running su - fishy or just logging out and back in with ur password. if its fucked u can always roll back in the bootloader :D
cd /etc/nixos git add . git commit -m "add awesome encrypted files!"
the .age files are safe 2 commit cztheyre encrypted! the secrets.nix file only has public keys, so thats fine 2 commit too.
ok u have a working nixos system with proper secrets management now!!!! here are some things u might wanna look at
gtk and dconfhome.packages or environment.systemPackagesnix-env (dont od this pls ), etc), it will be lost on the next rebuild,, always make changes in ur nix config and rebuild. that way ur system is always declarative and reproducible. the nix store is the ocean w all the fishy,, ,,, everything outside it is a puddle that evaporates. after the tide recedes..
made with paws <3 by ethie n packet 4 fishy~! :3