This repository has been archived on 2026-07-30. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
nixos/docs/internal/implementation-plan.md
T
beatzaplentyandClaude Sonnet 4.6 f85c65870f Initial infrastructure mono-repo scaffold
Consolidates nixos, docker, raspi, and debian-configuration into a single
infrastructure-as-code repo. Includes:

- ansible/: full inventory + proxmox-hardening, freeipa, and raspberrypi
  roles (converted from debian-configuration bash scripts)
- terraform/: Proxmox VMs, Dynu DNS, Pi-hole (decommissioned stub),
  Docker container catalog — migrated from docker/infrastructure/terraform/
- stacks/docker/, stacks/raspi/, nixos/: placeholder READMEs pending
  git subtree population (see implementation plan)
- docs/: internal MkDocs site with architecture, network topology, runbooks,
  and drift-detection guide; external sanitized site
- scripts/: drift-detect.sh, docs-build.sh, install-hooks.sh, check-secrets.sh
- CI: secret-scan (push/PR), drift-detect (daily), docs-build (on change)
- Pi-hole removed throughout — DNS is FreeIPA, DHCP is router

See docs/internal/implementation-plan.md for the phased rollout after
pushing to Gitea.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UvNjoxTWEDkhXsd1Dq2ETP
2026-07-30 07:07:47 +10:00

373 lines
12 KiB
Markdown

