Files
dev/docs/00 INBOX/ai/adguard-pikachu-migration-design-tree.md

111 lines
7.9 KiB
Markdown

# AdGuard migration to Pikachu: design tree
## Research facts
- The pinned Nixpkgs revision provides `adguardhome` version `0.107.79`.
- NixOS provides the `services.adguardhome` module.
- The module runs AdGuard Home as a hardened dynamic systemd user.
- It stores state in `/var/lib/AdGuardHome`.
- It grants `CAP_NET_BIND_SERVICE` for DNS port 53.
- It grants `CAP_NET_RAW` when AdGuard Home DHCP is enabled.
- The module supports an arbitrary YAML configuration through `settings`.
- The module supports fully declarative operation with `mutableSettings = false`.
- In fully declarative operation, the module replaces `/var/lib/AdGuardHome/AdGuardHome.yaml` on each service start.
- Fully declarative operation requires `dns.bootstrap_dns` as a list.
- Declarative settings pass an AdGuard configuration check during the build.
- `mutableSettings = true` merges declared settings into UI changes at service start.
- `mutableSettings = false` replaces the state configuration on every service start.
- `openFirewall` opens only the web interface port.
- The migration must explicitly open DNS ports 53/TCP and 53/UDP when clients need them.
- Pikachu has address `10.23.10.102` on its untagged network.
- Pikachu carries VLAN 20 and creates bridge `br-vlan20`.
- The guest framework runs each guest in its own systemd-nspawn container.
- Each guest attaches to a host VLAN bridge and receives an address through DHCP.
- Pikachu currently configures only VLAN 20 for guests.
- The repository has no AdGuard configuration, Proxmox inventory, or DHCP-provider configuration.
- The current DHCP provider remains unestablished.
- The current Proxmox AdGuard container uses MAC `bc:24:11:91:60:bf`.
- The current Proxmox AdGuard container uses address `10.23.20.33` on VLAN 20.
- This environment has no Proxmox SSH target or local Proxmox control utility.
- The current AdGuard configuration listens on all guest addresses at DNS port 53.
- The current AdGuard configuration has no allowed-client or disallowed-client restrictions.
- The current AdGuard configuration uses Cloudflare, Google, and Quad9 encrypted upstreams in parallel.
- The current AdGuard configuration disables DNSSEC.
- The current AdGuard configuration defines three local rewrites.
- The repository configures no host backup system or monitoring system.
- Hosts update through explicit `nixos-rebuild switch --flake` commands.
- The working tree has unrelated operator changes.
## Service tradeoffs
| Service | NixOS support | Domain filtering | Administration UI | DHCP | Encrypted DNS | Main tradeoff |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| AdGuard Home | `services.adguardhome`; checked YAML and mutable or immutable state | Native block lists, rules, and per-client policy | Native | Native | Upstream and client-facing encrypted DNS | Direct migration with the closest state and feature match. UI state conflicts with fully declarative operation unless configuration is immutable. |
| Blocky | `services.blocky`; checked declarative YAML | Native lists, allow lists, rewrites, and per-client groups | No UI; REST API and metrics only | No | Encrypted upstreams and client-facing DoH, DoT, and DoQ | Strong Git-managed configuration and low operational surface. It lacks an interactive administration interface and DHCP. |
| Pi-hole | `services.pihole-ftl` plus `services.pihole-web`; declarative TOML, lists, and dashboard | Native gravity lists, allow lists, regex rules, and groups | Native | Native | Use a companion for encrypted upstream DNS | Familiar filtering product with a dashboard. The migration changes product data and uses more coupled services. |
| Technitium DNS Server | `services.technitium-dns-server`; service and firewall options only | Native block lists and DNS applications | Native | Native | Upstream and client-facing encrypted DNS | Broadest DNS feature set, including authoritative DNS. NixOS does not expose its runtime configuration declaratively. |
| Unbound | `services.unbound`; structured server and free-form settings | Local zones or response-policy zones require custom policy management | No | No | DNS-over-TLS support | Mature validating recursive resolver. Filtering and user operations require separate design. |
| dnscrypt-proxy | `services.dnscrypt-proxy`; declarative TOML | Local block and allow rules, without a list-management UI | No | No | Encrypted upstream DNS, including DNSCrypt and DoH | Strong upstream privacy and resolver selection. It is not a full network filtering appliance. |
| Knot Resolver | `services.kresd`; structured listener settings and extra Lua | Custom policy modules | No | No | Client-facing DoH and TLS listeners | Flexible high-performance recursive resolver. Filtering requires resolver expertise and custom policy. |
| SmartDNS | `services.smartdns`; free-form settings | Domain and address rules | No NixOS-integrated UI | No | Encrypted upstream and client-facing DNS | Optimizes upstream selection and latency. It offers less complete filtering management than dedicated blockers. |
`Native` means that the product provides the feature without a companion service.
## Decision tree
- **D1 — DNS service product** [settled]
- Retain AdGuard Home on Pikachu.
- Constraint: Preserve AdGuard Home as the DNS service product.
- **D2A — AdGuard Home role** [settled]
- Provides DNS filtering and resolution only.
- Constraint: The existing DHCP provider continues to assign addresses and DHCP options.
- **D3A — Execution placement** [settled]
- Runs in a dedicated guest on Pikachu's existing VLAN 20.
- Constraint: Isolate AdGuard Home from the Pikachu host.
- **D4A — Guest network identity** [settled]
- Reuses MAC `bc:24:11:91:60:bf` and address `10.23.20.33` on VLAN 20.
- Constraint: Stop the Proxmox container before the guest starts.
- **D5A — Network exposure** [settled]
- Listens on all guest addresses at port 53.
- Accepts requests from every reachable internal device.
- **D6A — Resolver policy** [settled]
- Preserves the existing upstream resolvers and parallel resolver mode.
- Preserves disabled DNSSEC and the three local rewrites.
- **D7A — Configuration authority** [settled]
- Uses fully declarative Nix configuration.
- Constraint: Set `services.adguardhome.mutableSettings = false`.
- Constraint: Declare `dns.bootstrap_dns` as a list.
- Constraint: Web UI changes do not persist.
- Constraint: Match existing service behavior except for the administrator password.
- **D8A — Migration data** [settled]
- Migrate functional DNS configuration only.
- Constraint: Do not migrate query history or statistics.
- **D9A — Operations** [settled]
- Declares a newly generated bcrypt verifier in Nix.
- Constraint: Keep the plaintext password outside the repository and Nix store.
- Constraint: The verifier is readable from the Nix store.
- **D10A — Cutover and rollback** [settled]
- Uses a planned atomic cutover with rollback.
- Stop the Proxmox container before starting the guest.
- Validate the guest before restoring the Proxmox container during rollback.
- **D11A — Runtime-state backup** [settled]
- Do not back up rebuildable AdGuard Home runtime state.
- **D12A — Monitoring** [settled]
- Do not add DNS availability monitoring.
- **D13A — Upgrade ownership** [settled]
- The normal flake update and explicit NixOS rebuild workflow owns upgrades.
- Replace AdGuard Home. [not selected]
- **D2B — Replacement product** [not applicable]
- Choose Blocky, Pi-hole, Technitium, or a composed resolver.
## Frontier
None.
## Sources
- Pinned Nixpkgs module: `nixos/modules/services/networking/adguardhome.nix`.
- Pinned Nixpkgs modules: `blocky.nix`, `pihole-ftl.nix`, `pihole-web.nix`, `technitium-dns-server.nix`, `unbound.nix`, `dnscrypt-proxy.nix`, `knot-resolver.nix`, and `smartdns.nix`.
- AdGuard Home README, release `v0.107.79`.
- Technitium DNS Server README, current upstream.