Files
oikos/archive/hermes-plans/2026-06-03_223218-dhcp-pool-exclude-static-ips.md
dtoro 6b75f7302d db as source of truth: wiki→seeds, archive old artifacts, knowledge ingestion
- Migrations 010 (content_hash) + 011 (search tsvector column)
- new: internal/knowledge/seed.go — knowledge seed ingest engine
- new: internal/httpapi/knowledge.go — SearchKnowledge + GetEntityKnowledge
- wire knowledge ingest into oikos seed pipeline
- convert all 36 wiki docs + 6 investigations + 12 runbooks → seeds/knowledge.yaml
- archive: knowledge/wiki/→archive/, oikos/cards/→archive/, .hermes/plans/→archive/
- delete: 9 superseded Python kernel files, ledger/, mcp/build_host_files.py
- remove empty knowledge/ directory tree
2026-07-07 20:22:30 +02:00

10 KiB
Raw Blame History

Plan: Narrow Technitium DHCP Pool to Avoid Static-IP Conflicts

For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.

Goal: Eliminate the IP conflict risk created by the Technitium DHCP pool (.100.240) overlapping with all static LXC/VM IPs (.101.239).

Architecture: Shrink the DHCP pool range on Technitium so it only covers IPs that no static host uses. No LXC/VM IPs change. Single server-side change (Technitium API), plus documentation updates.

Tech Stack: Technitium DNS API (/api/dhcp/scopes/set), bash/curl, homelab-context repo for docs.


Problem statement

The Technitium DHCP server on CT 107 serves 192.168.8.100192.168.8.240. Every static homelab IP except hubris (.77) sits inside that range:

Host IP Inside pool?
hubris (Proxmox) .77 No — below .100
haos (VM 108) .101 YES
gitea (104) .121 YES
paperless (103) .130 YES
arriman (122) .132 YES
mule-images (120) .136 YES
sophia (119) .157 YES
mac-mini .174 YES
caddy (121) .175 YES
authentik (124) .180 YES
plato (126) .190 YES
zimaos (VM 100) .195 YES
nfs-export (102) .200 YES
apps (105) .205 YES
jellyfin (101) .206 YES
nextcloud (114) .224 YES
claudio-bot (123) .230 YES
elementsynapse (118) .239 YES

The docs claim "Static-IP LXCs (below .100) are unaffected" — this is false. Static IPs span .101.239, the DHCP pool spans .100.240. They overlap almost entirely.

If the DHCP server hands out .121/.136/.224 (or any of the above) to a new dynamic client before the static LXC claims it on boot, the static service will fail to bind and the service goes dark.


Proposed approach: Shrink the pool

Move the DHCP pool start from .100 to .241, resulting in:

  • New pool: 192.168.8.241 192.168.8.254 (14 dynamic IPs)
  • Reserved: .100.240 stays for static hosts, .2 for Technitium, .1 for gateway
  • Zero changes to any LXC, VM, Caddy, or Proxmox config.

Why .241.254:

  • Highest static IP is .239 (elementsynapse) — .241 gives a 1-IP gap
  • .255 is the broadcast address (unusable)
  • 14 IPs is plenty for truly dynamic clients (new transient containers, test VMs)
  • If more are ever needed, the pool can easily be widened back down

Tasks

Task 1: Verify current Technitium DHCP scope from the API

Objective: Confirm the active pool range matches what's documented.

Step 1: Log in to Technitium API and get a token

TOKEN=$(curl -sk -X POST http://192.168.8.2:5380/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"user":"admin","pass":"'$(cat /opt/technitium/admin_password.txt)'","includeInfo":false}' \
  | jq -r '.token')
echo "Token: ${TOKEN:0:10}..."

Step 2: Fetch current DHCP scopes

curl -sk "http://192.168.8.2:5380/api/dhcp/scopes/list?token=$TOKEN" | jq .

Expected: One scope named homelab with startingAddress: "192.168.8.100" and endingAddress: "192.168.8.240".

Verification: If the scope is NOT .100.240, note the actual range and adjust the plan.


Task 2: Update the DHCP scope to .241.254

Objective: Shrink the pool so it no longer overlaps static IPs.

Step 1: Update the scope via API

curl -sk -X POST "http://192.168.8.2:5380/api/dhcp/scopes/set?token=$TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "homelab",
    "startingAddress": "192.168.8.241",
    "endingAddress": "192.168.8.254",
    "subnetMask": "255.255.255.0",
    "gatewayAddress": "192.168.8.1",
    "dnsServerAddresses": ["192.168.8.2"],
    "leaseTime": 86400
  }'

Step 2: Verify the change took effect

curl -sk "http://192.168.8.2:5380/api/dhcp/scopes/list?token=$TOKEN" | jq '.response.scopes[0] | {startingAddress, endingAddress}'

Expected:

{
  "startingAddress": "192.168.8.241",
  "endingAddress": "192.168.8.254"
}

Pitfall: If the API returns {"status":"error"}, the scope name or parameter format may differ. Inspect the response body. Technitium's API might use rangeStart/rangeEnd instead of startingAddress/endingAddress. Adjust if needed (check the full scope object from Task 1 step 2 for exact key names).


Task 3: Check for active DHCP leases in the old pool that would be stranded

Objective: Ensure no DHCP client is currently holding an IP in .100.240 that it will lose when its lease expires.

Step 1: List active DHCP leases

curl -sk "http://192.168.8.2:5380/api/dhcp/leases/list?token=$TOKEN" | jq '.response.leases[] | {ip: .ipAddress, client: .clientHostname, mac: .hardwareAddress, expires: .leaseExpires}'

