docs: record PD qdrant/seerr drift cleanup incident
Some checks failed
secret-guardrails / gitleaks (push) Has been cancelled
secret-guardrails / artifact-secret-scan (push) Has been cancelled

This commit is contained in:
Fizzlepoof
2026-05-29 00:59:39 +00:00
parent 1227814277
commit db8dce8c8b
3 changed files with 136 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
# Incident: PD qdrant / seerr compose drift cleanup (2026-05-28)
## Summary
An earlier recovery on PlausibleDeniability left the runtime state healthier than the operator metadata around it.
Public and LAN access were restored, but `qdrant` had been manually recreated from `docker inspect` data because its prior compose-path labeling was stale or absent. That fixed service availability, but it also created the risk that live state, repo state, and future operator assumptions could drift apart.
On 2026-05-28, the stack was squared away by taking fresh backups, re-validating the live compose roots, re-syncing repo-managed compose content into the live directories, pruning a stale backup artifact from the live media compose directory, and re-verifying health.
## What broke
The incident had two layers:
1. **Service health / accessibility breakage**
- `qdrant` and related AI/media surface area needed recovery work earlier in the day.
- `seerr` public/local availability also had to be checked as part of the same cleanup pass.
2. **Post-recovery metadata drift risk**
- `qdrant` no longer had a trustworthy compose lifecycle trail from the original broken state.
- Manual recreation from `docker inspect` restored the container, but that is not the same as a clean repo-driven compose reconciliation.
- A stale file (`docker-compose.yaml.pre-d3b-20260526-003732`) was still sitting in the live media compose directory, which was not dangerous by itself but increased operator ambiguity.
## What was manually recreated
During the earlier recovery, `qdrant` was recreated manually from live container metadata when the prior compose-path labels were stale or missing.
By the end of the cleanup, the running `qdrant` container correctly reported:
- working dir: `/mnt/docker-ssd/docker/compose/ai`
- compose file: `/mnt/docker-ssd/docker/compose/ai/docker-compose.yaml`
- service: `qdrant`
`seerr` correctly reported:
- working dir: `/mnt/docker-ssd/docker/compose/media`
- compose file: `/mnt/docker-ssd/docker/compose/media/docker-compose.yaml`
- service: `seerr`
## Backups taken before cleanup
Fresh rollback-friendly backups were created on PD at:
- directory: `/mnt/docker-ssd/docker/backups/post-incident-sync-20260528-192945`
- tarball: `/mnt/docker-ssd/docker/backups/post-incident-sync-20260528-192945.tgz`
Backup contents include:
- live `ai` compose tree
- live `media` compose tree
- `qdrant` appdata
- `docker inspect` output for `qdrant`, `openwebui`, and `seerr`
- rendered `docker compose config` output for both stacks
## Cleanup actions performed
1. verified the live container labels for `qdrant`, `openwebui`, and `seerr`
2. compared live compose files against the repo-managed copies under `truenas-stacks`
3. confirmed the active tracked compose content already matched repo
4. re-synced repo-managed `ai/` and `media/` compose trees into the live PD compose directories
5. pruned the stale live file `docker-compose.yaml.pre-d3b-20260526-003732` from the media compose directory **after** backup
6. re-ran `docker compose config` validation in both live directories
7. re-checked HTTP health for `qdrant`, `openwebui`, and `seerr`
## What was verified at the end
### Live compose / label alignment
The live runtime and repo are back in agreement for the affected services:
- `qdrant` labels point to `/mnt/docker-ssd/docker/compose/ai/docker-compose.yaml`
- `seerr` labels point to `/mnt/docker-ssd/docker/compose/media/docker-compose.yaml`
- repo-managed tracked compose content matches the live active files
### Health
The following checks passed after cleanup:
- `http://10.5.30.6:6333/healthz``200`
- `http://10.5.30.6:8282/``200`
- `http://10.5.30.6:5055/``200`
### Compose validation
These both rendered successfully:
- `/mnt/docker-ssd/docker/compose/ai/docker-compose.yaml`
- `/mnt/docker-ssd/docker/compose/media/docker-compose.yaml`
## Important non-actions
- The Docker daemon was **not** restarted.
- The stacks were **not** redeployed during this cleanup pass.
- No repo-unrelated working tree changes were touched.
That was intentional: the active tracked compose files already matched repo, and the services were healthy, so a no-redeploy reconciliation was the safer path.
## Why this matters
The main value of this cleanup was not changing application behavior. It was restoring operator confidence that:
- the live compose roots are the authoritative ones
- the running containers point back to those roots
- repo state and live state are aligned again
- future maintenance can start from the compose directories instead of from ad hoc inspect output
## Next time: safer recovery procedure
If a PD stack service must be rebuilt under pressure:
1. **Identify the authoritative compose directory first.**
- Prefer `/mnt/docker-ssd/docker/compose/<stack>/docker-compose.yaml` over ad hoc recreation.
2. **Take a backup before cleanup, even if the service is already back up.**
- Capture compose trees, relevant appdata, and `docker inspect` output.
3. **If manual recreation is unavoidable, treat it as temporary stabilization only.**
- Reconcile labels, compose roots, and repo state immediately afterward.
4. **Compare repo ↔ live compose content before redeploying.**
- If tracked content already matches and health is good, avoid unnecessary churn.
5. **Prune stale backup/override files from live compose dirs only after backup.**
- Leaving old compose fragments nearby makes future incident work harder.
6. **Verify both health and metadata.**
- A container being up is not enough; confirm compose labels, working dir, and health endpoints.
## Follow-up status
No immediate further action was required after the cleanup pass. The remaining lesson is procedural: manual container recreation is acceptable as a short-term rescue, but it should always be followed by a backup-first repo/live reconciliation so drift does not become the new baseline.