# nix-homelab describes a whole homelab as one declarative NixOS flake

> This repository is a complete NixOS homelab: servers, desktops and network devices configured from a single flake, with Clan providing the host inventory, secret generation and backups. It mixes native NixOS services with Podman containers where a package does not exist, and it documents the one machine that boots unencrypted on purpose.

**badele/nix-homelab** — Homelab and dotfiles made with NixOS

- Repository: https://github.com/badele/nix-homelab
- Stars: 533 · Forks: 16
- Language: Nix
- License: GPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/badele-nix-homelab

## One flake for servers, desktops and network gear

The repository describes itself as my personal homelab infrastructure, fully managed with NixOS and Clan, and says it contains all configurations for my servers, desktops and network devices. The strapline at the top is centralize the configuration of your immutable NixOS machines.

What is being managed is listed plainly. The servers are a public VPS on Hetzner and Infomaniak, physical servers, and Raspberry Pi machines. The desktops are personal laptops and workstations. Everything is declarative, reproducible and version-controlled whenever possible.

The repository layout is the map of that. There is a machines/ directory for the hosts, modules/ for the reusable service definitions, vars/ for settings, users/ for user accounts, templates/ for scaffolding, and an inventory.json at the root that is Clan's machine list. Then the supporting pieces: terraform/ for the VPS provisioning, sops/ with a .sops.yaml at the root for encrypted secrets, flakes/ and a flake.nix with a flake.lock for the Nix side, and a justfile for the human-facing commands.

That last file is where the ergonomics live, and it is worth reading because it shows what the day-to-day work actually is. The justfile is grouped into sections with group comments, and the clan group holds the operational recipes:

```text
@machine-add MACHINE:
    clan machines create {{MACHINE}}
    git add machines/{{MACHINE}}
```

Adding a host is a clan command followed by a git add of the directory it created, which is the shape of the whole workflow: a machine is created through the tool, and the result lands in version control for you to commit.

The update recipes work the same way:

```text
@machine-update +HOST:
    clan machines update {{HOST}}

@local-update :
    just machine-update "$(hostname)"
```

The plus sign on the HOST parameter is a just convention for a variadic argument, so a single recipe updates several hosts at once, and the comment says so: update machines, multiple hosts can be updated by space separated list. The local-update recipe simply calls the same thing with the current hostname, which is a small touch that makes the local case no different from the remote one.

There is also a recipe for reading a machine's disk identifiers, which shells out to lsblk over ssh with a specific column set, and a recipe for reading a Clan's variables. The file continues past what is visible here with a Hetzner section that pulls the cloud token from Clan secrets and queries the hcloud API.

## Clan, and the three things it takes off your hands

The README is direct about why the project uses Clan rather than plain NixOS configuration. Clan is described as the backbone of independent infrastructure, and the author's own summary of the intent is Kill the cloud, build your darkcloud.

Four benefits are listed. Simple host management comes with an easy inventory system. Automatic secrets means built-in secret generation and management. Backup made easy means an integrated backup solution. And declarative means everything in code, no manual steps.

Those four map onto the four things a homelab configuration normally accumulates as manual work. A host inventory that is a list of machines rather than a directory of similar-looking files. Secrets that are generated once and stored encrypted rather than pasted into a config. Backups that are part of the system definition rather than a cron job someone remembered. And an inventory.json at the root is the concrete form of that first item: the list of machines is data, and the justfile commands read and write it through clan.

The repository has a dedicated docs/clan.md page linked from the README, and there is a separate CONVENTIONS.md file at the root, which suggests the project has written down its own conventions separately from Clan's documentation. There is also a TODO.md, a pre-commit configuration with its own pre-commit-scripts/ directory, an .envrc file, and a .pre-commit-config.yaml, so the flake and the tooling are checked before they are committed.

One detail from the justfile is telling about how secrets are handled. The Hetzner recipe does not read a token from the environment the operator sets. It does this:

```text
@clan-vars-get HOST VARNAME:
    clan vars get {{HOST}} {{VARNAME}}
```

