/ipa-k8s-help — Kubernetes Setup & Troubleshooting Guide
This skill is the interactive counterpart to make doctor. It helps a builder
stand up and debug the local k3d/Tilt inner-dev loop for the app-lib
service, and it guides (does not perform) a manual EKS deploy of the
Helm chart.
This is a guidance/helper skill, not a stack skill — it wraps no
CloudFormation contract and composes nothing.
/ipa-k8s-help vs /ipa-help
/ipa-help reports IPA lifecycle state (init → compose → prepare →
deploy) and suggests the next lifecycle skill. It is about CloudFormation
tiers.
/ipa-k8s-help (this skill) is about the Kubernetes offering: the local
Tilt loop and the manual EKS path. It checks tools and configuration and
guides; it is not a lifecycle inspector.
Iron rules (never violate)
- Check and instruct only. NEVER auto-install a tool. NEVER run
ctlptl apply, tilt up, helm install/upgrade, or kubectl apply on the
builder's behalf. Print the exact command and let the builder run it.
- Read-only diagnostics. You may run read-only checks (
command -v,
aws sts get-caller-identity, aws dynamodb describe-table,
kubectl config current-context, helm template). You may NOT mutate any
cluster or cloud resource.
- Missing
.env is a first-class failure — never assume it exists.
- Degrade gracefully. When a cloud resource is unreachable, report "cannot
verify" and say why; do not fail the whole flow.
Scope A — Local loop (k3d + Helm + Tilt)
Work through these steps in order. Stop at the first failure, print the exact
remediation, and re-run from that step once the builder reports it fixed.
Step 1 — Tool check
Run command -v docker kubectl helm k3d tilt ctlptl aws. For each missing tool,
name it and print the exact install command:
Do not proceed to Step 2 until every tool is present. (This mirrors
make doctor; either is a valid gate.)
Step 2 — Credentials check
- If the repo-root
.env is absent, STOP: report a first-class "missing
.env" failure and instruct the builder to run /ipa-init (or copy
.env.example). Do not assume defaults.
- Otherwise run
set -a; . ./.env; set +a; aws sts get-caller-identity. This
honors AWS_PROFILE from .env when set, or the default credential chain
when not. On failure (expired/absent credentials, wrong profile), print the
error and the fix: refresh credentials for your profile however that profile
authenticates (e.g. aws sso login --profile <name>, renew an IAM session,
or aws configure), then re-run.
Step 3 — Convergence check
The local pod must read the SAME DynamoDB table the backend tier created. Read
APP_NAMESPACE, APP_ENV, AWS_REGION from .env. Confirm they are non-empty
and tell the builder the table name the pod will resolve:
{APP_NAMESPACE}_{APP_ENV}_passengers. These are injected by the Tiltfile from
.env — the builder should NOT hand-edit the overlay to set them.
Step 4 — Table-exists check
Run aws dynamodb describe-table --table-name {APP_NAMESPACE}_{APP_ENV}_passengers --region {AWS_REGION}. If it does not exist, the data plane is not deployed:
instruct the builder to run /ipa-compose → /ipa-prepare → /ipa-deploy for the
backend tier with EnablePassengersTable=true. If the call is AccessDenied,
their profile lacks DynamoDB read on that table.
Step 5 — Cluster state
Run kubectl config current-context. If it is not the local k3d context
(k3d-ipa-local), the cluster is not set up: instruct make local-setup.
If ctlptl get cluster shows no cluster, same remedy.
Step 6 — Guided run
Walk the builder through, one command at a time (they run each):
make local-setup — creates the k3d cluster + registry.
make local-up — Tilt builds, deploys, port-forwards :8000.
- Verify:
curl -s localhost:8000/health → {"status":"ok"}.
- Tear down when done:
make local-destroy.
Step 7 — Triage table
| Symptom |
Likely cause |
Remediation (builder runs) |
make doctor names a missing CLI |
tool not installed |
run the printed install command |
missing .env |
not initialized |
/ipa-init (or copy .env.example) |
aws-check-creds red in Tilt |
expired/absent credentials for AWS_PROFILE (or default chain) |
refresh creds for your profile (e.g. aws sso login --profile <name>, renew IAM session), then re-trigger |
| port 8000 already bound |
another process on :8000 |
lsof -i :8000 then free it, or change the port-forward |
pod ResourceNotFoundException |
table not deployed / name mismatch |
Step 4; confirm .env matches the deployed tier |
pod AccessDeniedException |
profile lacks DynamoDB read |
grant read on {ns}_{env}_passengers, or switch profile |
kubectl context wrong |
pointed at another cluster |
kubectl config use-context k3d-ipa-local |
| live-update not syncing |
edited outside app-lib/src/app_lib |
edit under the synced path, or expect a rebuild |
| cluster won't create |
docker not running |
start Docker Desktop, re-run make local-setup |
Scope B — Manual EKS deploy (guidance only)
For a cloud deploy, walk the builder through the annotated overlay and validate
pre-conditions — apply nothing. The authoritative guidance lives in
infra/k8s/envs/eks/CLAUDE.md; reference that file rather than restating its
steps here (single source — avoids drift).
What to do:
- Open
infra/k8s/envs/eks/values.yaml and walk each override point with the
builder (ECR image URI, IRSA role ARN, namespace/region env, replicas,
resources). Point them at eks/README.md (copy-and-customize) and
eks/CLAUDE.md (the guidance + review checklist).
- Validate the pre-conditions from
eks/CLAUDE.md using read-only checks only:
credentials, target kubectl context (must NOT be the local k3d cluster),
ECR repo + pushed image, IRSA role exists with a scoped trust + DynamoDB
policy, and convergence env resolves to the intended table.
- Render for inspection (does not deploy):
helm template app-lib infra/k8s/helm/app-lib -f infra/k8s/envs/eks/values.yaml.
- Hand the deploy command to the builder to run themselves:
helm upgrade --install app-lib infra/k8s/helm/app-lib -f <their-values>.
You do not run it.
Degrade gracefully: if a cloud resource is unreachable (no cluster access,
ECR not reachable), report exactly what you could not verify and continue with
what you can. EKS-scope behavior is not verifiable without a real cluster.
What this skill does NOT do
- Does not install any tool.
- Does not create, apply, or modify any cluster or cloud resource.
- Does not run
make local-up, tilt up, ctlptl apply, or any helm/
kubectl apply for the builder.
- Does not report IPA lifecycle state — that is
/ipa-help.
1---2name: ipa-k8s-help3description: Guide and troubleshoot the local Kubernetes (k3d + Helm + Tilt) inner-dev loop, and guide a manual EKS deploy of the app-lib chart. Use when the user says 'set up local k8s', 'tilt won't start', 'pod can't reach DynamoDB', 'my local cluster is broken', 'deploy this chart to EKS', or invokes /ipa-k8s-help. NOT for IPA lifecycle state — that is /ipa-help.4---56# /ipa-k8s-help — Kubernetes Setup & Troubleshooting Guide78This skill is the interactive counterpart to `make doctor`. It helps a builder9stand up and debug the **local** k3d/Tilt inner-dev loop for the `app-lib`10service, and it **guides** (does not perform) a **manual EKS** deploy of the11Helm chart.1213**This is a guidance/helper skill, not a stack skill** — it wraps no14CloudFormation contract and composes nothing.1516## /ipa-k8s-help vs /ipa-help1718- **`/ipa-help`** reports IPA *lifecycle* state (init → compose → prepare →19 deploy) and suggests the next lifecycle skill. It is about CloudFormation20 tiers.21- **`/ipa-k8s-help`** (this skill) is about the *Kubernetes* offering: the local22 Tilt loop and the manual EKS path. It checks tools and configuration and23 guides; it is not a lifecycle inspector.2425## Iron rules (never violate)2627- **Check and instruct only.** NEVER auto-install a tool. NEVER run28 `ctlptl apply`, `tilt up`, `helm install/upgrade`, or `kubectl apply` on the29 builder's behalf. Print the exact command and let the builder run it.30- **Read-only diagnostics.** You may run read-only checks (`command -v`,31 `aws sts get-caller-identity`, `aws dynamodb describe-table`,32 `kubectl config current-context`, `helm template`). You may NOT mutate any33 cluster or cloud resource.34- **Missing `.env` is a first-class failure** — never assume it exists.35- **Degrade gracefully.** When a cloud resource is unreachable, report "cannot36 verify" and say why; do not fail the whole flow.3738---3940## Scope A — Local loop (k3d + Helm + Tilt)4142Work through these steps in order. Stop at the first failure, print the exact43remediation, and re-run from that step once the builder reports it fixed.4445### Step 1 — Tool check4647Run `command -v docker kubectl helm k3d tilt ctlptl aws`. For each missing tool,48name it and print the exact install command:4950| Tool | Install |51|---|---|52| docker | https://docs.docker.com/get-docker/ |53| kubectl | `brew install kubectl` |54| helm | `brew install helm` |55| k3d | `brew install k3d` |56| tilt | `brew install tilt-dev/tap/tilt` |57| ctlptl | `brew install tilt-dev/tap/ctlptl` |58| aws | https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html |5960Do not proceed to Step 2 until every tool is present. (This mirrors61`make doctor`; either is a valid gate.)6263### Step 2 — Credentials check6465- If the repo-root `.env` is **absent**, STOP: report a first-class "missing66 `.env`" failure and instruct the builder to run `/ipa-init` (or copy67 `.env.example`). Do not assume defaults.68- Otherwise run `set -a; . ./.env; set +a; aws sts get-caller-identity`. This69 honors `AWS_PROFILE` from `.env` when set, or the default credential chain70 when not. On failure (expired/absent credentials, wrong profile), print the71 error and the fix: refresh credentials for your profile however that profile72 authenticates (e.g. `aws sso login --profile <name>`, renew an IAM session,73 or `aws configure`), then re-run.7475### Step 3 — Convergence check7677The local pod must read the SAME DynamoDB table the backend tier created. Read78`APP_NAMESPACE`, `APP_ENV`, `AWS_REGION` from `.env`. Confirm they are non-empty79and tell the builder the table name the pod will resolve:80`{APP_NAMESPACE}_{APP_ENV}_passengers`. These are injected by the Tiltfile from81`.env` — the builder should NOT hand-edit the overlay to set them.8283### Step 4 — Table-exists check8485Run `aws dynamodb describe-table --table-name {APP_NAMESPACE}_{APP_ENV}_passengers86--region {AWS_REGION}`. If it does not exist, the data plane is not deployed:87instruct the builder to run `/ipa-compose → /ipa-prepare → /ipa-deploy` for the88backend tier with `EnablePassengersTable=true`. If the call is AccessDenied,89their profile lacks DynamoDB read on that table.9091### Step 5 — Cluster state9293Run `kubectl config current-context`. If it is not the local k3d context94(`k3d-ipa-local`), the cluster is not set up: instruct `make local-setup`.95If `ctlptl get cluster` shows no cluster, same remedy.9697### Step 6 — Guided run9899Walk the builder through, one command at a time (they run each):1001011. `make local-setup` — creates the k3d cluster + registry.1022. `make local-up` — Tilt builds, deploys, port-forwards :8000.1033. Verify: `curl -s localhost:8000/health` → `{"status":"ok"}`.1044. Tear down when done: `make local-destroy`.105106### Step 7 — Triage table107108| Symptom | Likely cause | Remediation (builder runs) |109|---|---|---|110| `make doctor` names a missing CLI | tool not installed | run the printed install command |111| missing `.env` | not initialized | `/ipa-init` (or copy `.env.example`) |112| `aws-check-creds` red in Tilt | expired/absent credentials for `AWS_PROFILE` (or default chain) | refresh creds for your profile (e.g. `aws sso login --profile <name>`, renew IAM session), then re-trigger |113| port 8000 already bound | another process on :8000 | `lsof -i :8000` then free it, or change the port-forward |114| pod `ResourceNotFoundException` | table not deployed / name mismatch | Step 4; confirm `.env` matches the deployed tier |115| pod `AccessDeniedException` | profile lacks DynamoDB read | grant read on `{ns}_{env}_passengers`, or switch profile |116| `kubectl` context wrong | pointed at another cluster | `kubectl config use-context k3d-ipa-local` |117| live-update not syncing | edited outside `app-lib/src/app_lib` | edit under the synced path, or expect a rebuild |118| cluster won't create | docker not running | start Docker Desktop, re-run `make local-setup` |119120---121122## Scope B — Manual EKS deploy (guidance only)123124For a cloud deploy, **walk the builder through the annotated overlay and validate125pre-conditions — apply nothing.** The authoritative guidance lives in126`infra/k8s/envs/eks/CLAUDE.md`; **reference that file** rather than restating its127steps here (single source — avoids drift).128129What to do:1301311. Open `infra/k8s/envs/eks/values.yaml` and walk each override point with the132 builder (ECR image URI, IRSA role ARN, namespace/region env, replicas,133 resources). Point them at `eks/README.md` (copy-and-customize) and134 `eks/CLAUDE.md` (the guidance + review checklist).1352. Validate the pre-conditions from `eks/CLAUDE.md` using read-only checks only:136 credentials, target `kubectl` context (must NOT be the local k3d cluster),137 ECR repo + pushed image, IRSA role exists with a scoped trust + DynamoDB138 policy, and convergence env resolves to the intended table.1393. Render for inspection (does not deploy):140 `helm template app-lib infra/k8s/helm/app-lib -f infra/k8s/envs/eks/values.yaml`.1414. Hand the deploy command to the builder to run themselves:142 `helm upgrade --install app-lib infra/k8s/helm/app-lib -f <their-values>`.143 You do not run it.144145**Degrade gracefully:** if a cloud resource is unreachable (no cluster access,146ECR not reachable), report exactly what you could not verify and continue with147what you can. EKS-scope behavior is not verifiable without a real cluster.148149---150151## What this skill does NOT do152153- Does not install any tool.154- Does not create, apply, or modify any cluster or cloud resource.155- Does not run `make local-up`, `tilt up`, `ctlptl apply`, or any `helm`/156 `kubectl` apply for the builder.157- Does not report IPA lifecycle state — that is `/ipa-help`.