What I publish and what I don't
The dotfiles repo is public and the services repo is not. Where that line is, the three reasons something ends up private, and what pushed pieces across it.
Two repos, one of them readable
Everything in this series has come out of github.com/PieterPel/dotfiles, which is public. The other half of my setup
lives in a private repo: the services, the data they own, the secrets, the deploy. This part is about that line, where
it sits today, and the fact that it moved.
Why publish at all
Three reasons, in the order they matter to me.
The first is that I got here by reading other people’s public configs. Most of what is in my repo started as someone else’s commit, so publishing mine is the same trade one step later.
The second is showing off, and I would rather say that than dress it up as something else.
The third is that I respect people who publish theirs. It is a much better way to learn Nix than another tutorial, because you can see a working machine instead of an excerpt.
The three kinds of private
Not everything I keep back is a secret. There are three different reasons something is not in the public repo, and they need different answers:
It only concerns me. The private layer on the Pi is the clearest example: the controller mapping, the audio routing and the video settings exist because of one specific TV and one specific set of pads. Nobody else can use those values, and they would be noise in a public repo.
I do not know yet what I want to do with it. The services repo is full of things I am still deciding about. Half of it runs, the other half I keep in the same place so it can become something later. Publishing commits me to a shape before I have picked one.
It gives away security detail nobody needs. A list of open ports, what sits behind the proxy, which hostnames exist: on its own none of that is a disaster, together it is a map of my setup handed to anyone who asks. The secrets themselves are a separate category again, and no repo holds them in the clear.
How the line looks in files
The Pi is the machine where you can see all three layers at once. The public repo owns the machine, and my private repo extends it:
# Physical Raspberry Pi 400 (RetroArch box) — not a VM. The public dotfiles
# flake provides the base `nixberry` system (hardware, rpi profile, RetroArch);
# this layers on the private/host-specific bits.
flake.nixosConfigurations.nixberry = inputs.dotfiles.nixosConfigurations.nixberry.extendModules {
modules = [
# The tailnet firewall gates on declared ports, so the way in is one of
# them (the dotfiles base host pulls the module in).
{ tailnet.openPorts.nixberry."22" = "sshd, the way in from the tailnet"; }
config.flake.modules.nixos.nixberry-private
# No tailnet, libretro, alloy or node-exporter module here: the dotfiles
# base host already pulls in every module of its own flake, those included.
];
};The public half is a complete machine. The Pi boots from it, plays games, and knows nothing about my private repo. The line above adds the parts that are mine.
The most useful trick I have found is to publish the mechanism and keep the topology. Caddy is the reverse proxy that puts all my services on one hostname with certificates, and the module that does it is public:
options.modules.networking.caddy = {
enable = lib.mkEnableOption "Caddy, with every site declared once";
email = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "ACME account email.";
};
sites = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
text = lib.mkOption {
type = lib.types.lines;
description = "The Caddyfile site block.";
};
port = lib.mkOption {
type = lib.types.port;
default = 443;
description = "TCP port the site is served on.";
};
why = lib.mkOption {
type = lib.types.str;
description = "What the port is for, as it appears in tailnet.openPorts.";
};
};
}
);
default = [ ];
description = "Caddyfile blocks, each carrying the tailnet port it needs.";A site is a Caddyfile block plus the port it needs and a sentence about what the port is for. The module turns that list into two things: the running proxy and the firewall entries for the tailnet. It is all reusable and says nothing about me.
The list of sites is in the private repo. Same for the tailnet gate, which blocks everything on my VM except ports that have been declared: the mechanism is public, and which ports are declared is mine.
What moved, and why
The line is not where it started. Three things crossed it while I was writing this series, and the reason was the same each time: the thing turned out to be about the machine, not about my services.
- The RetroArch build for the Pi’s GPU (the one from part 2) was in the private repo, because it is a hack for my hardware. It is public now, because the hardware is a Raspberry Pi 400 that anyone can buy.
- The monitoring modules (the exporter that collects numbers, the shipper that forwards logs) moved the same way. What stayed private is the part that names things: which service is allowed to alert me, and about what.
- The dashboards moved too, after sitting unused in the private repo as exported JSON. They are “how I watch a box”, and now they are readable.
What pushed all of it was this blog. Posts read code out of the public repos, so anything I want to write about has to live there. That is a better boundary test than any principle I could have written down: if I cannot publish the file, I cannot write the post, and most of the time the file turns out to be publishable once the names are generic.
The one direction
The dependency only runs one way. My private repo takes dotfiles as an input; dotfiles does not know the private repo exists. That is what keeps the public half honest: it has to build a machine on its own, because that is exactly what it does on the Pi.
What to take from this
If you are deciding the same thing: publish the machine, keep the services. Machine config is mostly generic once you leave out your hostnames, and it is the part other people can learn from. Services are where your names, your data and your keys live, and almost nobody needs to read them.
If you are unsure about a file, publish the mechanism and keep the values. The pattern repeats all over this series: a module that knows how to do something is public, the list of where it is applied is not.
Where this ends
Four parts: one file for one thing, one flake for every machine, every file the same shape, and the line between public
and private. That is the whole setup, and all of it is at github.com/PieterPel/dotfiles if you want to read the real
thing instead of my summary of it.