Skip to main content
Source

This page is generated from skills/eks-ingress-migration/references/lbc-migrate-toolkit.md. Edit the source, not this page.

lbc-migrate — AWS Load Balancer Controller (LBC) Ingress → Gateway API Automation Toolkit

Not a standalone rated section. This describes the automation sub-path for Option 1 (Gateway API) in the report (report-generation.md). It is the second hop of the complete path this skill covers: NGINX Ingress → LBC Ingress (alb-migration.md) → Gateway API (this file). Use it when the estate is already on the AWS Load Balancer Controller (ALB Ingress) and the target is Gateway API. All discovery is read-only.

⚠️ Assessor scope — this skill assesses, it does not perform the migration. Only Step 1 (Translate), the local generation of dry-run manifests, and read-only inspection of the resulting plans/diffs are safe for the assessor to run: lbc-migrate reads the cluster read-only (get/list) and writes YAML to a local directory. Everything that changes the cluster or AWS is an OPERATOR / DevOps action the assessor documents but MUST NOT execute — installing the Gateway API / LBC Gateway CRDs (kubectl apply), enabling the IngressPlanAnnotation feature gate (helm upgrade), applying the generated dry-run manifests (Step 2 — creates live Gateway objects in the cluster, even though no ALB is provisioned), applying Gateway manifests with --dry-run=false or kubectl annotate (Step 3 — creates real ALBs), and kubectl delete ingress (Step 6). Present these as steps for the cluster owner to run under their change-management process, never as commands the assessment runs. (Matches the sibling references: all assessment checks are read-only.)

What it is

The Ingress-to-Gateway API migration toolkit for the AWS Load Balancer Controller (LBC) automates the conversion that migration-plan.md Phase 2 otherwise hand-authors. It shipped in the LBC v3.4.0 release (2026-06-03) and was announced on the AWS networking blog in July 2026. It is recent — cite the sources at the bottom of this file and do not over-generalize beyond them. It has two parts:

  • lbc-migrate CLI — translates LBC Ingress resources into equivalent Gateway API manifests (annotations, path rules, IngressGroups). Reads static YAML/JSON or a live cluster (--from-cluster). Cluster access is read-only (list/get only); it never creates, updates, or deletes cluster resources. Existing Deployments and Services are reused as-is — the tool generates only routing-layer resources.
  • Migration Console — a local, read-only web UI bundled in the same binary (lbc-migrate --console) that shows a field-by-field diff of the AWS resource plans the ingress controller and the gateway controller would each produce, so equivalence can be confirmed before any production change.

Prerequisites (CRDs + build tag)

The toolkit is a build artifact of the LBC v3.4.0 release (see the callout below for why that is not a controller runtime requirement). What the path needs:

  • The lbc-migrate CLI, built from the LBC v3.4.0 tag (see Install below). The controller runtime requirement is the same as the manual path — ≥ v2.13.3 (L4) / ≥ v2.14 (L7), per gateway-api.md — and Gateway API support turns on automatically once the CRDs are present.
  • Gateway API standard CRDs v1.5.0 and the LBC Gateway CRDs (OPERATOR action — cluster change; the assessor documents these, does not run them):
    # Standard Gateway API CRDs
    kubectl apply --server-side=true \
    -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml
    # LBC Gateway CRDs (LoadBalancerConfiguration, TargetGroupConfiguration, ...)
    kubectl apply \
    -f https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v3.4.0/config/crd/gateway/gateway-crds.yaml
  • Confirm the ALB Gateway controller is live:
    kubectl -n kube-system logs deploy/aws-load-balancer-controller | grep "gateway.k8s.aws/alb"

Where the versions come from. The lbc-migrate CLI ships in the AWS Load Balancer Controller v3.4.0 release — build it from that tag (see Install below). It is not a higher controller runtime tier than the hand-authored path: the controller's Gateway API runtime support is the same either way (≥ v2.13.3 L4 / ≥ v2.14 L7, per gateway-api.md). What the toolkit path needs is the current standard Gateway API CRDs (v1.5.0) plus the LBC Gateway CRDs (installed above). On EKS Auto Mode, Gateway API is provided built-in via the eks.amazonaws.com controller; the upstream guide does not document whether lbc-migrate targets that built-in path — do not assume it applies, verify against the guide before recommending it there.

Install (build from source)

