Building your fleet¶
You've installed GISNIX once and have a working machine. This is what to do next: live with that one host productively, then grow to two, ten, or however many machines you end up running — while keeping GISNIX's own updates flowing in for free.
What you actually have¶
The installer left you ~/nixos-config: a small flake that pins GISNIX as
an input and points at your one host.
{
inputs.gisnix.url = "github:kartoza/gisnix";
outputs = { self, gisnix, ... }: {
nixosConfigurations.myhost = gisnix.lib.mkHost "myhost" {
hostPath = ./hosts/myhost;
};
};
}
Every package, every desktop profile, every overlay comes from GISNIX. Your
own repo holds exactly three things: hosts/myhost/ (its hardware, disk
layout, and which bundles it takes), users/ (your account, your SSH
key), and this flake.nix gluing the two to GISNIX. That's the whole
point — GISNIX carries the opinion about what a good GIS workstation looks
like, your repo carries the facts about your own machines.
Living with one host¶
Ticks bundles on and off, shows you a diff, writes nothing until you
confirm. No GISNIX checkout needed — this reads GISNIX's bundle registry
directly from the flake input and edits your own hosts/myhost/config.nix
in place. #bundles (no host argument) is the read-only version — what
exists, what's in it, what implies what.
If you'd rather hand-edit config.nix, that works too — every bundle is
listed there, commented out; uncomment a line to take it. Either way,
apply the change with:
Add --flake to also pull in whatever's changed upstream — new GISNIX
commits your flake.lock hasn't seen yet, most immediately COSMIC itself,
which tracks nixos-unstable rather than the stable channel your first boot
used (see the quickstart's note on why that trade exists).
gisnix update --flake=gisnix is the one-line version of "update this pin
and nothing else."
Making it your fleet's repo¶
Before adding a second machine, put ~/nixos-config somewhere durable:
cd ~/nixos-config
git init
git add -A
git commit -m "first host"
git remote add origin git@github.com:you/nixos-config.git
git push -u origin main
This repo is now the private layer sitting on top of GISNIX — the same relationship kartoza/nix-config has to GISNIX itself, just smaller. Nothing about it is GISNIX-specific: it's a plain git repo you own outright.
Adding a second machine¶
Boot the GISNIX installer on the new machine and go through the wizard as
before — it'll generate its own small hosts/<name2>/ the same way it did
the first time. What's different this time is where that directory ends
up: move it into your existing repo rather than leaving it as its own
standalone flake.
# on the new machine, before or after its own first rebuild:
scp -r hosts/name2 you@your-git-host:~/nixos-config-checkout/hosts/
scp users/name2.nix you@your-git-host:~/nixos-config-checkout/users/ # if it added a new user
(Or copy by hand however suits you — a USB stick, rsync, pasting into an
editor. The installer's generated files have no host-specific magic beyond
what's readable in hosts/name2/config.nix.)
Then, in your fleet repo, either add a second line:
or switch to mkFleet, which discovers every hosts/*/config.nix for you
— worth doing once you're past two or three hosts:
Commit, push, and run gisnix update on the new machine against your
real repo (not the installer's throwaway one).
Why this is manual right now
gisnix create-host — the command for adopting an already-installed
machine into a flake — is designed to run from a single checkout that
has both GISNIX's own tooling and your hosts/ together, which a thin
downstream repo deliberately doesn't have. Copying the generated
hosts/<name>/ directory across by hand is the honest current
answer, not a workaround for a bug — see Building on
GISNIX for
where this is tracked.
Keeping private things private¶
Two kinds of content never belong in GISNIX, and both attach to
gisnix.lib.mkHost/mkFleet the same way — extraModules:
Your own projectConfig and fleet registry. GISNIX's own defaults
are placeholders (example.com, a generic nixosStateVersion). Supply
your real ones once your fleet has values worth keeping straight —
your actual domain, and (if you want ssh othermachine to resolve
without a hosts-file entry you maintain by hand) a real fleet registry:
gisnix.lib.mkFleet ./hosts {
projectConfig = import ./config.nix;
fleet = import ./hosts/fleet.nix;
};
Anything a shared GISNIX module deliberately doesn't carry. A few of
GISNIX's modules are generic on purpose where a real value would have to
be private — trusting an internal CA certificate is the clearest example:
GISNIX's services-system bundle exposes an extraCA.certificateFiles
option, empty by default, rather than shipping anyone's actual chain.
gisnix.lib.mkHost "myhost" {
hostPath = ./hosts/myhost;
extraModules = [
{ extraCA.certificateFiles = [ ./resources/my-ca-chain.crt ]; }
];
};
The same pattern covers anything else specific to you: a private script, an
internal-only application, a fleet-wide VPN dispatcher. Small modules in
your own repo, layered on via extraModules, never touching GISNIX's tree.
Building on GISNIX has the full
reference for gisnixRoot, per-host overrides via mkFleet's
perHostArgs, and what's proven to work versus still a known gap.
Contributing back¶
If a fix or a bundle you built for your own fleet is generically useful —
not tied to your own hostnames, secrets, or internal services — it likely
belongs in GISNIX itself rather than staying private. AGENTS.md in
GISNIX's own repo covers the rules of the road (bundle conventions, what
the ISO does and doesn't ship, docs voice); a PR against
kartoza/gisnix is the way in. Every
fleet running GISNIX gets the improvement the next time it updates the
pin — that's the whole reason to keep the split between "opinion" and
"your machines" honest in the first place.