<CR>, indent 4
blink.cmp uses the `enter` preset with `completion.list.selection.preselect = false`, so an item is taken only once selected with `<Down>`/`<C-n>` and `<CR>` otherwise inserts a newline. `<C-y>` stays bound as vim's own confirm key. `<Esc>` and `<C-e>` both run `cancel` rather than the preset's `hide`: hiding leaves the auto_insert preview in the buffer without the `additionalTextEdits` and snippet expansion a real accept applies, and builtin ins-completion `<C-e>` likewise restores the typed text. `shiftwidth`/`tabstop` are 4, matching PHP's PSR-12. PHP's `indentkeys` carry `*<Return>`, which reindents the just-finished line on every newline; at width 2 that silently re-indented 4-space files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015D4NsszUG7hy9ghKZ4nfhm
NixOS + home-manager flake
A flake-based NixOS and home-manager configuration: a set of reusable modules plus a ready-to-run example host that assembles them. Use it either way:
- as a starting template — clone, swap in your hardware, rebuild; or
- as modules — import them into your own (private) flake.
Quick start (template)
nix flake init -t github:<owner>/<repo> # or: git clone …
# generate hardware config for THIS machine (the one file you must replace):
sudo nixos-generate-config --show-hardware-config > hosts/example/hardware-configuration.nix
# set your hostName + username in hosts/example/default.nix, then:
sudo nixos-rebuild switch --flake .#example
nixosConfigurations.example is a real, buildable machine assembled from the modules in
modules/. It doubles as living documentation: it shows exactly how the modules wire
together, so there's nothing to reverse-engineer. The only genuinely machine-specific file
is hardware-configuration.nix (NixOS generates it); everything else works from defaults
you can tweak.
Import the modules into your own flake
Add this repo as an input and assemble your machines in a private flake. Below is a
realistic example layout. Structurally only flake.nix is required — every other path
is just one way to organize your config; rename, merge, inline, or drop them freely. The one
bit of content a bootable machine can't skip is its hardware-configuration.nix
(nixos-generate-config output); the hosts/<host>/ directory is only where this example
keeps it.
your-inventory/
├── flake.nix # inputs (baseline + your nixpkgs) + nixosConfigurations.<host>
├── flake.lock
├── system/ # system-level modules
│ ├── regional.nix # time zone / keymap / locale (see Overriding)
│ ├── packages.nix # extra system packages
│ └── tailscale.nix # VPN / network topology
├── home/ # home-manager modules, one file per concern
│ ├── identity.nix # git identity, name/email
│ ├── ssh.nix # SSH hosts (topology — stays private)
│ ├── syncthing.nix # Syncthing peers
│ └── … # personal apps: browser, editor, chat, …
└── hosts/
└── myhost/
├── default.nix # hostname, user account, host-specific system bits
└── hardware-configuration.nix # `nixos-generate-config` output
The flake.nix that wires it together:
{
inputs = {
# You own nixpkgs, so your update cadence is yours — the baseline modules
# build against the versions you pin here.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
baseline.url = "github:<owner>/<repo>";
baseline.inputs.nixpkgs.follows = "nixpkgs"; # baseline builds against YOUR nixpkgs
baseline.inputs.home-manager.follows = "home-manager";
};
outputs = inputs@{ self, nixpkgs, baseline, home-manager, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
baseline.nixosModules.default # generic system baseline
./system/regional.nix # your regional values (see below)
./hosts/myhost # hardware + hostname
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
# Inject your own inputs into your home modules; the public ones ignore them.
home-manager.extraSpecialArgs = { inherit inputs; };
home-manager.users.me.imports = [
baseline.homeModules.default # generic home baseline
./home/identity.nix # your git identity, name/email, …
./home/ssh.nix # your SSH hosts (topology stays private)
# … more personal home modules
];
}
];
};
};
}
This repo ships modules, not pinned packages: they build against the nixpkgs you
declare above, so you decide when packages move. Quickstart alternative: drop your own
nixpkgs and write nixpkgs.follows = "baseline/nixpkgs" to pin to this repo's tested
versions instead — one less thing to think about, but then your update cadence is whatever
this repo pins, not the reverse.
Your machine list, hostnames, and any private specifics stay in your flake — you pull only the generic modules from here.
Updating (day-to-day)
Every package version comes from the nixpkgs you declare — including the packages
this repo's own modules install, since they build against your nixpkgs via follows, not
against anything this repo pins. So the whole upgrade happens in your flake; you never touch
this repo to move packages, even the ones it declares:
nix flake update # bump every input to newest
sudo nixos-rebuild switch --flake .#myhost # build + activate
nix flake update advances nixpkgs to the newest commit on the branch you pinned (e.g.
nixos-unstable or nixos-25.11) — that is what upgrades your packages. home-manager and
baseline follow your nixpkgs, so they move in lockstep with no version skew. To move
one input at a time, name it: nix flake update nixpkgs bumps only packages;
nix flake update baseline pulls the latest modules from this repo (new features/fixes)
without changing any package version. These are independent knobs — routine upgrades need
only the nixpkgs bump.
A release jump (e.g. nixos-25.11 → nixos-26.05) is not a plain update: edit the
branch in the nixpkgs and home-manager URLs in your flake.nix, then update and rebuild.
Overriding the baseline
The public modules are a baseline you extend without forking. Three ways to shape them from your private flake, in rough order of how often you reach for them:
-
Neutral default → your real value. Region-agnostic settings ship as
mkDefaultplaceholders (console.keyMap = "us",time.timeZone = "UTC",en_USlocale). Set the real ones in a private module — nomkForceneeded:# system/regional.nix { time.timeZone = "Europe/Berlin"; console.keyMap = "de"; } -
local.*options. A module that exposes a knob puts it under thelocal.*namespace with a default. Override in one line, e.g. a fuller TeX Live:local.texlive.package = pkgs.texlive.withPackages (ps: [ ps.scheme-full ]);A few
local.*options exist precisely because a module pins a package to a nixpkgs other than the one you declare (a deliberate freeze that would otherwise reach you regardless of your own pin); the option is how you pull it back onto your nixpkgs. -
Extra modules. Anything with no public counterpart is just another module in the import lists above — personal apps, SSH/Syncthing topology, secrets wiring.
Architecture: three layers
Kept apart on purpose. When adding config, decide which layer it belongs to:
- Modules — generic, public (this repo). Reusable building blocks under
modules/, exposed asnixosModules.*/homeModules.*. Anything machine- or user-specific is a module option with a sane default — never hard-code personal values into a module. - Inventory — topology, private (your flake). The actual machines:
nixosConfigurations.<host>, per-hosthosts/<host>/(hardware, hostname), and topology-revealing config (SSH hosts, Syncthing peers, internal IPs). This is what you deploy from; it imports the modules above. Publish the modules, keep the inventory in a private repo. Theexamplehost here is a placeholder-filled stand-in for it. - Secrets — neither repo. Private keys, tokens, passwords never enter any repo.
Provide them as runtime files (
0600, out-of-band) or via a secrets manager that decrypts to a runtime path; nix references only the path.
Layout
flake.nix # inputs + outputs (nixosModules, homeModules, example host, devShells)
modules/ # the reusable library
hosts/example/ # the template host (placeholder hardware-configuration.nix)
Shared *.nix / *.lock live at the top level; per-host files under hosts/<host>/.
Chasing a shutdown/boot hang? modules/shutdown-debug.nix exposes local.shutdownDebug.*
(verbose systemd logging to kmsg, a tty9 debug shell, a shortened stop-timeout) — see its
header comment for how to read the evidence.