# Pin to the v3.4.0 tag — the lbc-migrate CLI ships in that release
git clone --branch v3.4.0 --depth 1 https://github.com/kubernetes-sigs/aws-load-balancer-controller.git
cd aws-load-balancer-controller
make lbc-migrate # binary at bin/lbc-migrate
make install-lbc-migrate # optional: symlink onto PATH
# or run without building: go run ./cmd/lbc-migrate/ [flags]
ModeCommandNotes
Individual fileslbc-migrate -f ingress1.yaml,ingress2.yamlCombine with --input-dir; warns on missing referenced Services/IngressClass/IngressClassParams
Directorylbc-migrate --input-dir ./manifests/Same missing-resource warnings
Live clusterlbc-migrate --from-cluster --namespaces prod (or --all-namespaces; add --ingress-name my-api to narrow to a single Ingress)Recommended — auto-fetches referenced Services/IngressClass/IngressClassParams for the most accurate translation. Read-only RBAC (get/list) is sufficient.

Useful flags: --output-dir (default ./gateway-output), --output-format yaml|json, --split=namespace (one file per namespace + a gatewayclass file), --dry-run (default true), --console/--port.

Output resources

KindAPI groupNotes
GatewayClassgateway.networking.k8s.ioAlways controllerName: gateway.k8s.aws/alb; one per run
Gatewaygateway.networking.k8s.ioOne per Ingress (or per group.name group); listeners from listen-ports
HTTPRoutegateway.networking.k8s.ioOne or more per Ingress; ssl-redirect becomes a RequestRedirect filter
LoadBalancerConfigurationgateway.k8s.awsLB-level settings; only when LB-level annotations present
TargetGroupConfigurationgateway.k8s.awsPer-service TG settings; only when TG-level annotations present
ListenerRuleConfigurationgateway.k8s.awsAuth, fixed-response, source-IP conditions

Existing Deployment/Service are reused — HTTPRoute backendRefs point at your Services by name. You replace only the Ingress manifest.

The 6-step guided flow (each step is safe to pause at)

  1. Translate (assessor-safe — read-only)lbc-migrate --from-cluster --namespaces <ns> --output-dir ./gw/. Reads the cluster (get/list) only and writes YAML locally. Rollback: delete the generated files.
  2. Dry-run preview (recommended) (generating the manifests locally and inspecting a plan are read-only; enabling the feature gate and applying the dry-run manifests are OPERATOR actions) — verify the generated Gateway will produce the same ALB config before creating anything:
    • Enable the ingress-side plan (OPERATOR action — helm upgrade restarts the controller): set the LBC feature gate IngressPlanAnnotation=true (Helm: --set controllerConfig.featureGates.IngressPlanAnnotation=true). The ingress controller then writes its model to each Ingress as alb.ingress.kubernetes.io/dry-run-plan. Sensitive-data caveat: the dry-run-plan annotation embeds the controller's full built model (listener/target-group config, cert ARNs, auth settings) on the live object and is readable by anyone with get on Ingresses/Gateways — treat it as potentially sensitive, avoid pasting it verbatim into reports, and disable the gate at cleanup (Step 6) so the annotation is removed.
    • Apply the generated manifests (OPERATOR action — the cluster owner applies; the assessor documents it and never runs it). They carry gateway.k8s.aws/dry-run: "true" by default, so the gateway controller writes gateway.k8s.aws/dry-run-plan without creating an ALB — but the Gateway objects themselves are real, live cluster resources, which is why this is not an assessment action.
    • Compare (assessor-safe — read-only): lbc-migrate --console (field-by-field diff) or inspect gateway.k8s.aws/dry-run-plan directly with kubectl get gateway … -o jsonpath=….
    • Rollback (OPERATOR action): delete the dry-run Gateway. (Dry-run is ignored on an already-provisioned Gateway — it is for previewing new Gateways, not pausing live ones.)
  3. Apply (OPERATOR action — creates real ALBs) — regenerate with --dry-run=false (GitOps-clean) or kubectl annotate … gateway.k8s.aws/dry-run- in place. LBC creates new ALBs alongside the existing Ingress ALBs, pointing at the same Services/Pods. Rollback: delete the Gateway resources.
  4. Verify (read-only checks)Programmed: True on the Gateway, status.addresses has the new ALB DNS, aws elbv2 describe-target-health all healthy, CloudWatch HealthyHostCount/HTTPCode_ELB_5XX_Count/TargetResponseTime nominal; curl the Gateway ALB directly.
  5. Shift traffic (OPERATOR action) — move from the Ingress ALB to the Gateway ALB gradually. The upstream guide leaves the exact mechanism to your environment (DNS provider / traffic tooling) and gives AWS Global Accelerator as one example; Route 53 weighted records are another common DNS-based option. Whichever you use, plan for quick rollback and gradual shifting. Rollback: shift back.
  6. Cleanup (OPERATOR action — deletes the Ingress and its ALB)kubectl delete ingress <name> (LBC removes the old ALB/target groups/listener rules), delete any leftover dry-run Gateway, and disable the IngressPlanAnnotation gate when no migrations are in progress.

