⚠️ NOTICE: This is a sanitized public version of a private NixOS configuration.Personal information, secrets, network configurations, and other sensitive data have been removed or replaced with generic placeholders. This configuration will not build as-is without customization.
Use this repository as a template and reference - you will need to:
- Replace all placeholder values with your own (usernames, hostnames, emails, etc.)
- Create your own
network.nixfrom the providednetwork.nix.example- Set up your own secrets management with sops-nix
- Adjust hardware configurations for your specific system
For questions about specific configurations, please open an issue.
Personal NixOS configuration for Framework 13 (Intel Core Ultra Series 3) with secure boot, LUKS encryption, Btrfs with impermanence, and Stylix theming.
Hardware: Framework 13 (Intel Core Ultra Series 3)
Desktop: GNOME (Plasma and COSMIC also available)
Theme System: Stylix with Kanagawa color scheme
Bootloader: Lanzaboote (systemd-boot for initial install, switched via post-install.sh)
Boot Splash: Plymouth with the custom framework-penguin theme, recolored from the active Stylix scheme
Filesystem: Btrfs with LUKS encryption
Partition Layout: Disko-managed with impermanence
- Security: Secure Boot with Lanzaboote, LUKS full-disk encryption unlocked automatically by the TPM (
systemd-pcrlock, PCRs 0/4/7), sops-nix secrets management - Impermanence: Root and home wiped on cold boot, hibernation-safe (systemd
HibernateLocationEFI variable; the wipe is skipped on resume) - Hybrid Swap: zram (50% RAM) for active compression + swapfile for hibernation
- Hardware Support: Framework-specific optimizations, fan control (fw-fanctrl), QMK/VIA keyboard support, Thunderbolt, fingerprint reader
- External Monitor Control: DDC/CI brightness control via ddcutil and a GNOME quick-settings extension
- Cloud Storage: Google Drive (multiple accounts) mounted via rclone and surfaced in the Nautilus sidebar
- Development: VSCode, Claude Code, Git, Docker, Virt-Manager, Ghostty, Distrobox, Direnv
- Unified Theming: Stylix manages colors across system, browser extensions, and applications
- Automation: GitHub Actions for automated flake.lock and package updates
nixos/
├── flake.nix # Main flake with inputs/outputs
├── configuration.nix # System-level NixOS configuration
├── home.nix # Home Manager user configuration
├── hardware-configuration.nix
├── stylix.nix # Unified theming (Kanagawa)
├── de/
│ ├── default.nix # DE selector (gearhead.desktop option)
│ ├── gnome.nix # GNOME desktop environment (active)
│ ├── kde.nix # KDE Plasma desktop (selectable)
│ ├── cosmic.nix # COSMIC DE (selectable)
│ ├── dconf.nix # GNOME dconf settings
│ └── plasma.nix # plasma-manager home config (used with kde)
├── firefox.nix # Firefox browser config
├── disko.nix # Disk partitioning
├── impermanence.nix # Impermanence/stateless config
├── archived/flatpak.nix # Flatpak support (not currently imported)
├── network.nix # NetworkManager profiles (sops-nix)
├── overlays/
│ └── gnome-extensions/ # Temporary overlay pinning newer extension versions
├── packages/
│ ├── duplicacy-web.nix # Duplicacy service
│ ├── package-duplicacy.nix
│ ├── package-copyous.nix # Copyous GNOME extension
│ └── package-framework-penguin.nix # Plymouth boot theme (Stylix-recolored)
├── scripts/
│ ├── install.sh # Initial installation script
│ ├── post-install.sh # Post-install setup (Secure Boot)
│ ├── root-diff.sh # Debug impermanence changes
│ ├── boot-loader-helper.sh # Marker-based helper to toggle systemd-boot/lanzaboote config blocks
│ └── sync-pr-cleanup.sh # Interactive tool to merge newest sync PR and close/delete stale ones
├── secrets/
│ └── secrets.yaml # Encrypted secrets (sops)
├── dots/ # Dotfiles and configs
├── templates/
│ └── python-direnv/ # Python dev environment template
└── .github/workflows/ # Automated update workflows
- NixOS ISO booted with networking configured
- USB drive (or other accessible media) with the sops-nix
keys.txt—install.shprompts interactively for its path
-
Clone and run installer:
git clone https://github.com/0x6a64/NixOS-Public.git cd nixos ./scripts/install.sh -
The script will:
- Configure git and authenticate with GitHub
- Partition and format disks using Disko
- Generate hardware configuration
- Copy sops-nix keys from USB
- Install NixOS with the
nixos-frameworkflake configuration - Prompt for reboot
-
After installation:
- Reboot into new system
- Run
/home/user/Nixos/scripts/post-install.shto complete setup
The post-install.sh script completes the following:
-
Configuration Migration:
- Copies
/etc/nixosinto~/Nixos, fixes ownership, then clears/etc/nixos(the directory itself is kept — it is a persisted bind mount)
- Copies
-
Secure Boot Setup:
- Uncomments lanzaboote configuration in
configuration.nixif needed - Rebuilds the system
- Checks Secure Boot setup mode status
- Enrolls Secure Boot keys with
sbctl enroll-keys -m -f
- Uncomments lanzaboote configuration in
-
Manual Steps After Script:
- Enable Secure Boot in BIOS/UEFI
- Reboot to verify Secure Boot is active
/boot 2GB FAT32 (ESP)
/dev/nvme0n1p2 LUKS encrypted, Btrfs subvolumes:
root Ephemeral / + home (rotated into persist/old_roots on cold boot)
nix Nix store
persist Persistent state (bind-mounted to ephemeral paths)
log /var/log
lib /var/lib (docker, systemd, bluetooth, sbctl, etc.)
swap /persist/swap swapfile (48GB, nocow)
On each cold boot an initrd systemd service (root-explode) runs when the kernel command line does not contain resume= (i.e. not a hibernate resume). It moves the current root Btrfs subvolume to persist/old_roots/<timestamp> and creates a fresh root subvolume, giving a clean slate. Old roots older than 30 days are pruned, keeping at least the latest one.
What's ephemeral (wiped on cold boot):
/root filesystem/home/*(except persisted directories)- Random dotfiles, app caches, temp files
What's persisted:
- XDG directories: Desktop, Documents, Downloads, Music, Pictures, Videos, etc.
- Security:
.ssh,.gnupg,.local/share/keyrings, rclone OAuth tokens - App configs: ghostty, VSCode, OBS, Discord, etc.
- Desktop state: monitor layout/VRR (
monitors.xml), Nautilus bookmarks, Mesa GPU shader cache - System:
/etc/nixos, NetworkManager connections, machine-id
- zram: 50% of RAM, priority 100 (used first)
- Swapfile: 48GB at
/persist/swap/swapfile, priority 10 (fallback + hibernation)
Lanzaboote signs the kernel/initrd and boot.loader.systemd-boot.enable is forced off. LUKS unlocks
automatically from the TPM via boot.lanzaboote.measuredBoot against PCRs 0, 4 and 7, using
systemd-pcrlock rather than raw PCR value-matching — PCR 11 is deliberately excluded because
systemd-pcrphase rolls it on every boot, so it can never be value-matched.
Because lanzaboote re-signs the pcrlock policy in place on every rebuild, kernel and initrd updates
do not require re-enrolling. The tradeoff is configurationLimit = 8: systemd-pcrlock cannot
build a policy covering more generations than that.
The original LUKS passphrase keyslot is kept as a fallback and should stay that way —
systemd-pcrlock is still labelled experimental upstream.
Enrollment was done without --tpm2-with-pin. Instead, GDM autologin is disabled so a real password
still gates the session; this also fixes gnome-keyring auto-unlock, since pam_gnome_keyring needs
pam_unix to actually authenticate before it can be handed the key.
Boot failures are self-healing: bootCounting.initialTries = 3 rolls back to the last known-good
generation, and systemd-bless-boot (wired to multi-user.target) marks a boot good so the counter
resets.
- Lid close: suspend-then-hibernate (hibernates after 1h)
- Power button: hibernate
- Power button long press: poweroff
- Shell: Zsh with Home Manager configuration
- Editor: VSCode with Nix extensions
- Terminal: Ghostty, Ptyxis
- Browser: Firefox with Stylix integration
- Communication: Discord, Signal
- Productivity: Obsidian, LibreOffice, OBS Studio
- Cloud Storage: rclone (Google Drive, multiple accounts)
- Virtualization: Docker (Btrfs storage), libvirtd/QEMU, Virt-Manager
Secrets stored in secrets/secrets.yaml encrypted with sops-nix:
- User password
- Wallhaven API key
- SSH keys
- GitHub token
Age key stored at /persist/sops-nix/keys.txt
Day-to-day rebuilds go through nh, configured via programs.nh
(flake = "/home/user/Nixos", exported as NH_FLAKE). nh self-elevates, so these are run without sudo:
| Alias | Runs | Purpose |
|---|---|---|
rebuild |
nh os switch |
Build and activate now |
rebuildst |
nh os switch --show-trace |
Same, with a full trace when evaluation fails |
update |
nh os boot --update |
Update flake inputs, activate on next boot |
nh bundles nix-output-monitor for build progress and prints a closure diff on completion.
Validate a change without activating it (no sudo required):
nixos-rebuild build --flake /home/user/Nixos # produces ./result
nix store diff-closures /run/current-system ./result
alejandra -c . # formatting checkGarbage Collection & Store Maintenance:
- Weekly automatic GC via
programs.nh.clean(nh-clean.service/timer,--keep 5 --keep-since 3d). This replacesnix.gc.automatic— the two conflict, so do not re-enable it. - Daily scheduled store deduplication (
nix.optimise) - Boot menu capped at the last 8 generations (lanzaboote inherits
configurationLimit); 8 is the ceilingsystemd-pcrlockcan build a TPM policy for
This repository includes GitHub Actions workflows for automated maintenance:
- File:
.github/workflows/update-flake-lock.yml - Schedule: Weekly (Sundays at midnight UTC)
- Action: Updates
flake.lockand creates a PR for review
- File:
.github/workflows/update-duplicacy-web.yml - Schedule: Weekly (Mondays at midnight UTC)
- Action: Checks for new versions, updates hash, creates PR
- File:
.github/workflows/update-copyous.yml - Schedule: Weekly (Mondays at midnight UTC)
- Action: Checks GitHub releases for new versions, uses Nix tooling to compute hash, creates PR
Manual Trigger: Go to Actions tab → Select workflow → "Run workflow"
Cost: Free for public repos. Private repos get 2,000+ free minutes/month (these workflows use ~30 min/month total).
Direnv is enabled for automatic environment loading. Templates are provided in templates/.
-
Copy the template to your project:
cp -r ~/Nixos/templates/python-direnv/* /path/to/project/
-
Allow direnv:
cd /path/to/project direnv allow -
Customize
flake.nixwith your required packages
The environment auto-loads when you enter the directory and creates a .venv/ for pip packages.
DEs are switched with a single option in configuration.nix — all de/*.nix modules are always imported and gate themselves on it (home-manager modules follow automatically via osConfig):
gearhead.desktop = "gnome"; # or "kde" or "cosmic"Then rebuild with the rebuild alias.
The network.nix module provides templates for declarative network configuration with sops-nix secrets.
Supported profiles:
- WiFi (WPA-PSK)
- VLAN tagging
- WireGuard VPN
Setup:
- Add secrets to
secrets/secrets.yaml:wifi-home: "your-password" wg-private-key: "your-wireguard-key"
- Import
./network.nixinconfiguration.nix - Uncomment and customize desired profiles
Stylix exposes colors via config.lib.stylix.colors, an attribute set generated by mkSchemeAttrs from base16.nix.
| Attribute | Example Value | Description |
|---|---|---|
base08 |
"ff0000" |
Raw hex (no #) |
base08-hex |
"ff0000" |
Same as above |
base08-hex-r |
"ff" |
Red component (hex) |
base08-hex-g |
"00" |
Green component (hex) |
base08-hex-b |
"00" |
Blue component (hex) |
base08-rgb-r |
"255" |
Red component (decimal string) |
base08-rgb-g |
"0" |
Green component (decimal string) |
base08-rgb-b |
"0" |
Blue component (decimal string) |
base08-dec-r |
"0.996094" |
Red component (0–1 float string) |
base08-dec-g |
"0.0" |
Green component (0–1 float) |
base08-dec-b |
"0.0" |
Blue component (0–1 float) |
Standard color names that map to base16 values:
red,green,yellow,blue,magenta,cyan,orange,brownbright-red,bright-green,bright-yellow,bright-blue,bright-magenta,bright-cyan
Access hex colors with # prefix:
config.lib.stylix.colors.withHashtag.base08 # => "#ff0000"
config.lib.stylix.colors.withHashtag.red # => "#ff0000"From stylix/colors.nix:
# Convert hex color to 0x format
config.lib.stylix.mkHexColor "#ff0000" # => "0xff0000"
# Convert hex color with opacity (0-1) to 0xRRGGBBAA format
config.lib.stylix.mkOpacityHexColor "#ff0000" 0.5 # => "0x7fff0000"| Color | Typical Usage |
|---|---|
base00 |
Default Background |
base01 |
Lighter Background (status bars, line numbers) |
base02 |
Selection Background |
base03 |
Comments, Invisibles |
base04 |
Dark Foreground (status bars) |
base05 |
Default Foreground |
base06 |
Light Foreground |
base07 |
Lightest Foreground |
base08 |
Red (Variables, Errors) |
base09 |
Orange (Integers, Constants) |
base0A |
Yellow (Classes, Search) |
base0B |
Green (Strings) |
base0C |
Cyan (Support, Regex) |
base0D |
Blue (Functions, Methods) |
base0E |
Magenta (Keywords) |
base0F |
Brown (Deprecated, Embedded) |
theme.colors = [{
red = lib.strings.toInt config.lib.stylix.colors.base0E-rgb-r;
green = lib.strings.toInt config.lib.stylix.colors.base0E-rgb-g;
blue = lib.strings.toInt config.lib.stylix.colors.base0E-rgb-b;
}];uiAccentCustom0 = "${config.lib.stylix.colors.withHashtag.base0D}";background = colors.base00; # => "1a1b26"