My NixOS configuration files.
lucie@T495
OS NixOS
(\ /) Kernel 6.12.55
( . .) Shell zsh
c(")(") WM wlroots wm
nh is a Nix helper tool that provides prettier output, automatic garbage collection, and better UX than raw nix commands.
Rebuild home configuration:
nh home switch .Rebuild system configuration:
nh os switchUpdate flake inputs and rebuild:
nh home switch . --updateClean old generations and garbage collect:
nh clean allSearch for packages:
nh search <package-name>Apply system configuration:
sudo nixos-rebuild switch --flake .#<hostname>Apply user configuration:
home-manager switch --flake .#<user>configuration.nix- System-wide NixOS configurationhome.nix- User-specific home-manager configurationflake.nix- Flake definition with inputs and outputshardware-configuration.nix- Hardware-specific settingsmodules/- Modular configuration components
Update flake inputs:
nix flake updateRebuild and switch system configuration:
sudo nixos-rebuild switch --flake .#<hostname>Test configuration without switching:
sudo nixos-rebuild test --flake .#<hostname>Rebuild configuration without switching (bootable):
sudo nixos-rebuild boot --flake .#<hostname>List system generations:
sudo nix-env --list-generations --profile /nix/var/nix/profiles/systemRollback to previous generation:
sudo nixos-rebuild switch --rollbackSystem-wide garbage collection:
sudo nix-collect-garbage -dUser garbage collection:
nix-collect-garbage -dDelete generations older than 30 days:
sudo nix-collect-garbage --delete-older-than 30dOptimize nix store:
sudo nix-store --optimizeCheck disk usage:
nix path-info -Sh /run/current-systemSearch for packages:
nix search nixpkgs <package-name>Install package temporarily:
nix shell nixpkgs#<package-name>Run package without installing:
nix run nixpkgs#<package-name>Check package dependencies:
nix-store --query --references /run/current-systemSwitch home configuration:
home-manager switch --flake .#<user>List home generations:
home-manager generationsCheck home-manager news:
home-manager news --flake .#<user>Rollback home configuration:
home-manager switch --rollbackEnter development shell:
nix developCheck flake configuration:
nix flake checkShow flake metadata:
nix flake metadataDebug build issues:
nix log /nix/store/<derivation-path>Show package information:
nix show-derivation nixpkgs#<package-name>Show current system closure size:
nix path-info -Sh /run/current-systemList all installed packages:
nix-env -qaCheck service status:
systemctl status <service-name>View system logs:
journalctl -u <service-name>Stage and commit changes:
git add .
git commit -m "description of changes"Amend last commit:
git commit --amendReset to last commit:
git reset --hardReset to previous commit:
git reset --hard HEAD~1Stash uncommitted changes:
git stashRestore specific file:
git restore <file>NixOS cannot run dynamically linked executables intended for generic Linux environments out of the box. This configuration includes nix-ld to provide compatibility.
Most external binaries should work automatically with nix-ld enabled:
./some-external-binaryConfigure additional libraries in system configuration:
programs.nix-ld = {
enable = true;
libraries = with pkgs; [
stdenv.cc.cc
openssl
curl
zlib
# Add other libraries as needed
];
};For binaries requiring more complex setup:
# Install nix-alien
nix shell nixpkgs#nix-alien
# Run external binary through nix-alien
nix-alien ./external-binaryCreate temporary standard Linux filesystem environment:
nix shell --impure nixpkgs#fhs --command bash
# Run your binary inside this environmentLaunch Steam games with GameMode:
gamemoderun %command%i2p router in rootless podman with --network=host.
- web console:
http://proxmox-lab.tail5296cb.ts.net:7070 - http proxy:
proxmox-lab.tail5296cb.ts.net:4444 - socks proxy:
proxmox-lab.tail5296cb.ts.net:4447 - sam bridge:
proxmox-lab.tail5296cb.ts.net:7656 - i2pcontrol api:
https://127.0.0.1:7650(localhost only) - p2p port:
31000(tcp/udp, forwarded on the router) - config:
modules/i2pd.nix - data:
~/i2pd/
in about:config set these:
network.proxy.allow_hijacking_localhosttotruekeyword.enabledtofalsenetwork.dns.blockDotOniontofalse
then in firefox settings, network settings, manual proxy configuration:
- http proxy:
proxmox-lab.tail5296cb.ts.net, port4444 - check "also use this proxy for HTTPS"
- check "proxy DNS when using SOCKS v5"
for monitoring/automation, default password is itoopie, change it before using.
# get a token
curl -sk -X POST https://127.0.0.1:7650 \
-d '{"id":1,"jsonrpc":"2.0","method":"Authenticate","params":{"API":1,"Password":"itoopie"},"Token":""}'
# query router info (use the token from above)
curl -sk -X POST https://127.0.0.1:7650 \
-d '{"id":2,"jsonrpc":"2.0","method":"RouterInfo","params":{
"i2p.router.status":"",
"i2p.router.net.status":"",
"i2p.router.net.tunnels.participating":"",
"i2p.router.netdb.knownpeers":"",
"i2p.router.uptime":"",
"i2p.router.net.bw.inbound.1s":"",
"i2p.router.net.bw.outbound.1s":""
},"Token":"<TOKEN>"}'methods: Authenticate, RouterInfo, RouterManager, NetworkSetting.
- enable i2p support in the client
- set i2p router host to
proxmox-lab.tail5296cb.ts.net - set sam port to
7656
--network=hostbecause pasta caused i2pd to report symmetric NAT- i2pd auto-detects external ip through peer testing
- port 31000 forwarded on the router (NAT/PAT) and allowed through ufw
encrypted ipv6 mesh network (200::/7) in rootless podman with a socks5 proxy sidecar.
- config:
modules/yggdrasil.nix - private key: sops-managed (
yggdrasil_private_keyin vault) - container images: nix-built (official docker image stuck on 0.4.2)
- socks proxy:
proxmox-lab.tail5296cb.ts.net:1080
in browser proxy settings, set only the socks host:
- socks host:
proxmox-lab.tail5296cb.ts.net, port1080, socks v5 - check "proxy DNS when using SOCKS v5"
- leave http/ssl/ftp proxy fields empty
site directory and service list: https://yggdrasil-network.github.io/services.html
# check status
systemctl --user status yggdrasil
systemctl --user status yggdrasil-proxy
# restart
systemctl --user restart yggdrasilsecure hypervisor access via tailscale.
- url:
https://proxmox-lab.tail5296cb.ts.net:8006 - certs: automated via tailscale-certs + proxmox-cert-sync
federated music server (ActivityPub). shares libraries with friends.
- url:
https://funkwhale.luvcie.love - config:
modules/funkwhale.nix - data:
~/funkwhale/data/ - music mount:
/mnt/media/music(read-only, same as navidrome)
# create admin account
podman exec -it funkwhale manage createsuperuserfunkwhale does not auto-scan, must import manually via the container shell.
- create a library in the web ui (your profile > libraries)
- copy the library UUID from the url
- run the import:
podman exec funkwhale sh -c "manage import_files <library-uuid> /music --recursive --in-place --noinput" 2>/dev/null2>/dev/null hides django deprecation warnings.
caddy terminates TLS for internal services using tailscale certs. currently proxies CouchDB.
- https endpoint:
https://proxmox-lab.tail5296cb.ts.net:5443 - config:
modules/caddy.nix - data:
~/caddy/data/ - image:
docker.io/library/caddy:2 - certs: tailscale certs from
~/.tailscale-certs/(auto-renewed daily by tailscale-certs service)
systemctl --user status caddy
systemctl --user restart caddy
curl -k https://proxmox-lab.tail5296cb.ts.net:5443/self-hosted CouchDB for real-time obsidian vault sync via the Self-hosted LiveSync plugin. tailscale-only, behind caddy for TLS.
- url:
https://proxmox-lab.tail5296cb.ts.net:5443 - fauxton dashboard:
https://proxmox-lab.tail5296cb.ts.net:5443/_utils - local (no TLS):
http://localhost:5984 - config:
modules/couchdb.nix - data:
~/couchdb/data/ - credentials: sops-managed (
couchdb_username,couchdb_password) - image:
docker.io/library/couchdb:3
important: set up one device at a time. the first device creates the database and encryption salt, the second device must pull from it, not create its own.
- install Self-hosted LiveSync from community plugins
- open the plugin settings and configure the remote:
- URI:
https://proxmox-lab.tail5296cb.ts.net:5443 - username/password: from sops vault (
sops vault/encrypted-sops-secrets.yaml) - database name: use the same name on all devices (e.g.
obsidianlucy) - encryption passphrase: use the same passphrase on all devices
- URI:
- set sync mode to LiveSync under "Sync Settings"
- for the first device: just enable — it will create the database and push
- for additional devices: after entering the same settings, use "Rebuild everything → Fetch from Remote" to pull existing data. this ensures the device picks up the existing PBKDF2 salt instead of generating a new one
- AFTER EVERY SETUP OR REBUILD: GO TO SYNC SETTINGS AND SET THE SYNC MODE TO "LIVESYNC". it resets to on-demand/periodic after rebuilds and will NOT sync in real-time until you change it back, do this on every device.
do NOT run the "Self-hosted LiveSync config doctor" on a new device. the doctor changes settings (chunk size, case sensitivity, etc.) and then prompts you to overwrite the server with the current device's data. on a new/empty device this wipes the remote database clean and breaks sync for all other devices. only use the doctor on your primary device if you know what each setting does.
- nothing syncs: check that the sync mode is set to LiveSync (not periodic/on-demand). the plugin doesn't auto-start syncing after initial setup
Decryption with HKDF failed: the encryption salt doesn't match between devices. this happens when both devices were set up independently. fix: disable plugin on all devices, nuke the remote db (curl -sk -X DELETE https://proxmox-lab.tail5296cb.ts.net:5443/obsidianlucy -u user:pass), then set up from scratch — first device pushes, second device fetchesLoad failedon specific notes: stale data in the local IndexedDB cache from a previous broken setup. use "Rebuild: Fetch everything from remote" to fix- 404 errors in dev console: normal. PouchDB checks for checkpoint documents that don't exist yet on a fresh setup. the plugin logs
The above 404 is totally normalright after - dev console access: desktop
Ctrl+Shift+I/ macCmd+Option+I - the device must be on tailscale to reach the server
systemctl --user status couchdb
systemctl --user restart couchdb
curl -u user:pass http://localhost:5984/- no
:roon ini mounts: the couchdb entrypoint runschmod/chownon everything under/opt/couchdb/etc/withset -e— a read-only mount causes a silent exit 1 with zero logs [admins]section format: the entrypoint greps for\[admins\]\n[^;]\w+— the admin username must be on the very next line after[admins], no blank lines allowed- put credentials in the ini, not env vars:
COUCHDB_USER/COUCHDB_PASSWORDenv vars are processed by the entrypoint, butrequire_valid_user = truein a custom ini triggers CouchDB's preflight check before the entrypoint injects the admin. define them under[admins]in the ini instead - sops template permissions: sops-nix renders templates as
0400by default. couchdb runs as a non-root user inside the container. usemode = "0444"on the template - debugging silent crashes: override entrypoint with
bash -c 'bash -x /docker-entrypoint.sh /opt/couchdb/bin/couchdb'to trace
file server with sops-managed accounts.
- url:
https://files.luvcie.love - edit passwords:
sops vault/encrypted-sops-secrets.yaml
terminal chat room — users connect by SSHing into the server. no accounts, just pick a username.
- url:
ssh.luvcie.love(via cloudflare tunnel) - config:
modules/sshchat.nix - binary: pre-built from github releases, fetched via
fetchurlwith pinned hash (fully reproducible) - host key:
~/sshchat/host_key— generated once on first activation, persists across rebuilds - service:
systemctl --user status sshchat
requires cloudflared installed once on the client:
# install cloudflared (once)
# arch: yay -S cloudflared | mac: brew install cloudflared | others: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
# add to ~/.ssh/config (once)
Host ssh.luvcie.love
ProxyCommand cloudflared access ssh --hostname %h
# connect (pick any username)
ssh yourname@ssh.luvcie.lovesystemctl --user status sshchat
systemctl --user restart sshchat
journalctl --user -u sshchat -fupdate the version and hash in modules/sshchat.nix:
# get new hash
nix-prefetch-url --type sha256 https://github.com/shazow/ssh-chat/releases/download/vX.X/ssh-chat-linux_amd64.tgz
nix hash to-sri --type sha256 <hash>then update version and hash in the module and run nh home switch ..
websocket terminal bridge for the luvcie.love website terminal. runs the luvcie-lab podman container (nix-built) and exposes it over a websocket at wss://term.luvcie.love.
- config:
modules/shell-bridge.nix - image: nix-built via
dockerTools.buildImage, single layer (not layered — avoids vfs disk waste) - service:
systemctl --user status shell-bridge
to avoid reloading the container image on every nh home switch (podman load is slow and would trigger the activation timeout), the activation script uses a marker file:
~/.local/share/containers/luvcie-lab-loaded
this file contains the nix store path of the currently loaded image. on each activation, if the file exists and matches the current image derivation, podman load is skipped. if the paths differ (new build) or the file is missing, the image is reloaded.
if you delete the image in portainer, the marker still exists so nh home switch won't reload it. fix:
rm ~/.local/share/containers/luvcie-lab-loaded
nh home switch ~/dotnixessystemctl --user status shell-bridge
systemctl --user restart shell-bridge
journalctl --user -u shell-bridge -f