Step 2: Interpret results

  • If the only leases are from static LXCs that configured themselves before the DHCP move (e.g., old leases from before the 2026-06-02 static-IP migration), these leases are stale and harmless.
  • If a dynamic client (e.g., a test laptop, transient VM) holds .195 or similar, note it — it will lose its IP on next renew and should be moved to a static assignment or into the .241+ pool.
  • ZimaOS (VM 100) at .195 is a DHCP lease, not static — this is the one host that needs attention. Either:
    • Set a static IP inside ZimaOS (preferred), or
    • Add a DHCP reservation for MAC in Technitium to pin .195

Verification: No "surprise" dynamic clients that would break on lease expiry.


Task 4: Fix ZimaOS IP stability (if needed)

Objective: Ensure ZimaOS at .195 won't float or break when the pool shrinks.

If ZimaOS already has a static IP configured inside the VM: Nothing to do.

If ZimaOS is DHCP-only (likely — doc says "DHCP lease, not a reservation"):

Option A (preferred): Set a static IP inside ZimaOS via its web UI at http://192.168.8.195 → Settings → Network → Static IP → 192.168.8.195/24, gateway 192.168.8.1, DNS 192.168.8.2.

Option B: Add a DHCP reservation in Technitium for ZimaOS's MAC address:

ZIMAMAC=$(ssh root@hubris "qm config 100 | grep net0 | grep -oE '([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}'")
curl -sk -X POST "http://192.168.8.2:5380/api/dhcp/reservations/add?token=$TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"hardwareAddress\":\"$ZIMAMAC\",\"ipAddress\":\"192.168.8.195\"}"

Pitfall: The /api/dhcp/reservations/add endpoint signature is unverified — confirm the exact endpoint name from Technitium's API docs or the web UI before running it. The web console at http://192.168.8.2:5380 → DHCP → Reservations can be used as a manual fallback.


Task 5: Update documentation in homelab-context

Objective: Fix the now-wrong claims about static IPs being "below .100".

Files to edit:

  1. infrastructure/network.md — Line 53

    • Old: Most homelab LXCs use static IPs below \.100`. DHCP only covers new/transient containers.`
    • New: Static IPs span \.101.239` (all LXCs + VMs + workstations). DHCP pool narrowed to `.241.254` to avoid overlap.`
  2. containers/107-dns.md — Lines 37, 42, 55

    • Line 37: Update pool range: 192.168.8.241 192.168.8.254
    • Line 42: Static-IP LXCs (below \.100`)Static-IP LXCs (`.101.239`) are excluded from the pool.`
    • Line 55: Add changelog entry for the pool shrink
  3. containers/107-dns.md — Add changelog entry:

    ### 2026-06-03 — DHCP pool narrowed to `.241.254` to exclude static IPs
    Previous pool `.100.240` overlapped with all static LXCs/VMs (\`.101.239\`), creating IP conflict risk. Shrunk pool to `.241.254`. No services re-IP'd. See [plan](../plans/2026-06-03-dhcp-pool-exclude-static-ips.md).
    
  4. infrastructure/network.md — Line 51: Update pool range in the DHCP table row.

  5. plans/2026-06-01-slate-ax-to-sodola-migration.md — Line 60: Optionally update the pool range in the config table (or add a post-migration note). This is the historical migration plan, so a footnote rather than an edit may be better.

Commit:

cd /opt/homelab-context
git add infrastructure/network.md containers/107-dns.md plans/
git commit -m "docs: DHCP pool narrowed to .241-.254 to exclude static IPs"
git push

Task 6: Verify no regressions

Objective: Smoke-test that DNS and key services still work after the scope change.

# 1. DNS resolution via Technitium
dig @192.168.8.2 +short git.hubris.network
# Expected: 192.168.8.175

# 2. Caddy reverse-proxy chain
curl -sI https://git.hubris.network | head -1
# Expected: HTTP/2 200

# 3. All app names resolve
for name in git cloud media paperless photos matrix auth plato artifacto; do
  result=$(dig @192.168.8.2 +short ${name}.hubris.network)
  printf "%-20s → %s\n" "${name}.hubris.network" "$result"
done

# 4. Technitium DHCP scope is correct
curl -sk "http://192.168.8.2:5380/api/dhcp/scopes/list?token=$TOKEN" | jq '.response.scopes[0] | {startingAddress, endingAddress}'

Risk assessment

Risk Likelihood Impact Mitigation
API call fails (wrong field names) Medium Low Inspect live scope object first (Task 1); adjust payload
ZimaOS loses IP on next boot Low Medium Task 4 makes ZimaOS static or reserved
Active DHCP client in .100.240 gets stranded Low Low Task 3 surfaces this; client just requests a new IP from .241+
Technitium admin password file missing Low Medium /opt/technitium/admin_password.txt was created during setup; verify existence

Open questions

  1. Is zimaos (VM 100) currently DHCP or static? The doc says DHCP lease, but it's listed as lan_ip: 192.168.8.195 in inventory. If it's actually DHCP, it's the one host that needs a static assignment before the pool shrinks.
  2. Are there any transient DHCP clients (test laptops, phones) on the homelab subnet that hold .100.240 addresses? Check leases before cutting over.
  3. Should we widen the pool slightly (e.g., .230.254) for more headroom? Currently 14 IPs. If 3+ transient devices are expected, .230.254 = 25 IPs — still safe since the highest static is .239 and .230.239 could be excluded.

Execution preference

All changes are on the Technitium API + homelab-context repo. No LXC/VM restarts needed. The pool shrink takes effect immediately for NEW DHCP requests; existing leases in the old range continue until expiry (24h max).