Awesome
<p float="left"> <a href="https://oddlama.github.io/nix-topology/examples/complex/main.svg"><img src="https://oddlama.github.io/nix-topology/examples/complex/main.svg" alt="Main view. Click to enlarge." width="45%" /></a> <a href="https://oddlama.github.io/nix-topology/examples/complex/network.svg"><img src="https://oddlama.github.io/nix-topology/examples/complex/network.svg" alt="Network view. Click to enlarge." width="51%" /></a> </p>Documentation | Installation and Usage
π nix-topology
With nix-topology you can automatically generate infrastructure and network diagrams as SVGs directly from your NixOS configurations, and get something similar to the diagram above. It defines a new global module system where you can specify what nodes and networks you have. Most of the work is done by the included NixOS module which automatically collects all the information from your hosts.
- π± Extracts a lot of information automatically from your NixOS configuration:
- π Interfaces from systemd-networkd
- π΅ Known configured services
- π₯οΈ Guests from microvm.nix
- π₯οΈ Guests from nixos containers
- π Network information from kea
- πΊοΈ Renders both a main diagram (physical connections) and a network-centric diagram
- β‘οΈ Automatically propagates assigned networks through your connections
- π¨οΈ Allows you to add external devices like switches, routers, printers ...
Have a look at the examples directory for some self-contained examples or view the rendered results in the documentation.
Why?
I became a little envious of all the manually crafted infrastructure diagrams on r/homelab. But who's got time for that?! I'd rather spend a whole lot more time to create a generator that I will use once or twice in my life π€‘π. Maybe it will be useful for somebody else, too.
π¦ Installation and Usage
Installation should be as simple as adding nix-topology to your flake.nix, defining the global module and adding the NixOS module to your systems. A flake-parts module is also available (see end of this section for an example).
- Add nix-topology as an input to your flake
inputs.nix-topology.url = "github:oddlama/nix-topology";
- Add the exposed overlay to your global pkgs definition, so the necessary tools are available for rendering
pkgs = import nixpkgs { inherit system; overlays = [nix-topology.overlays.default]; };
- Import the exposed NixOS module
nix-topology.nixosModules.default
in your host configsnixosConfigurations.host1 = lib.nixosSystem { system = "x86_64-linux"; modules = [ ./host1/configuration.nix nix-topology.nixosModules.default ]; };
- Create the global topology by using
topology = import nix-topology { pkgs = /*...*/; };
. Expose this as an output in your flake so you can access it.# Repeat this for each system where you want to build your topology. # You can do this manually or use flake-utils. topology.x86_64-linux = import nix-topology { inherit pkgs; # Only this package set must include nix-topology.overlays.default modules = [ # Your own file to define global topology. Works in principle like a nixos module but uses different options. ./topology.nix # Inline module to inform topology of your existing NixOS hosts. { nixosConfigurations = self.nixosConfigurations; } ]; };
- Render your topology via
nix build .#topology.x86_64-linux.config.output
, the resulting directory will contain your finished svgs. Note that this can take a minute, depending on how many hosts you have defined. Evaluating many nixos configurations just takes some time, and the renderer sometimes struggles with handling bigger PNGs in a timely fashion.
{
inputs = {
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nix-topology.url = "github:oddlama/nix-topology";
nix-topology.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, flake-utils, nixpkgs, nix-topology, ... }: {
# Example. Use your own hosts and add the module to them
nixosConfigurations.host1 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./host1/configuration.nix
nix-topology.nixosModules.default
];
};
}
// flake-utils.lib.eachDefaultSystem (system: rec {
pkgs = import nixpkgs {
inherit system;
overlays = [ nix-topology.overlays.default ];
};
topology = import nix-topology {
inherit pkgs;
modules = [
# Your own file to define global topology. Works in principle like a nixos module but uses different options.
./topology.nix
# Inline module to inform topology of your existing NixOS hosts.
{ nixosConfigurations = self.nixosConfigurations; }
];
};
});
}
</details>
<details>
<summary>Example flake.nix with flake-parts</summary>
{
inputs.flake-parts.url = "github:hercules-ci/flake-parts";
inputs.nix-topology.url = "github:oddlama/nix-topology";
# ...
outputs = inputs:
inputs.flake-parts.lib.mkFlake {inherit inputs;} {
imports = [
inputs.nix-topology.flakeModule
];
perSystem = {...}: {
topology.modules = [
{
# Your global topology definitions
}
];
};
};
}
</details>
π± Adding connections, networks and other devices
After rendering for the first time, the initial diagram might look a little unstructured. That's simply because nix-topology will be missing some important connections that can't be derived from a bunch of NixOS configurations, like physical connections. You'll probably also want to add some common devices like an image for the internet, switches, routers and stuff like that. But don't worry, all of this is quite simple. There's a whole chapter in the documentation that will guide you through it.
TL;DR: You can add connections and networks by specifying this information in the global topology module, or locally in one of your NixOS configs:
# This is a topology module, so use it in your global topology, or under `topology = {};` in any participating NixOS node
{
# Connect node1.lan -> node2.wan
nodes.node1.interfaces.lan.physicalConnections = [{ node = "node2"; interface = "wan"; }];
# Add home network
networks.home = {
name = "Home Network";
cidrv4 = "192.168.1.1/24";
};
# Tell nix-topology that myhost.lan1 is part of this network.
# The network will automatically propagate via the interface's connections.
nodes.myhost.interfaces.lan1.network = "home";
}
Or locally (e.g. host1/configuration.nix
):
{
topology.networks.home = {
name = "Network Made by Host1";
cidrv4 = "192.168.178.1/24";
};
topology.self.interfaces.lan1.network = "home";
}
π¨ TODO
Yep, there's still a lot that could be added or improved.
Information Gathering (Extractors)
- Podman / docker harvesting
- networking.interfaces extractor
- Disks (from disko) + render
- Impermanence render?
- Nixos nftables firewall render?
General
- NAT indication
- Macvtap/vlan/bridge interface type svg with small link
- configurable font
- Make colors configurable
β€οΈ Contributing
Contributions are whole-heartedly welcome! Please feel free to suggest new features, implement extractors, other stuff, or generally help out if you'd like. We'd be happy to have you. There's more information in CONTRIBUTING.md and the Development Chapter in the docs.
π License
Licensed under the MIT license (LICENSE or https://opensource.org/licenses/MIT). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, shall be licensed as above, without any additional terms or conditions.