Dual-ALB cost during Steps 3–5. The original Ingress ALBs and the new Gateway ALBs run in parallel until cleanup — expect duplicate ALB-hours and LCU-hours on the bill for the migration window. This is the trade for a non-disruptive, reversible cutover.

Traffic-shift blocker — direct ALB DNS. If clients reach the app directly via the Ingress ALB DNS name (not a Route 53 / custom domain), traffic cannot be shifted without updating every client. Confirm the traffic path before Step 5.

Known gaps — where the tool skips or warns (hand-edit required)

  • url-rewrite / host-header-rewrite with capture groups (e.g. replace: "/$1", "$1.example.org") — no Gateway API equivalent. The URLRewrite filter does static replacements only (ReplaceFullPath/ReplacePrefixMatch for paths, PreciseHostname for hostnames), which cannot reproduce a capture-group substitution. The tool skips the dynamic transform — the original regex/replace is discarded from the generated manifest (upstream documents the skip but not a warning for this specific case, so diff the output rather than relying on one) — and it stays a Tier-A / Re-architecture-Gate item: a hand redesign, not a mechanical fill-in. (Only static prefix strips — e.g. a fixed /api strip — map cleanly to ReplacePrefixMatch; those translate automatically.) This reinforces the skill's existing snippet/rewrite blind-spot warnings.
  • ALB listener-rule count & priority differences — the generated Gateway can yield a different number of ALB listener rules and a different rule-priority order than the source Ingress (Gateway API expresses precedence differently). Review the dry-run diff and the upstream Known Differences from Ingress before cutover.
  • WAF Classic (alb.ingress.kubernetes.io/waf-acl-id, web-acl-id) — not supported. Migrate to WAFv2 (wafv2-acl-arn) on the source Ingress before running lbc-migrate, or the generated Gateway has no WAF protection.
  • Frontend NLB (alb.ingress.kubernetes.io/frontend-nlb-*) — not supported yet.
  • group.order — no Gateway API equivalent for ALB rule priority. The tool uses it to pick the primary group member, then warns. If Ingresses rely on group.order to resolve overlapping host+path rules, verify precedence with the dry-run before shifting.
  • External Target Groups in actions.* — an external TG can attach to only one ALB at a time. During the side-by-side window, delete the Ingress rules referencing it (cutover) or duplicate the TG.
  • defaultBackend + host rules — Gateway API has no defaultBackend; the tool emits a separate catch-all HTTPRoute → one extra ALB listener rule and its own target group vs the Ingress. Expected behavior; note it in the report.
  • Complex host-header wildcards / regex that don't conform to Gateway API hostname format are rejected by the API server on apply — review before applying.

When to recommend lbc-migrate vs the manual path

  • Recommend lbc-migrate when the estate is on LBC ALB Ingress and the target is Gateway API — it automates the bulk translation and gives dry-run validation. This is the preferred Option 1 sub-path once the prerequisites above are in place: controller at the Gateway API runtime baseline (≥ v2.13.3 L4 / ≥ v2.14 L7), standard Gateway API CRDs v1.5.0 plus the LBC Gateway CRDs installed, and the CLI built from the v3.4.0 tag.
  • Keep the manual HTTPRoute authoring (migration-plan.md Phase 2, gateway-api.md) as the fallback for the skip-or-warn cases above, or on EKS Auto Mode where the built-in provider path differs. For an estate below the Gateway API runtime baseline (controller < v2.13.3 L4 / < v2.14 L7) or without standard CRDs v1.5.0, note that Option 1 itself is blocked until the operator upgrades — beyond a handful of Ingresses, recommend upgrading the controller to the current v3.4.0 release line first (it clears the runtime baseline and provides the CLI) rather than hand-authoring HTTPRoutes against an unsupported controller.
  • Note: lbc-migrate migrates LBC Ingress → Gateway API. It does not convert raw NGINX annotations — do the NGINX → LBC Ingress hop first (alb-migration.md), then run lbc-migrate.

Reference URLs (cite these; do not invent)