Services and security patterns
Services and security patterns
This part of the standalone NixOS book is organized by topic (normal book chapters), not by calendar day.
Read chapters in order within the part when learning; skip or skim freely when using the book as a reference.
Baseline: NixOS / nixpkgs 26.05, flakes, Home Manager as NixOS module (from the previous part), deliberate firewall and secrets discipline.
What this part is for
Layout and Home Manager made the repo operable. This part makes a service slice operable and defensible:
| Theme | Chapters |
|---|---|
| Secrets threat model | The secrets problem |
| Primary secrets tooling | sops-nix |
| Comparison alternative | agenix |
| Host attack surface | Host hardening patterns |
| Network policy | Firewall discipline |
| HTTPS front door | TLS and reverse-proxy |
| Durable state | A data service pattern (PostgreSQL) |
| Isolation literacy | NixOS containers |
| OCI industry path | Podman and Docker patterns |
| See what is running | Observability patterns |
| Optional k8s awareness | k3s (optional) |
After this part you should be able to run a small app path—proxy → backend → database—with encrypted secrets, documented ports, and a monitoring or logs pattern—without secrets in the Nix store.
Prerequisites
- Working flake host with
hosts/·modules/·homes/layout (or equivalent discipline) - Lock policy and selective updates
- Comfort with
systemdunits andjournalctl - Firewall basics from the host networking chapter
- No reliance on plaintext passwords in the flake
Chapter map
| Chapter | Focus | Outcome |
|---|---|---|
| The secrets problem | Store ≠ vault | Audit + remove anti-patterns |
| sops-nix | Encrypt in git; path at runtime | End-to-end secret consumer |
| agenix (comparison) | Same job, different UX | House default declared |
| Host hardening | SSH, sudo, surface | Hardening module + recovery notes |
| Firewall discipline | Deny by default | docs/ports.md living matrix |
| TLS front door | Caddy or Nginx + ACME/lab TLS | 443 front; localhost backends |
| Data service | PostgreSQL pattern | State, auth, backup story |
| NixOS containers | nspawn containers.* |
Isolation decision record |
| Podman/Docker | Declarative OCI | Localhost publish + comparison |
| Observability | Metrics and/or logs | Runbook or Prometheus path |
| k3s optional | Skip or lite install | Explicit decision artifact |
Mental model — service slice
clients
│
▼
:443 reverse proxy (TLS) ── modules/services/proxy-*.nix
│
▼
127.0.0.1:app ── backend unit / OCI / nspawn
│
▼
127.0.0.1:5432 ── PostgreSQL (localhost)
│
secrets via sops-nix ── /run/secrets/... paths only
firewall ── 22, 80, 443 (documented)
observability ── journald and/or Prometheus localhost
| Layer | Default posture |
|---|---|
| Edge | TLS terminate; minimal open ports |
| App | Bind localhost; no public DB |
| Secrets | Ciphertext in git; decrypt at activation |
| State | Mutable dirs outside store; backup plan |
| Observe | Localhost metrics or excellent logs |
Suggested module layout (extends previous part)
modules/
common/
hardening/
firewall.nix
sops.nix
services/
proxy-caddy.nix # or proxy-nginx.nix
postgres.nix
observability.nix
podman.nix
oci-hello.nix
secrets/
lab.yaml # sops-encrypted
.sops.yaml
docs/
secrets.md
ports.md
proxy.md
data-service.md
isolation.md
observability.md
How to work this part
- Threat model and cleanup before tooling
- sops-nix end-to-end before real service passwords
- agenix once for comparison; pick a house default
- Hardening + firewall before public exposure
- Proxy + one data service as the spine
- Containers literacy without containerizing everything
- Observability so the slice is inspectable
- k3s only if justified—or skip with a written decision
Prefer small commits: secrets infra, then hardening, then each service.
Cross-cutting conventions (26.05)
| Topic | Convention |
|---|---|
| Secrets primary | sops-nix (agenix compared once) |
| Secret consumption | Runtime paths, never Nix strings of secret values |
| DB / metrics / backends | Listen on 127.0.0.1 unless documented otherwise |
| Proxy | One of Caddy or Nginx |
| OCI | Prefer Podman; localhost port publishes |
| Ports | Living docs/ports.md |
| ACME tokens / DNS API | sops only |
Security non-goals (this part)
| Non-goal | Note |
|---|---|
| Full CIS benchmark automation | Patterns over checklists |
| Multi-AZ HA databases | Single-node honesty |
| Corporate SSO platform | Mentions only |
| “Containers make it secure” | Kernel shared; be honest |
| Required Kubernetes | Optional chapter |
Skills checklist (end of part)
- Can explain why store ≠ vault in one minute
- sops-nix: encrypt, activate, consume path
- House secrets default documented; dual-stack cleaned up
- Hardening module: no password SSH, recovery path written
- Firewall on; every open port in
docs/ports.md - TLS (or documented lab alternative) reverse proxy
- PostgreSQL (or one DB) with backup/restore story
- Isolation decision (native vs nspawn vs OCI)
- One declarative OCI container or justified refusal
- Observability path: metrics and/or logs runbook
- k3s decision recorded (skip is first-class)
Reading strategies
| Mode | Approach |
|---|---|
| Spine first | secrets → sops → harden → firewall → TLS → postgres → observability |
| Compare tools | sops vs agenix; nspawn vs OCI; metrics vs logs |
| Audit existing host | secrets problem + firewall + ports.md first |
| Resource-constrained | skip k3s; logs-first observability; native modules over k8s |
Relationship to other parts
| Earlier | This part uses |
|---|---|
| Host networking / firewall intro | Deepens discipline |
| Systemd via Nix | Service units, ordering |
| Flake layout / HM / locks | Where modules and inputs live |
| Later: ops, packaging, capstone | Deploy, tests, multi-node, wipe proofs |
Lab host assumptions
Examples use host lab, user alice, system x86_64-linux, domains like app.example.invalid (replace with real DNS or lab TLS). Never paste production secrets into the book examples or your git history.
Next parts
Packaging, ops and fleet, and capstone assume you can keep secrets out of the store, open only justified ports, and rebuild a service slice from the flake. If this part is thin, those parts become theater.