Incident: hungryhub + utility Namespace Deletion — 2026-06-08
Summary
Triggering terraform apply for eks-services with new code on branch fix/409-ca-priority-expander deleted the hungryhub and utility Kubernetes namespaces, taking down all workloads in those namespaces for ~30–45 minutes.
Timeline (UTC+7 / Bangkok)
| Time | Event |
|---|---|
| ~11:10 | Terraform Deployment Workflow triggered for eks-services, environment=prod, action=apply on main branch (run #27115482778) |
| ~11:18 | Apply failed — kubernetes_namespace resources for hungryhub, utility, hh-syn-public, etc. errored with “already exists”. These namespaces existed in cluster but were NOT in terraform state. |
| ~11:20 | Terraform had already deleted hungryhub and utility namespaces mid-apply before hitting the error. All pods/deployments/services in those namespaces destroyed. |
| ~11:21 | hungryhub and utility namespaces recreated manually via kubectl create namespace |
| ~11:22 | hungryhub-server-config ConfigMap copied from hh-cosmos-private to unblock sidekiq pods |
| ~11:30 | Most hungryhub pods recovered (30 Running). hungryhub-menu still down (missing hungryhub-menu-config) |
| ~11:40 | CodePipeline prod-hh-server-main completed hungryhub deploy step, restoring remaining pods |
Root Cause
namespace.tf in eks-services declares kubernetes_namespace resources for all prod namespaces. These namespaces had been created manually (pre-Terraform) and were never imported into Terraform state. Terraform therefore treated them as resources to create.
On the first apply after this discrepancy was exposed, Terraform attempted to create all namespaces. For namespaces already in state (dev-preview-*), it refreshed correctly. For the 10 that were NOT in state, it tried to create them. hungryhub and utility completed the delete step before the conflict errors terminated the apply — the others got “already exists” because their create was attempted while they still existed.
Why only hungryhub and utility were deleted: Terraform processes resources in parallel. Those two happened to complete delete before the conflict errors terminated the apply run.
Services Affected
| Service | Impact |
|---|---|
hungryhub-sidekiq-critical | Down ~20 min — critical queue jobs delayed |
hungryhub-sidekiq-default | Down ~20 min |
hungryhub-sidekiq-kafka | Down ~20 min |
hungryhub-sidekiq-inv | Down ~20 min |
hungryhub-sidekiq-lp | Down ~20 min |
hungryhub-karafka | Down ~20 min |
hungryhub-helper | Down ~20 min |
hungryhub-menu | Down ~40 min (needed hungryhub-menu-config from pipeline) |
ai-text-to-image | Down indefinitely — k8s Secret deleted with namespace; values not stored in SSM/Secrets Manager |
hh-tiger, hh-puma | Briefly down, recovered quickly |
utility namespace | Empty — no active workloads lost |
Recovery Steps Taken
-
Recreated namespaces:
kubectl create namespace hungryhub kubectl create namespace utility -
Copied
hungryhub-server-configConfigMap fromhh-cosmos-private(pods need it to start):kubectl get configmap hungryhub-server-config -n hh-cosmos-private -o json | \ python3 -c " import json,sys cm = json.load(sys.stdin) cm['metadata'] = {'name': 'hungryhub-server-config', 'namespace': 'hungryhub'} print(json.dumps(cm)) " | kubectl apply -f - -
Pods rescheduled automatically once namespace + ConfigMap existed.
-
CodePipeline
prod-hh-server-main(already running) completed the hungryhub deploy step, restoring remaining workloads.
What Was NOT Recovered Automatically
ai-text-to-image Secret — contained AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, GEMINI_API_KEY, FAL_KEY. Only existed as a Kubernetes Secret, not backed by SSM or Secrets Manager. Must be recreated manually before ai-text-to-image can start.
Fix Applied
Added namespace-imports.tf to eks-services with native Terraform import blocks (TF 1.5+) for all 10 untracked namespaces. On next successful terraform apply, these namespaces will be imported into state and Terraform will manage them without deleting.
PR: https://github.com/hungryhub-team/hungryhub-terraform/pull/411
Prevention / Follow-up Actions
| Action | Priority |
|---|---|
Store ai-text-to-image secret values in SSM or AWS Secrets Manager | HIGH |
Fix eks-services terraform apply (namespace import run still failing) | HIGH |
Audit all k8s Secrets in hungryhub namespace — confirm each is backed by external store | HIGH |
Add lifecycle { prevent_destroy = true } to all kubernetes_namespace resources | MEDIUM |
Remove dev-preview-* namespaces from prod namespace.tf (sandbox-only) | MEDIUM |
Run terraform plan before every apply and gate on no unexpected destroys | MEDIUM |
Lessons Learned
- Never apply terraform on a module with unimported live resources. Run
terraform planfirst; gate on zero unexpected destroys. - Terraform delete+create is not atomic. A failed apply can leave resources partially destroyed.
- k8s Secrets must be backed by an external secret store. A Secret living only in etcd is one namespace deletion from permanent loss.
- Add
prevent_destroyto namespace resources to require explicit override before deletion.
Related
- EKS Cost Optimization 2026 — context for the CA expander change (#409) that triggered the apply
- hungryhub-terraform PR#411