# Implementation Plan
Phased rollout of the `infrastructure` mono-repo. Work through these phases in order
after the initial push to Gitea. Each phase is independently completeable — the repo
is usable after Phase 0 even if later phases aren't done yet.
---
## Phase 0: Initial push and git history migration
**Goal:** Get the repo to Gitea and bring in existing repos with their commit histories.
### 0.1 — Create Gitea repo and push
```bash
cd /home/wayne/repos/infrastructure
git add -A
git commit -m "Initial infrastructure mono-repo scaffold"
# Create the repo on Gitea first (via web UI), then:
git remote add origin git@gitea.lan.ddnsgeek.com:wayne/infrastructure.git
git push -u origin main
```
### 0.2 — Migrate existing repos via git subtree
`git subtree` merges another repo's history into a subdirectory. Run these in order.
`--squash` collapses history into one merge commit (cleaner). Remove `--squash` if you
want full per-commit history (makes the log noisier but preserves full blame).
```bash
# Main docker stack → stacks/docker/
git subtree add --prefix=stacks/docker /home/wayne/repos/docker main --squash
git push
# Raspberry Pi stack → stacks/raspi/
git subtree add --prefix=stacks/raspi /home/wayne/repos/raspi main --squash
git push
```
> **Note:** `debian-configuration` is NOT imported via subtree — it's being dissolved.
> Its content has been converted into Ansible roles in `ansible/roles/`. The original
> repo should be archived in Gitea (Settings → Danger Zone → Archive) after Phase 3
> is complete.
### 0.3 — Install pre-commit hook locally
```bash
./scripts/install-hooks.sh
```
### 0.4 — Configure Gitea CI secrets
In the Gitea repo → Settings → Secrets, add:
| Secret name | Value |
|-------------|-------|
| `PROXMOX_ENDPOINT` | `https://pve1.sweet.home:8006/` |
| `PROXMOX_API_TOKEN_ID` | Terraform API token ID (create in PVE if not exists) |
| `PROXMOX_API_TOKEN_SECRET` | Terraform API token secret |
| `DYNU_API_KEY` | Dynu API key |
| `ANSIBLE_SSH_KEY` | Private SSH key for Ansible connections (base64 or raw PEM) |
| `GOTIFY_URL` | `https://gotify.lan.ddnsgeek.com` |
| `GOTIFY_TOKEN` | Gotify app token |
**Creating a Proxmox API token for Terraform (if not already done):**
```bash
# On pve1 as root:
pveum user add terraform@pve --comment "Terraform service account"
pveum role add TerraformRole --privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Monitor VM.Audit VM.PowerMgmt Datastore.AllocateSpace Datastore.Audit Pool.Allocate Sys.Audit Sys.Console Sys.Modify"
pveum aclmod / -user terraform@pve -role TerraformRole
pveum user token add terraform@pve tf --privsep=0
# Copy the token secret — it's shown only once
```
---
## Phase 1: Ansible — verify connectivity and run check mode
**Goal:** Confirm Ansible can reach all managed hosts and produce clean check-mode output.
### 1.1 — Install Ansible and collections
```bash
pip install ansible
cd ansible
ansible-galaxy collection install -r collections/requirements.yml
```
### 1.2 — Verify inventory and connectivity
```bash
cd ansible
# List all hosts
ansible all --list-hosts
# Ping everything
ansible-playbook playbooks/ping.yml
```
Fix any unreachable hosts (SSH key, hostname, `ansible_user`) before proceeding.
### 1.3 — Run check mode against each host group
```bash
# Proxmox nodes
ansible-playbook playbooks/proxmox.yml --check --diff --limit proxmox
# FreeIPA (check only — install tasks are gated by ipa_installed check)
ansible-playbook playbooks/freeipa.yml --check --diff
# Raspberry Pi
ansible-playbook playbooks/raspi.yml --check --diff
```
**Expected result:** `check` mode should show either "no changes" (already hardened) or
show exactly the diffs you'd expect. If it shows unexpected changes, review the task
and update the role defaults before applying.
### 1.4 — Apply to pve-test first (sandbox validation)
```bash
ansible-playbook playbooks/proxmox.yml --limit pve-test.sweet.home
```
Verify the node is still reachable and the PVE web UI works before applying to pve1.
### 1.5 — Apply to production hosts
```bash
# Confirm check mode looks clean, then:
ansible-playbook playbooks/proxmox.yml --limit pve1.sweet.home
ansible-playbook playbooks/raspi.yml
```
---
## Phase 2: Terraform — import existing infrastructure state
**Goal:** Bring existing Proxmox VMs and DNS records under Terraform state management,
enabling drift detection.
### 2.1 — Configure credentials
```bash
export TF_VAR_proxmox_endpoint="https://pve1.sweet.home:8006/"
export TF_VAR_proxmox_api_token_id="terraform@pve!tf"
export TF_VAR_proxmox_api_token_secret="<token>"
```
Or create `terraform/proxmox/terraform.tfvars` (this file is git-ignored):
```hcl
proxmox_endpoint = "https://pve1.sweet.home:8006/"
proxmox_api_token_id = "terraform@pve!tf"
proxmox_api_token_secret = "<token>"
```
### 2.2 — Initialise Terraform workspaces
```bash
cd terraform/proxmox && terraform init
cd ../dns && terraform init
```
### 2.3 — Verify existing resource blocks match live state
The `terraform/proxmox/` workspace already has resource blocks for existing VMs
(`docker.tf`, `nix-cache.tf`, `server-nixos.tf`, etc.) — these were generated from
live state previously. Verify they're accurate:
```bash
cd terraform/proxmox
# Check if state is already populated (it may not be on first run)
terraform state list
# If state is empty, import each existing VM:
terraform import proxmox_virtual_environment_vm.docker pve/qemu/103
terraform import proxmox_virtual_environment_vm.nix-cache pve/qemu/105
terraform import proxmox_virtual_environment_vm.server-nixos pve/qemu/104
# Add imports for any other VMs not yet in state
# VMID reference: check 'qm list' on pve1
```
### 2.4 — Run plan and reconcile any drift
```bash
terraform plan
```
If plan shows changes, the `.tf` resource block differs from live state.
Either update the `.tf` file to match (if the live state is correct) or apply
to enforce the declared state. **Do not apply to production VMs without reviewing
every planned change.**
### 2.5 — DNS workspace
```bash
cd terraform/dns
# Configure Dynu credentials if not already set:
export TF_VAR_dynu_api_key="<api_key>"
terraform plan
```
The DNS workspace already has the existing records imported. Verify no drift.
### 2.6 — Verify drift detection works end-to-end
Manually introduce a small change in the PVE UI (e.g. add a VM note) and run:
```bash
./scripts/drift-detect.sh --terraform
```
Confirm it detects the change. Revert it or update the `.tf` to match.
---
## Phase 3: NixOS flake migration
**Goal:** Move the `nixos` repo into this mono-repo while keeping the flake fully functional.
### 3.1 — Import with git subtree
```bash
git subtree add --prefix=nixos /home/wayne/repos/nixos main --squash
git push
```
### 3.2 — Update the remote flake URL on all NixOS hosts
The `Switch-nix` and `Test-nix` aliases on NixOS hosts reference the old Gitea URL.
Update `nixos/variables.nix` (the `flakeUrl` variable) to point at the new path:
```nix
# Old:
flakeUrl = "git+ssh://gitea.lan.ddnsgeek.com/wayne/nixos";
# New:
flakeUrl = "git+ssh://gitea.lan.ddnsgeek.com/wayne/infrastructure?dir=nixos";
```
Deploy the change to the `docker` LXC first (lowest risk), then remaining hosts.
### 3.3 — Validate flake builds from subdirectory
```bash
nix build ./nixos#proxmox-docker
nix build ./nixos#proxmox-nix-cache
```
### 3.4 — Archive the old nixos repo
Once all hosts are rebuilding successfully from the new URL, archive the old repo:
Gitea → `wayne/nixos` → Settings → Danger Zone → Archive Repository.
---
## Phase 4: Activate drift detection CI
**Goal:** Daily automated drift checks running in Gitea CI with Gotify notifications.
### 4.1 — Verify secrets are configured (from Phase 0.4)
Trigger the `drift-detect` workflow manually from Gitea CI → Actions → Drift Detection.
### 4.2 — Confirm first successful run
Review the step summary in Gitea. Expected outcome on a clean run:
- Terraform: "No changes. Infrastructure matches configuration."
- Ansible: `changed=0` on all hosts
### 4.3 — Test the notification path
Temporarily modify a `.tf` resource (without applying) and trigger a manual run.
Confirm the Gotify notification arrives.
---
## Phase 5: Documentation pipeline
**Goal:** Auto-generated service catalog and host inventory, with both internal and external sites building on every push.
### 5.1 — Install docs dependencies
```bash
pip install mkdocs mkdocs-material jinja2 pyyaml
```
### 5.2 — Test local build
```bash
./scripts/docs-build.sh
./scripts/docs-build.sh --serve # preview at http://localhost:8000
```
### 5.3 — Implement service catalog generator
Create `scripts/generate-service-catalog.py`:
- Parses all `docker-compose.yml` files under `stacks/`
- Extracts service name, image, Traefik domain (from labels), network memberships
- Outputs `docs/generated/service-catalog.md`
Template for the output:
```markdown
# Service Catalog
Auto-generated from compose files in stacks/.
| Service | Image | Domain | Stack |
|---------|-------|--------|-------|
| traefik | traefik:latest | — | stacks/docker/core/traefik |
| nextcloud-webapp | nextcloud:production | nextcloud.lan.ddnsgeek.com | stacks/docker/apps/nextcloud |
...
```
### 5.4 — Configure docs deploy target
Set these Gitea secrets (from Phase 0.4):
- `PAGES_SSH_KEY` — key for rsync to the docs server
- `PAGES_HOST` — user@host for rsync
- `PAGES_PATH_INTERNAL` — path on the docs server for internal site
Or use Gitea Pages if self-hosted Gitea supports it.
---
## Phase 6: Cleanup and archive
**Goal:** Retire the old repos cleanly.
### 6.1 — Verify nothing still references old repos
- All NixOS hosts rebuilding from `infrastructure?dir=nixos`
- Docker stack deployed from `stacks/docker/`
- Ansible roles replacing debian-configuration scripts ✓
### 6.2 — Archive deprecated repos
Archive these repos on Gitea (makes them read-only, preserves history):
- `wayne/debian-configuration` — content dissolved into `ansible/roles/`
- `wayne/docker` — content migrated to `stacks/docker/`
- `wayne/raspi` — content migrated to `stacks/raspi/`
- `wayne/nixos` — migrated to `infrastructure/nixos/`
Do NOT delete them — they have git history that may be useful for reference.
### 6.3 — Update any external references
- CLAUDE.md files in the old repos — add a note pointing to the new mono-repo
- Any README links, bookmarks, or documentation that references the old repo URLs
---
## Ongoing: Adding new infrastructure
### Adding a new Proxmox VM
1. Add a resource block in `terraform/proxmox/<name>.tf`
2. Run `terraform plan``terraform apply`
3. Add the host to `ansible/inventory/hosts.yml`
4. If NixOS: add to `nixos/hosts/<name>/host.nix`, run `./scripts/sync-host-keys.sh`
5. Deploy: `nixos-rebuild switch --flake ./nixos#<target> --target-host <hostname>`
### Adding a new Docker service
1. Add a `docker-compose.yml` under `stacks/docker/apps/<service>/` or `stacks/docker/monitoring/<service>/`
2. Add Traefik labels, networks, secrets (following existing service patterns)
3. Update `stacks/docker/default-environment.env` if new env vars are needed
4. Test: `./services-up.sh config` (validates interpolation), then `./services-up.sh up -d <service>`
### Rotating credentials
- Proxmox API token: create new, update Gitea secrets, delete old
- Ansible SSH key: replace `ANSIBLE_SSH_KEY` secret, re-add public key to all hosts
- Dynu API key: update `DYNU_API_KEY` secret in Gitea
- NixOS SOPS secrets: use `nixos/scripts/rotate-admin-key.sh`