kgateway is a control plane implementing the Kubernetes Gateway API for Envoy. It's built on KRT (Kubernetes Declarative Controller Runtime from Istio) and uses a plugin-based architecture for extensibility.
- Policy → IR: Plugins translate CRDs to PolicyIR (close to Envoy protos). Done once per policy CRD change.
- HTTPRoute/Gateway → IR with Policies Attached: Core kgateway aggregates routes/gateways and performs policy attachment via
targetRefs. - IR → xDS: Translates to Envoy config. Plugins provide
NewGatewayTranslationPassfunctions called during route/listener translation.
See /devel/architecture/overview.md and the translation diagram at /devel/architecture/translation.svg.
- cmd/: 3 binaries:
kgateway(controller),envoyinit(does some envoy bootstrap config manipulation),sds(secret server) - api/v1alpha1/kgateway/: kgateway CRD definitions. Use
+kubebuildermarkers for validation/generation - pkg/pluginsdk/: Plugin interfaces (
Plugin,PolicyPlugin,BackendPlugin) - pkg/kgateway/extensions2/plugins/: Plugin implementations (trafficpolicy, httplistenerpolicy, etc.)
- pkg/kgateway/krtcollections/: KRT collections for core resources
- test/e2e/: End-to-end tests using custom framework (see test/e2e/README.md)
At the core kgateway translates kubernetes Gateway API resources to Envoy configuration. To add features like policies, or backends, we use a plugin system. Each plugin contributes to the translation, usually by adding a new type of CRD (most commonly a Policy CRD) that users can create to express their desired configuration.
Policy CRDs are attached to Gateway API resources via targetRefs or targetSelectors. kgateway manages the attachment
of policies to the appropriate resources during translation.
The plugin is then called in the translation process to affect the dataplane configuration. To do this efficiently, the plugin should convert the CRD to an intermediate representation (IR) that is as close to Envoy protos as possible. This minimizes the amount of logic needed in the final translation, and allows for better status reflected back to the user if there are errors.
Plugins are stateless across translations but maintain state during a single gateway translation via ProxyTranslationPass. Each plugin:
- Provides a KRT collection of
ir.PolicyWrapper(containsPolicyIR+TargetRefs) - Implements
NewGatewayTranslationPass(tctx ir.GwTranslationCtx, reporter reporter.Reporter) ir.ProxyTranslationPass - Can process backends via
ProcessBackend,PerClientProcessBackend, orPerClientProcessEndpoints
Example: /pkg/kgateway/extensions2/plugins/trafficpolicy/traffic_policy_plugin.go
If you intend to include all source code, run 'go' commands that accept '-tags' with '-tags e2e'.
IRs output by KRT collections must implement Equals(other T) bool:
- Compare ALL fields or mark with
// +noKrtEquals(last line of comment) - Never use
reflect.DeepEqual(flagged by custom analyzer in/hack/krtequals/) - Use proto equality helpers:
proto.Equal(), not==
Common targets:
make generate-code: Ignores stamp files, generates all (takes around 30 seconds)make generate-all: Uses stamp files, only regenerates changed code (fast)make verify: CI target - always regenerates everything, checks git diffmake go-generate-apis: Only API changes (~1m)make fmtormake fmt-changed: Format code (always run before commit)
After API changes: Run make go-generate-apis then make fmt-changed. The Makefile uses dependency tracking in _output/stamps/.
If not sure, just run make generate-all.
- Unit tests: For new code, avoid Ginkgo. You may use Gomega matchers if appropriate.
- E2E tests: Use framework in
/test/e2e/- DO NOT directly kubectl apply in tests - Custom matchers:
/test/gomega/matchers/(e.g.,HaveHttpResponse) - Transforms: Compose matchers with
WithTransform()(see/devel/testing/writing-tests.md) - Prefer explicit error checking:
Expect(err).To(MatchError("msg"))overHaveOccurred() - Add descriptions:
Expect(x).To(BeEmpty(), "list should be empty on init")
Run tests:
make test TEST_PKG=./path/to/package # Unit tests
make e2e-test TEST_PKG=./test/e2e/tests/... # E2E tests
make unit # All unit tests (excludes e2e)- Create
*_types.goinapi/v1alpha1/with+kubebuildermarkers. You can use+kubebuilder:validation:AtLeastOneOfor+kubebuilder:validation:ExactlyOneOffor field groups. - Required fields: Use
+required, NOomitemptytag - Optional fields: Use
+optional, pointer types (except slices/maps),omitemptytag - Durations: Use
metav1.Durationwith CEL validation - Document defaults with
+kubebuilder:default=... - Run
make go-generate-apis(generates CRDs, clients, RBAC in helm chart) - Register CRD to the client in
pkg/apiclient/types.go - Add the CRD to the fake client's
filterObjectsinpkg/apiclient/fake/fake.goandAllCRDsintest/testutils/crd.go.
See /api/README.md for full guidelines.
- Add the field to the appropriate
Specstruct in the CRD Go type inapi/v1alpha1/. - Add validation markers as needed (e.g.,
+kubebuilder:validation:MinLength=1,+optional, etc.) - Run
make go-generate-apisto regenerate code. - Update the IR struct in the plugin package (
pkg/kgateway/extensions2/plugins/<plugin_name>/) to include the new field. - Add yaml tests cases in
pkg/kgateway/translator/gateway/gateway_translator_test.go. The yaml inputs go inpkg/kgateway/translator/gateway/testutils/inputs/. DO NOT create the outputs by yourself. Instead, run your tests with environment variableREFRESH_GOLDEN=true. For example:REFRESH_GOLDEN=true go test -timeout 30s -run ^TestBasic$/^ListenerPolicy_with_proxy_protocol_on_HTTPS_listener$ github.com/kgateway-dev/kgateway/v2/pkg/kgateway/translator/gatewayIt will generate the outputs for you automatically in thepkg/kgateway/translator/gateway/testutils/outputs/folder. Once the outputs are generated, inspect them to see they contain the changes you expect, and alert the user if that's not the case. - For non-trivial changes, also add unit tests.
- Consider also adding E2E tests using the framework. You can look at
test/e2e/features/cors/suite.goas an example for an E2E test. When writing an E2E test, prefer to usebase.NewBaseTestingSuiteas the base suite, as it provides many useful utilities. If you are adding a new test suite, remember to register it intest/e2e/tests/kgateway_tests.go. Additionally add it to one of the test kind clusters in.github/workflows/e2e.yaml.
- Avoid "util" packages - use descriptive names
- Lowercase filenames, underscores for Go files (
my_file.go), dashes for docs (my-doc.md) - Package names: Avoid separators, use nested dirs for multi-word names
- VSCode markers: Use
// MARK: Section Namefor long file navigation
# Initial setup
ctlptl create cluster kind --name kind-kind --registry=ctlptl-registry
# Build images and load into kind
VERSION=v1.0.0-ci1 CLUSTER_NAME=kind make kind-build-and-load # Builds all 3 images
# Deploy with Tilt (live reload enabled)
tilt up # Configure via tilt-settings.yaml
# as long as tilt is running, it will auto-reload on code changesSee Tiltfile and tilt-settings.yaml for configuration.
# Set up complete development environment
make run # kind + CRDs + MetalLB + images + charts
# Update after code change
make kind-reload-kgatewaymake conformance # Gateway API conformance
make all-conformance # All suites
# Run specific test by ShortName
make conformance-HTTPRouteSimpleSameNamespace- IR Equals() bugs: High-risk area. MUST compare all fields or mark
+noKrtEquals. - Proto comparison: Use
proto.Equal(), not==orreflect.DeepEqual - Codegen stamps:
make clean-stampsif regeneration seems stuck - E2E test resources: Never manually delete resources in specific order - let framework handle it
- PolicyIR translation: Translate as close to Envoy protos as possible in the Plugin IR, not in translation pass. The translation pass should be very light weight.
- KRT collections: Changes trigger minimal recomputation - dependencies tracked automatically
- Envoy image version: Defined in
MakefileasENVOY_IMAGE(update with care)
- Architecture:
/devel/architecture/overview.md - Contributing:
/devel/contributing/README.md - API conventions:
/api/README.md - Testing guide:
/devel/testing/writing-tests.md - Code generation:
/devel/contributing/code-generation.md - E2E framework:
/test/e2e/README.md - Plugin SDK:
/pkg/pluginsdk/types.go - Example plugin:
/pkg/kgateway/extensions2/plugins/trafficpolicy/
- Go version: Specified in
go.mod - Base image: Alpine 3.17.6 (distroless for production)
- Architectures: amd64, arm64 (controlled via
GOARCH) - Image registry:
ghcr.io/kgateway-dev(override viaIMAGE_REGISTRY) - Rust components: envoyinit includes dynamic filters built from
/internal/envoyinit/rustformations/
make help # Self-documenting targets
make analyze # Run golangci-lint (custom config)
make test # Run unit tests
make e2e-test # Run e2e tests
make generate-all # Smart codegen (uses stamps)
make verify # CI codegen check (always regenerates)
make fmt # Format all code
make fmt-changed # Format only changed files
make kind-create # Create kind cluster
make setup # Full local setup
make deploy-kgateway # Deploy to clustermake bump-gtw DEP_REF=v1.3.0 # Bump Gateway API
make generate-licenses # Update license attributionGateway API version is in go.mod and CRD install URL in Makefile (CONFORMANCE_VERSION).
- Ensure all linters pass:
make analyze,make verify - If you modified files in
.github/: Runmake lint-actionsto lint GitHub Actions workflows - Ensure tests pass in CI (unit + e2e + conformance)
- Use the PR template structure below
Every PR must include these sections:
-
Description - Explain motivation, what changed, and link issues (
Fixes #123) -
Change Type - Include one or more
/kindcommands in the PR body:/kind feature,/kind fix,/kind cleanup,/kind documentation/kind breaking_change,/kind deprecation,/kind design/kind bump,/kind flake,/kind install
-
Changelog - A fenced code block with
release-noteas the language identifier containing the release note text, orNONEif not user-facing -
Additional Notes (optional) - Extra context for reviewers
All code and comments should use American English spelling (i.e. "color" not "colour", "honor" not "honour").
When generating markdown documentation:
- Use ordered heading levels (no skipping from
#to###) - Headings should not end in punctuation
- Use consistent bullet types within a list (don't mix
-and*) - No empty headings
- Use
->instead of→for arrows (ASCII-compatible) - Prefer mermaid diagrams over ASCII art - use fenced mermaid code blocks for flowcharts, sequence diagrams, and architecture diagrams instead of box-drawing characters (┌ └ │ ├ ─ ► ▼ etc.)