Adding a new service¶
The full path from "I want to run X" to "X is deployed and reachable".
Decide where and how¶
Two questions first:
- Which host? Applications go on an application host (
midgardoralfheim). Only ingress/monitoring infrastructure goes on yggdrasil (principles). - Module or container?
Prefer this when nixpkgs packages the service well. Create
services/<name>.nix:
{...}: {
services.<name> = {
enable = true;
# bind to localhost or a tailnet-reachable port,
# never open the firewall
};
}
Use when upstream packages better as a container and the chosen host has Podman enabled (pinned tag):
{...}: {
virtualisation.oci-containers.containers.<name> = {
image = "ghcr.io/org/app:1.2.3"; # never :latest
ports = ["127.0.0.1:8090:8080"];
};
}
Checklist¶
- Create the module under
services/<name>.nix - Wire it in:
hosts/<host>/default.niximports (host-specific) orflake.nixmodules list (shared) - Secrets, if any: add to
secrets/*.yamlvia sops, declaresops.secrets."..."(see Secrets) - Exposure, if needed:
- Caddy virtualHost in
services/ingress.nix - Public service → hostname in
services/cloudflared.nixingress; tailnet-only →@tailnetmatcher pattern instead -
DNS record in Cloudflare (public: Tunnel CNAME, tailnet-only: yggdrasil's tailnet address). For public tunnel routes:
- [ ] Monitoring: add an Uptime Kuma check for public endpoints - [ ] Validate:cloudflared tunnel route dns <tunnel-id> <name>.ridewithmin.comnix flake check --no-build- [ ] Commit and open a PR - [ ] Wait for CI to build every host - [ ] Merge once green; CD deploys the change - [ ] Verify the service after CD completes
- Caddy virtualHost in
Verify¶
# on the host
systemctl status <name>
curl -fsS http://127.0.0.1:<port>/
# from a tailnet client, if exposed
curl -fsS https://<name>.ridewithmin.com/
If it goes wrong¶
Roll back on the host:
sudo nixos-rebuild switch --rollback
If an explicit manual just test activation was used, it disappears on reboot.