"A man and his tools make a man and his trade"
-Vita Sackville-West
"We shape our tools and then the tools shape us"
-Winston Churchill
These are my NixOS/macOS Nix setup.
NixOS | macOS |
---|---|
-
User environment and dotfiles management with
home-manager
. -
CLI-ready workflow with
fish
,zellij
,git
,fish
,gpg
,ssh
,curl
,rsync
, and power tools likebat
,zoxide
,bottom
,fzf
,yazi
,ripgrep
,jq
,just
,lazygit
,lazydocker
,gh
,gh-dash
, and more... -
Developer-ready languages: Nix, Rust, Lua, Python, and Julia.
-
Easy to develop environments with
nix-shell
direnv
, anddevshell
. You can put your soydev TypeScript/JavaScript/NodeJS stuff here. Check the recipes for several Nix shells. -
Text editor with Vim/NeoVim and Helix enabled with the following LSPs:
nil
: Nixbash-language-server
: Bash, Fish, Zsh, shell scripts, etc.rust-analyzer
: Rusttaplo
: TOMLyaml-language-server
: YAMLpyright
: Pythonmarksman
: Markdownvscode-langservers-extracted
: HTML, CSS, and JSONtypst-lsp
: Typst
-
Catppuccin Mocha theme everywhere.
-
Publishing and content CLI tools:
qpdf
,pandoc
,graphicsmagick
,tectonic
, andtypst
. -
age
-encrypted secrets withryantm/agenix
with YubiKey support. Check thesecrets/README.md
for details. -
Apps:
firefox
with Tor Browser also available- Bitcoin tools such as Sparrow wallet and Bisq
- Encrypted backup tool with Cryptomator
- Signal messenger
- Torrenting with Transmission
- Offline password manager with KeePassXC
- Screen Recorder with OBS Studio
This is paranoid build with root on tmpfs
.
This means that everything outside of some directories of /etc
and some directories of /home
will be wiped out.
Read more about this in the NixOs Paranoid Guide
(this is also a good source NixOS tmpfs
as /home
).
-
Hyprland
Wayland window manager:Waybar
status bar.Nemo
file manager.Rofi-wayland
application launcher.Mako
notification daemon.Swaylock-effects
screen locker.NetworkManager
network management tool.Nerdfonts
.
-
Apps:
-
firefox
with the following add-ons:
-
VPN support with
wireguard
-
Keyboard customizations with
keyd
: Caps Lock as Escape (if tapped) and Control (if held). -
Easy and automated disk partitioning with
disko
.
Before starting, remember to enable a BIOS password. And disable Secure Boot.
As root:
-
Prepare a 64-bit NixOS 23.11 minimal iso image or 64-bit NixOS unstable minimal iso image and burn it, then enter the live system. Suppose I have divided two partitions:
/dev/nvme0n1p1
and/dev/nvme0n1p2
-
Format the partitions:
mkfs.fat -F 32 /dev/nvme0n1p1 mkfs.ext4 /dev/nvme0n1p2 # or use LUKS with cryptsetup luksFormat /dev/nvme0n1p2 encryptedroot
or use the
disko
script for bcachefs with LUKS (don't forget to clone the repo first):nix run github:nix-community/disko -- --mode disko linux/disko.nix # verify the mount mount | grep /mnt # you may need to skip some commands in the next "mount" step
-
Mount:
mount -t tmpfs none /mnt mkdir -p /mnt/{boot,nix,etc/nixos} mount /dev/nvme0n1p2 /mnt/nix # or LUKS with mount /dev/mapper/encryptedroot /mnt/nix mount /dev/nvme0n1p1 /mnt/boot mkdir -p /mnt/nix/persist/etc/nixos mount -o bind /mnt/nix/persist/etc/nixos /mnt/etc/nixos
-
Generate a basic configuration:
nixos-generate-config --root /mnt
-
Clone the repository locally:
nix-shell -p git # recursive for git submodules git clone --recursive https://github.com/storopoli/flakes.git /mnt/etc/nixos/flakes cd /mnt/etc/nixos/flakes/ nix develop --extra-experimental-features "nix-command flakes" --extra-experimental-features flakes
-
If you want Secure Boot, now is the time that you should create your keys.
-
Migrate all the custom
hardware-configuration.nix
from/mnt/etc/nixos
into/mnt/etc/nixos/flakes/linux/system.nix
and/mnt/etc/nixos/flakes/linux/filesystem.nix
:vi /mnt/etc/nixos/flakes/linux/system.nix
... # This is just an example # Please refer to `https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/#step-4-1-configure-disks` fileSystems."/" = { device = "none"; fsType = "tmpfs"; options = [ "defaults" "size=12G" "mode=755" ]; }; fileSystems."/nix" = { device = "/dev/disk/by-uuid/49e24551-c0e0-48ed-833d-da8289d79cdd"; fsType = "ext4"; }; fileSystems."/boot" = { device = "/dev/disk/by-uuid/3C0D-7D32"; fsType = "vfat"; }; fileSystems."/etc/nixos" = { device = "/nix/persist/etc/nixos"; fsType = "none"; options = [ "bind" ]; }; ...
-
remove
/mnt/etc/nixos/flakes/.git
:rm -rf .git
-
Username modification: edit
user
in/mnt/etc/nixos/flakes/flake.nix
,/mnt/etc/nixos/flakes/linux/default.nix
, and/mnt/etc/nixos/flakes/linux/wayland.nix
; hostname modification: edit/mnt/etc/nixos/flakes/common/default.nix
to modify the hostName value in the networking property group -
Use the hash password generated by the
mkpasswd {PASSWORD} -m sha-512
command to replace the value ofusers.users.<name>.hashedPassword
in/mnt/etc/nixos/flakes/linux/default.nix
(there are two places to be edited) -
Perform install:
nixos-install --no-root-passwd --flake .#laptop
-
Reboot
reboot
-
If you want Secure Boot, now is the time that you should continue the setup.
-
Enjoy it!
Based on the Quickstart Guide from
lanzaboote
-
Verify if the ESP is mounted at
/boot
:bootctl status
-
Create your keys with
sbctl
(available in the Flake shell, i.e.nix develop .
)$ sudo sbctl create-keys [sudo] password for user: Created Owner UUID 8ec4b2c3-dc7f-4362-b9a3-0cc17e5a34cd Creating secure boot keys...✓ Secure boot keys created!
When it is done, your Secure Boot keys are located in
/etc/secureboot
.sbctl
sets the permissions of the secret key so that only root can read it.
-
Rebuild your system and check the
sbctl verify
output:$ sudo sbctl verify Verifying file database and EFI images in /boot... ✓ /boot/EFI/BOOT/BOOTX64.EFI is signed ✓ /boot/EFI/Linux/nixos-generation-355.efi is signed ✓ /boot/EFI/Linux/nixos-generation-356.efi is signed ✗ /boot/EFI/nixos/0n01vj3mq06pc31i2yhxndvhv4kwl2vp-linux-6.1.3-bzImage.efi is not signed ✓ /boot/EFI/systemd/systemd-bootx64.efi is signed
It is expected that the files ending with
bzImage.efi
are not signed. -
Enable Secure Boot. On Framework Laptops:
- Select "Administer Secure Boot"
- Select "Erease all Secure Boot Settings"
- When you are done, press
F10
to save and exit.
Once you've booted your system into NixOS again, you have to enroll your keys to activate Secure Boot.
$ sudo sbctl enroll-keys --microsoft
Enrolling keys to EFI variables...
With vendor keys from microsoft...✓
Enrolled keys to the EFI variables!
Finally, reboot and check if Secure Boot is activated and in user mode:
$ bootctl status
System:
Firmware: UEFI 2.70 (Framework 3.03)
Firmware Arch: x64
Secure Boot: enabled (user)
TPM2 Support: yes
Boot into FW: supported
-
First, update the input in
flake
:# update the specified input nix flake lock --update-input <foo> <foo> # or update all inputs nix flake update # also you can reclaim storage with nix-collect-garbage -d
-
Then, rebuild and switch to the system after rebuild:
doas nixos-rebuild boot --flake .#<hostname>
Sources: manpage of
wg-quick
, Mullvad WireGuard on Linux terminal > IVPN Autostart WireGuard in systemd, and IVPN WireGuard Kill Switch
For the extra paranoid, you can use VPNs without installing their apps. You will need WireGuard.
-
Create your configuration in
/etc/wireguard/wg0.conf
. You can also namewg0.conf
whatever you want. Any free-form string[a-zA-Z0-9_=+.-]{1,15}
will work. These configs are generally provided by your VPN provider. They generally look something like this:[Interface] PrivateKey = abcdefghijklmnopqrstuvwxyz0123456789= Address = x.y.z.w/32 DNS = x.y.z.w [Peer] PublicKey = abcdefghijklmnopqrstuvwxyz0123456789= Endpoint = sub.wg.domain.tld:9999 AllowedIPs = 0.0.0.0/0
-
Add "kill switch" configs. Add the following two lines to the
[Interface]
section, just before the[Peer]
section:PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
You may get a problem to connect to your local network. You can modify the kill switch, so it includes an exception for your local network, for example
! -d 192.168.1.0/24
:PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d 192.168.1.0/24 -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d 192.168.1.0/24 -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
-
Make sure that you have the correct permissions, so only
root
can read them:sudo chown root:root -R /etc/wireguard && sudo chmod 600 -R /etc/wireguard
-
Start the WireGuard connection with:
sudo wg-quick up wg0 # to disconnect sudo wg-quick down wg0
If you are using a Linux distribution that comes with systemd
,
you can autostart a WireGuard connection with:
sudo systemctl enable [email protected]
sudo systemctl daemon-reload
sudo systemctl start wg-quick@wg0
To check status: sudo systemctl status wg-quick@wg0
To remove the service and clean up the system:
sudo systemctl stop wg-quick@wg0
sudo systemctl disable [email protected]
sudo rm -i /etc/systemd/system/wg-quick@wg0*
sudo systemctl daemon-reload
sudo systemctl reset-failed
One way to test a down tunnel is to delete the IP address from the WireGuard network interface, like this via the Terminal:
sudo ip a del [IP address] dev [interface]
In this example, it’s possible to remove x.y.z.w
from the wg0
interface:
sudo ip a del x.y.z.w/32 dev wg0
The PostUP
iptables rule from above restricts all traffic to the tunnel,
and all outgoing attempts to get traffic out fail.
To gracefully recover from this,
you will likely have to use the wg-quick
command to take the connection down,
then bring it back up.
The macOS configs are minimalist in approach and geared towards enhancing security and privacy. It uses the best practices described in the MacOS Hardening Guide and the MacOS Security and Privacy Guide.
Honestly, Homebrew is a Ruby bloatware. It is slow, non-reproducible, and a mess to maintain.
Nix is superior in every way. It is fast as fuck, and it is 100% reproducible. Migrating to new hardware or rebuilding old hardware after a wipe is a breeze.
-
Tiling window manager with Rectangle.
-
Status Bar with stats
-
Apps:
- Alacritty
- Keyboard customization with Karabiner-Elements: Caps Lock as Escape (if tapped) and Control (if held).
- Android file transfer support
- IINA as the default video player
-
Common developer enhancements in Finder and Search
-
MacOS privacy and security enhancements
-
Debloating of animations
Before installing anything you'll need to prepare your system:
-
Don't register an Apple ID
-
Enable Lockdown Mode
-
Disable all Sharing stuff: General > Sharing: Disable All
-
Disable Notifications previews:
- Notifications > Show Previews: Never
- Notifications: Disable "Allow notifications when the screen is locked"
- Lock Screen > Require password immediately
-
Change NTP Server: General > Date & Time > Source: Change to "pool.ntp.org"
-
Set the smart battery saver: Boost mode on AC and Low Power mode on battery
-
Disable Siri:
- Siri and Spotlight: Disable "Ask Siri"
- Siri and Spotlight > Siri Suggestions > Disable all
-
Disable Analytics:
- Privacy and Security > Analytics > Improvements: Disable all
- Privacy and Security > Apple Advertising > Disable personalized ads
- Game Center: Disable all
-
Install Xcode Command Line Tools:
xcode-select --install
-
Install Nix using the official installer:
sh <(curl -L https://nixos.org/nix/install) --daemon
-
Enable Flake support:
echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf
-
Install
nix-darwin
:# aarch64 nix run nix-darwin -- switch --flake .#macbook # x86_64 nix run nix-darwin -- switch --flake .#macbook_x86
-
Apply changes to your system:
darwin-rebuild switch --flake .
-
First, update the input in
flake
:# update the specified input nix flake lock --update-input <foo> <foo> # or update all inputs nix flake update # also you can reclaim storage with nix-collect-garbage -d
-
Then, rebuild and switch to the system after rebuild:
nix --experimental-features 'nix-command flakes' build '.#darwinConfigurations.macbook.system' nix run --extra-experimental-features 'nix-command flakes' nix-darwin -- switch --flake . # or if nix-command and flakes are enabled: nix run nix-darwin -- switch --flake .