and in the Hetzner section, the cloud token is fetched with clan secrets get and exported into the environment of the command that needs it. The credential comes out of the encrypted store at the moment it is used, rather than living in a dotfile or a shell profile, and that is what the automatic secrets feature buys in practice.

## The supporting directories, and the checks that run before a commit

The rest of the top-level entries describe the parts of a configuration repository that are not the configuration itself, and each one answers a specific question about how the project is maintained.

There is a nixpkgs.nix file, which in a Nix flake setup is where a pinned nixpkgs revision is declared so that every machine resolves to the same package set rather than whatever the channel happens to be. Alongside it sit flake.nix and flake.lock, the flake definition and its lock file, and a flakes/ directory for additional flake outputs.

There is a templates/ directory, which is how new machines and new services get scaffolded. A template plus a generator means the shape of a new host is defined once rather than copied by hand, and that is what makes the declarative claim hold when the fleet grows from three machines to thirty.

There is a users/ directory next to vars/ and machines/, which separates the three axes of a configuration: who the users are, what the settings are, and what the machines are. That separation is the difference between a configuration you can change and one you have to read.

Then the secret handling. sops/ holds the encrypted secrets and .sops.yaml at the root declares the encryption rules, so the repository can be public without the credentials in it being readable. That combination of SOPS encryption and Clan's secret generation is why the justfile can fetch a cloud token on demand.

The terraform/ directory covers the other half of the fleet: the VPS servers, which are rented rather than owned, and therefore created and destroyed through a cloud provider's API rather than through a NixOS rebuild.

And the checks. There is a .pre-commit-config.yaml and a pre-commit-scripts/ directory, so the repository runs its own scripts before a commit is accepted, plus an .envrc for direnv users and a .github/ directory for the workflows. There is also CONVENTIONS.md, which records how the project expects things to be done, and a TODO.md, which is the sort of file that tells you the project is actively worked on rather than merely published.

inventory.json at the root is the piece that ties the operational tools together: it is the machine list that the clan recipes read and modify, and it is version-controlled alongside the machines/ directory that clan creates entries in.

## The service strategy: NixOS first, Podman where a package does not exist

The deployment strategy is described as a hybrid approach, and the reasoning for each half is given.

NixOS services first, because most applications run as native NixOS services. Then Podman when needed, for some applications that use containers for three reasons: to use plugins or features not well-supported in NixOS, with DokuWiki named as the example; to avoid service interruptions during system updates; and to maintain stability during version upgrades.

The claim is that this gives the best of both worlds, NixOS reproducibility with container flexibility. That is a fair characterisation, and the three reasons are the ones that come up in practice. DokuWiki is a good example because its plugin ecosystem lives in the application's own directory rather than in the Nix store, so a declarative NixOS module for it either has to package the plugins or leave them unmanaged, and a container sidesteps the question.

The containerisation reason that carries the most weight for a homelab is the second one. On a NixOS system, upgrading a service is a system rebuild, and the rebuild replaces the service. In a container, the image is what changes, and the underlying system does not. For a homelab that has to keep running while you experiment, that difference is the whole argument.

The topics confirm the shape of the stack: podman for containers, proxmox and virtualization for the hypervisor layer, nix-flake and nixos-configuration for the Nix side, and home-manager for the desktops. The mention of home-manager matters for the laptops: the same repository configures the machines that leave the building, and home-manager is how the user-level configuration travels with the user rather than living on the disk image.

The service catalogue in the README is a generated table, marked with a comment that names the template that produces it, and the anchors in the links point at sections of a features document called all-features.md. The visible entries include ACME for certificates, Authelia and authentik for single sign-on, Blocky as a DNS ad-blocker, Caddy as the web server, DokuWiki, Gatus for a status page, GoAccess as a log analyser, Grafana for metrics, Grist for spreadsheets, Homepage as a dashboard and it-tools as a collection of utilities. Some links point at a deprecated-services section, which is a useful signal: the author marks what has been retired rather than quietly leaving it in the tree.

## Disk encryption, and the one machine that boots without a passphrase

Disk encryption is enabled whenever possible across the homelab, and the reasoning is given in two parts. It is especially important on laptops because they are mobile devices and therefore more exposed to loss or theft, and the same principle also applies to servers, since a stolen machine may still contain sensitive data.

Then the exception, stated by name. The constellation machine is the main exception to this rule. It is a critical private server and provides core services such as DNS ad blocking for the local network, and after a power outage it must be able to boot automatically without requiring a manual passphrase entry.

That is a good reason and a specific trade. A machine that provides DNS to the local network and has to come back after a power cut cannot wait at a boot prompt for somebody to type a passphrase, and in a homelab the person responsible for it may be away. The cost is that the disk of that one machine is readable if the machine is stolen, which for a home setup is a smaller exposure than for a laptop that travels.

Naming the exception rather than leaving it implicit is what makes the policy usable. If you adopt this configuration, you are adopting a rule with a documented exception, which is much easier to review than a rule with silent gaps.

The other security-adjacent piece in the tree is the sops/ directory with .sops.yaml at the root. SOPS is the encryption layer for the secrets, and the .sops.yaml file is where the encryption rules live, naming which keys or recipients apply to which paths. Combined with the Clan's automatic secrets feature, that gives a repository where no credential is stored in plain text and none has to be exported by hand.

The README also links to a Grafana dashboard and a public server, which is the author's way of showing the homelab running, and the presence of a public URL for a homelab that keeps its services private is a deliberate choice rather than an oversight.

## Conclusion

Adopt this configuration as a reference for how a real homelab is put together on NixOS with Clan, because the README explains the decisions rather than just listing them, including why disk encryption is skipped on one specific machine and when a service is containerised instead of native. Do not adopt it as a starting point for your own infrastructure without expecting to rewrite it, since this is described as personal infrastructure with its own hostnames, its own Hetzner project and a machine called constellation. Verify first that you understand the Clan dependency, since the inventory, the secrets and the backups all come from it and the justfile recipes call clan directly, that your own machines are added through the clan machines create path rather than by editing files, and that the inventory and vars directories are where your own hosts and settings belong, since the secrets are handled through sops with a .sops.yaml at the root.

## FAQ

### What is badele/nix-homelab?

It is a complete NixOS homelab setup managing servers, which are a public VPS on Hetzner and Infomaniak, physical servers and Raspberry Pi machines, plus desktops including personal laptops and workstations. Everything is declarative, reproducible and version-controlled whenever possible.

### What does Clan add to the NixOS configuration?

The README lists four benefits: simple host management with an easy inventory system, automatic secrets with built-in secret generation and management, an integrated backup solution, and declarative configuration with everything in code. The inventory is the inventory.json file at the repository root, and the justfile commands call clan directly to read and modify it.

### When does this homelab use Podman instead of native NixOS services?

Most applications run as native NixOS services, and some use containers for three stated reasons: to use plugins or features not well-supported in NixOS, with DokuWiki given as the example, to avoid service interruptions during system updates, and to maintain stability during version upgrades.

### Which machine in the homelab does not use disk encryption?

The constellation machine, because it is a critical private server providing core services such as DNS ad blocking for the local network and has to boot automatically after a power outage without a manual passphrase entry. Disk encryption is enabled whenever possible everywhere else, especially on laptops.

### How do I add a machine to this homelab?

The justfile has a machine-add recipe that runs clan machines create with the machine name and then git adds the machines directory it created. Updating uses clan machines update, and the local-update recipe calls the same command with the current hostname.

### How are secrets handled in this NixOS homelab?

Secrets are stored encrypted in the sops/ directory with a .sops.yaml file at the root holding the encryption rules. The justfile reads them on demand through clan, for example the Hetzner recipe exports a token fetched with clan secrets get before calling the hcloud API.

## Sources

- [badele/nix-homelab on GitHub](https://github.com/badele/nix-homelab)
- [Issues](https://github.com/badele/nix-homelab/issues)
- [License: GPL-3.0](https://github.com/badele/nix-homelab/blob/main/LICENSE)
- [README](https://github.com/badele/nix-homelab/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/badele-nix-homelab
