This page covers what you need to know for managing Konnect resources using the kongctl declarative configuration approach. For supported resource types and field-level resource definitions, see the kongctl declarative resource reference.
Declarative configuration with kongctl
Overview
kongctl’s declarative management feature enables you to manage your Konnect resources with YAML declaration files and a state-free CLI tool.
Key principles
- Configuration manifests: Configuration is expressed as simple YAML files that describe the desired state of your Konnect resources. Configuration files can be split into multiple files and directories for modularity and reuse.
- Plan-based: Plans are objects that represent required changes to move a set of resources from their current state to a desired state. In kongctl, plan artifacts are first-class concepts that can be created, stored, reviewed, and applied. Plans are represented as JSON objects and can be generated and stored as files for later application. When running declarative commands, if plans are not provided they are generated implicitly and executed immediately.
- State-free: kongctl doesn’t use a state file or database to store the current state. The system queries Konnect directly to calculate plans and apply changes.
- Namespace resource isolation: Namespaces provide a way to isolate resources however the user desires (teams, environments, etc.).
Each resource under management is assigned to one namespace, and resources in other namespaces are not considered when calculating plans or
applying changes. A
defaultnamespace is used if none is specified in input configurations.
AI-assisted declarative setup
kongctl includes a kongctl-declarative skill for AI coding agents. The
skill helps an agent discover resource schemas with kongctl explain, generate
starter YAML with kongctl scaffold, bootstrap declarative files, integrate
decK through _deck, generate API configuration from OpenAPI documents, and
work through plan, diff, apply, sync, delete, and adopt workflows.
Install the bundled skills from the root of the repository where your agent will work:
kongctl install skillsPreview the files and symlinks before writing them:
kongctl install skills --dry-runAlways review agent-generated configuration before applying changes to
Konnect. Use kongctl diff --mode apply or kongctl plan
to preview proposed changes before running kongctl apply or kongctl sync.
See Use kongctl with AI agent skills for the complete skills overview.
Quick start
Prerequisites
- Konnect account: Sign up for free
- kongctl installed: See installation instructions
- Authenticated with Konnect: Run
kongctl login
Create your first configuration
Create a working directory:
mkdir kong-portal && cd kong-portalCreate a file named portal.yaml:
portals:
- ref: my-portal
name: "my-developer-portal"
display_name: "My Developer Portal"
description: "API documentation for developers"
authentication_enabled: false
default_api_visibility: "public"
default_page_visibility: "public"
apis:
- ref: users-api
name: "Users API"
description: "API for user management"
publications:
- ref: users-api-publication
portal_id: my-portalPreview changes:
kongctl diff --mode apply -f portal.yamlApply configuration:
kongctl apply -f portal.yamlVerify resources with kongctl get commands:
kongctl get portalskongctl get apisYour developer portal and API are now live! Visit the Konnect Console to see your developer portal with the published API.
Core concepts
Resource identity
Resources can have multiple identifiers:
- ref: kongctl declarative engine identifier.
refis used to identify the resource uniquely within a given set of declarative configurations.refisn’t written to the remote Konnect system and must be unique across all resources in a given set of input configuration files. This value is used to create inter-configuration references between resources. - id: Most Konnect resources have an
idfield which is a Konnect assigned UUID. This field isn’t stored in declarative configuration files but will be used internally by the declarative engine. - name: Many Konnect resources have a
namefield which may or may not be subject to a unique constraint within an organization for that resource type.
Top-level resource keys and field names in declarative YAML are stable
configuration contract names. Use the names documented in the
kongctl declarative resource reference, and use
ref values when one resource needs to refer to another.
application_auth_strategies:
- ref: oauth-strategy
name: "OAuth 2.0 Strategy"
portals:
- ref: developer-portal
name: "Developer Portal"
default_application_auth_strategy_id: !ref oauth-strategy#idPlan artifacts
Plans are central to how kongctl manages resource state. Plans are objects which define the required steps to move a set of resources from their current state to a desired state. Plans can be created, stored, reviewed, and applied at a later time and are stored as JSON files. Plans are not required to be used, but can enable advanced workflows.
How planning works
The declarative configuration commands
(apply, sync, delete, diff) use the planning engine internally:
Implicit Planning (direct execution):
# Internally generates plan and executes it
kongctl apply -f config.yamlExplicit Planning (two-phase execution):
# Phase 1: Generate plan artifact
kongctl plan --mode apply -f config.yaml --output-file plan.json# Phase 2: Execute plan artifact (can be done later)
kongctl apply --plan plan.jsonWhy use plan artifacts?
Plan artifacts enable more advanced workflows:
- Audit Trail: Store plans in version control alongside configurations
- Review Process: Share plans and review with team members before execution
- Deferred Execution: Generate plans in CI, apply them after approval
- Rollback Safety: Keep previously applied plans for rollback analysis
- Compliance: Document exactly what changes were planned
Configuration structure
Basic structure
# Optional defaults section
_defaults:
kongctl: # kongctl metadata defaults
namespace: production
protected: false
portals: # List of Parent portal resources
- ref: developer-portal # ref is required on all resources
name: "developer-portal"
display_name: "Developer Portal"
description: "API documentation hub"
kongctl: # kongctl metadata defined explicitly on resource, overrides _defaults
namespace: platform-team
protected: trueParent vs child resources
Generally the main concepts in the Konnect system are collections and many of them support child resources underneath them.
Parent resource examples:
apisportalsapplication_auth_strategiescontrol_planesanalytics.dashboardsorganization.teamsevent_gatewaysai_gateways
Child resource examples:
api.versionsapi.publicationsapi.implementationsapi.documentsportal.pagesportal.snippetsportal.customizationportal.custom_domainportal.email_configportal.email_templatesai_gateway.model_providersai_gateway.auth_strategiesai_gateway.policiesai_gateway.agentsai_gateway.consumersai_gateway.consumer_groupsai_gateway.modelsai_gateway.mcp_serversai_gateway.config_storesai_gateway.vaults
See the kongctl declarative resource reference for more details on supported resources.
Hierarchical vs flattened configuration
Parents are defined at the root of a configuration while children can be expressed both nested under their parent and at the root with a parent reference field.
Hierarchical configuration:
apis:
- ref: users-api
name: "Users API"
versions:
- ref: v1
version: "v1.0.0"
spec: !file ./specs/users-v1.yaml
publications:
- ref: public
portal_id: !ref main-portal
visibility: publicFlattened configuration:
apis:
- ref: users-api
name: "Users API"
api_versions:
- ref: v1
api: users-api
version: "v1.0.0"
spec: !file ./specs/users-v1.yaml
api_publications:
- ref: public
api: users-api
portal_id: !ref main-portalParent selectors and references
Fields such as portal on portal_pages and api on api_versions
select the parent declaration for a root-level child. They are kongctl
configuration fields. kongctl resolves the parent’s Konnect ID for API
requests, including route parameters. When a child is nested, its parent
is inferred and the selector can be omitted.
Fields such as portal_id on API publications are API relationship fields.
The flattened example uses api: users-api to select the owning API and
portal_id: !ref main-portal to reference the publication’s target portal.
A plain ref such as portal: portal-test identifies a declaration’s ref;
it is not a literal Konnect UUID.
portal: !ref portal-test and portal: !ref portal-test#id are also valid
parent selectors. !ref defaults to #id, but affected releases require
the following workaround.
Compatibility: In affected releases, including v1.21.0,
!refon root-level portal and API child parent selectors can silently omit children from plans. In sync mode, this can schedule declared live children for deletion. Use a plain parent ref, such asportal: docs-portal, or nest the child under its parent, until you use a release containing the fix for issue #2633. Continue using!reffor API relationship fields such asportal_id.
AI Gateway child parent selectors, such asai_gateway, support!refand don’t require this workaround.
For example, manage a root-level page under an existing external portal using a plain parent ref:
portals:
- ref: portal-test
_external:
selector:
matchFields:
name: "Docs Portal"
portal_pages:
- ref: home
portal: portal-test
slug: /
title: Home
visibility: public
status: published
content: "# Welcome"The external portal is not modified. Its declared page is managed normally.
Review kongctl diff --mode sync -f config.yaml before running sync and
confirm that declared pages are retained.
Configuration sources
Pass local files, directories, standard input, or HTTP and HTTPS URLs with
--filename or -f. Repeat the flag to combine sources. Use
--recursive to discover YAML files below a directory.
kongctl plan \
-f ./shared.yaml \
-f ./environments/production \
-f https://config.example.com/portals.yamlRelative !file paths are resolved from the source that contains the tag.
For remote sources, kongctl downloads referenced relative files into the
remote-file save directory. Use --remote-file-save-dir to select that
directory and --force to replace existing downloads.
Remote authentication defaults to automatic behavior. kongctl sends the
active Konnect token only to HTTPS
Konnect API hosts. It doesn’t send the token to arbitrary
hosts. Use --remote-auth none when the source must be fetched without
authentication.
kongctl metadata
The kongctl section provides metadata for resource management. This metadata is stored in Konnect labels and labels are only provided on parent resources. Thus, kongctl metadata is only supported on parent resources.
Protected resources
The protected field prevents accidental deletion of critical resources:
portals:
- ref: production-portal
name: "Production Portal"
kongctl:
protected: true # Cannot be deleted until protection is removedNamespace management
The namespace field enables resource isolation:
apis:
- ref: billing-api
name: "Billing API"
kongctl:
namespace: finance-team # Owned by finance team
protected: falseFile-level defaults
Use _defaults to set default values for all resources in a file:
_defaults:
kongctl:
namespace: platform-team
protected: true
portals:
- ref: api-portal
name: "API Portal"
# Inherits namespace: platform-team and protected: true
- ref: test-portal
name: "Test Portal"
kongctl:
namespace: qa-team
protected: false
# Overrides both defaultsNamespace and protected field behavior
kongctl provides some default behavior depending on how metadata fields are specified or omitted. The following tables summarize the behavior.
Namespace field behavior
| File Default | Resource Value | Final Result | Notes |
|---|---|---|---|
| Not set | Not set | “default” | System default |
| Not set | “team-a” | “team-a” | Resource explicit |
| Not set | ”” (empty) | ERROR | Empty namespace not allowed |
| “team-b” | Not set | “team-b” | Inherits default |
| “team-b” | “team-a” | “team-a” | Resource overrides |
| “team-b” | ”” (empty) | ERROR | Empty namespace not allowed |
| ”” (empty) | Any value | ERROR | Empty default not allowed |
Protected field behavior
| File Default | Resource Value | Final Result | Notes |
|---|---|---|---|
| Not set | Not set | false | System default |
| Not set | true | true | Resource explicit |
| Not set | false | false | Explicit false |
| true | Not set | true | Inherits default |
| true | false | false | Resource overrides |
| false | true | true | Resource overrides |
Child resources automatically inherit the metadata of their parent resource.
Namespace enforcement flags
The kongctl plan command provides built-in namespace guardrails:
--require-any-namespaceforces every managed resource to declare a namespace viakongctl.namespaceor_defaults.kongctl.namespace.--require-namespace=<ns>restricts planning to the provided namespaces (repeat or comma-separate the flag to allow multiple values).
These flags help prevent accidentally operating on unexpected namespaces, especially when running in sync mode.
External resources and namespaces
External resources are Konnect objects managed elsewhere
but selected by kongctl for use by managed resources. Use _external when
the object needs a reusable declarative ref or managed children. Use
!lookup to resolve an existing object directly in a relationship field.
!external is an exact alias for !lookup.
# External portal definition - this tells kongctl that this portal
# is managed externally (by the platform team) but we need to reference it
portals:
- ref: shared-developer-portal
_external:
selector:
matchFields:
name: "Shared Developer Portal"Catalog APIs and application Auth Strategies can also be external. An external API can own managed versions, publications, implementations, and documents:
apis:
- ref: shared-api
_external:
selector:
matchFields:
name: Shared API
versions:
- ref: shared-api-v2
version: v2
spec: !file ./openapi.yamlBecause kongctl doesn’t own external resources:
- External resources can’t declare
kongctlmetadata. File-level defaults are ignored for them. - External references don’t add namespaces to sync planning.
- kongctl never changes or deletes the external parent.
- Child collections explicitly included in sync scope are fully reconciled, including stale child deletion. Omitted child collections remain untouched.
Inline lookups use a field:value scalar or a mapping. The target resource is
inferred from the relationship field:
ai_gateway_model_providers:
- ref: shared-provider
ai_gateway: !lookup {name: shared-ai-gateway}
name: openai
type: openai
display_name: OpenAI
config: {}A mapping can contain multiple selectors, all of which must match. An
id:<uuid> selector binds a known ID directly and can’t be combined with
another selector. Other selectors must match exactly one resource.
Lookups run during planning and are cached for that plan. Saved plans contain the resolved IDs instead of tag placeholders.
Resources managed by decK
decK integration is configured on control planes via the _deck pseudo-resource. kongctl runs decK once per
control plane that declares _deck, then resolves external gateway services by selector name. _external.requires.deck
isn’t supported.
control_planes:
- ref: prod-cp
name: "prod-cp"
_deck:
files:
- "kong.yaml"
flags:
- "--select-tag=kongctl"
gateway_services:
- ref: billing-gw
_external:
selector:
matchFields:
name: "billing-service"Important notes for decK integration:
_deckis allowed only on control planes and only one_deckconfig is allowed per control plane._deck.filesmust include at least one state file._deck.flagscan include additional decK flags (but not Konnect auth or output flags)._external.selector.matchFields.nameis required for external gateway services and must be the only selector field.- kongctl runs exactly one
deck gateway applyordeck gateway syncper control plane that declares_deck. - decK state files should include
_info.select_tagsand matchingtagson entities sosyncdoesn’t delete resources owned by other decK files. kongctl doesn’t inject select tags for you. - Relative decK file paths are resolved relative to the declarative config file and must remain within the
--base-dirboundary (default: the config file directory). - Plan files store decK base directories relative to the plan file location. When emitting a plan to stdout,
the base directory is made relative to the current working directory (use
--output-filefor portable plans). Applying a plan resolves them from the plan file directory (or the current working directory when using--plan -). kongctl plan/diffrunsdeck gateway diffto decide whether an external tool change is needed.kongctl applyrunsdeck gateway applyandkongctl syncrunsdeck gateway sync. For apply mode, deletes reported by decK diff are ignored.- If the control plane is being created in the same plan (or the ID isn’t available), kongctl skips decK diff and includes the external tool step.
- For gateway steps, kongctl injects Konnect auth flags and output flags (
--json-output --no-color); do not supply--konnect-token,--konnect-control-plane-name,--konnect-addr, or output flags yourself. - Plans represent decK resolution targets explicitly via
post_resolution_targetson the_deckchange entry, including control plane identifiers and the gateway service selector.
For more information, see kongctl and decK.
Manage AI Gateway declaratively
kongctl manages AI Gateway resources in Kong Konnect. The supported declarative model includes:
- AI Gateway instances and data-plane certificates
- Model Providers and Models
- Auth Strategies and Policies
- Agents and MCP Servers
- Consumers, Consumer Credentials, and Consumer Groups
- Config Stores, Config Store Secrets, and Vaults
AI Gateway nodes are imperative, read-only resources. Inspect them with
kongctl get ai-gateway nodes; don’t include them in declarative
configuration.
Declare children under their gateway when one file owns the complete hierarchy:
ai_gateways:
- ref: shared-ai-gateway
name: shared-ai-gateway
display_name: Shared AI Gateway
deployment_type: hybrid
model_providers:
- ref: openai
name: openai
display_name: OpenAI
type: openai
config: {}
models:
- ref: gpt-4o
name: gpt-4o
display_name: GPT-4o
type: model
formats:
- type: openai
targets:
- name: gpt-4o
provider: openai
config:
type: openaiRoot-level child declarations use a parent field such as ai_gateway,
ai_gateway_consumer, or ai_gateway_config_store. The value can be a
ref, !ref, or supported !lookup:
ai_gateway_policies:
- ref: shared-rate-limit
ai_gateway: !lookup {name: shared-ai-gateway}
name: shared-rate-limit
display_name: Shared rate limit
type: rate-limiting-advanced
enabled: true
config: {}Child collections follow the normal sync-scope rules. Omit a collection to leave it unmanaged, or provide an empty collection under an identified parent to delete its managed children. Root-level empty AI Gateway child collections are rejected because they don’t identify a parent.
Policy display_name values must be explicit. Model targets identify their
Model Provider by API name. Agents, Models, and MCP Servers reference Auth
Strategies through access.auth_strategies and can reference same-gateway
Policies with !ref.
Use --include-child-resources when dumping AI Gateway. Direct
AI Gateway
child selectors are not supported:
kongctl dump declarative \
--resources ai_gateways \
--include-child-resourcesFor the complete resource model, see kongctl declarative resource reference.
Configuration templates
Define reusable configuration blocks under the top-level _templates key.
Select one from a resource or nested configuration block with _extends:
_templates:
private-portal:
authentication_enabled: true
default_api_visibility: private
labels:
managed-by: kongctl
portals:
- _extends: private-portal
ref: developer-portal
name: Developer Portal
labels:
team: platformAll files supplied to one command share one template registry. Template names must be unique, and a template can extend another template. Each consuming block can extend exactly one template. Unknown names and inheritance cycles are errors.
Consumer values take precedence:
- Objects merge recursively.
- Scalars, arrays, values of a different type, and explicit
nullreplace inherited values. - Arrays never append or merge by element.
- Omitted keys retain the inherited value.
Templates expand before schema validation and sync-scope capture. An inherited
empty child collection has the same deletion behavior as one written directly.
Tags in a template use the template definition file’s context. _templates
is shared across sources, while _defaults remains automatic and file-local.
YAML tags
YAML tags are like preprocessors for YAML file data. They allow you to load content from external files, reference across resources, load values from environment variables, and extract specific values from structured data. Over time more tags may be added to support various functions and use cases.
Relationship tags include:
!ref: Reference a resource declared in the same configuration.!lookup: Resolve one existing remote resource during planning.!external: An exact alias for!lookup.
kongctl explain <resource>.<field> reports the supported tags, target
resource type, selectors, and required scope for a field.
Compose YAML tags
A lookup mapping can use !env directly as a selector value:
portal_id: !lookup
name: !env PORTAL_NAME!secret can use !env as its source or as an item in parts.
Other nested combinations, including !file or !ref inside a lookup, are
rejected. Use mapping syntax when nesting a tag; the scalar
field:value lookup form can’t contain another tag.
kongctl resolves a nested lookup’s environment value during planning. Saved plans retain only the resolved resource ID and don’t repeat the lookup during execution.
Loading file content to YAML fields
Load the entire content of a file as a string:
apis:
- ref: users-api
name: "Users API"
description: !file ./docs/api-description.mdAll file paths are resolved relative to the directory containing the configuration file:
project/
├── config.yaml # Main config file
├── specs/
│ ├── users-api.yaml
│ └── products-api.yaml
└── docs/
└── descriptions.txtIn config.yaml:
apis:
- ref: users-api
name: !file ./specs/users-api.yaml#info.title
description: !file ./docs/descriptions.txtSupported file types: Any text file (.txt, .md, .yaml, .json, etc.)
Security features
Path Traversal Prevention: Absolute paths are blocked. Relative paths may include
.., but the resolved path must stay within the base directory boundary. By default,
the boundary is the root of each -f source (file: its parent dir, dir: the directory itself).
For stdin, the boundary defaults to the current working directory. Set the base directory with
--base-dir or konnect.declarative.base-dir (KONGCTL_<PROFILE>_KONNECT_DECLARATIVE_BASE_DIR,
for example KONGCTL_DEFAULT_KONNECT_DECLARATIVE_BASE_DIR).
# These will fail with security errors
description: !file /etc/passwd
# This will fail if it resolves outside the base directory
config: !file ../../../sensitive/file.yaml
# These are allowed (if they stay within the base directory)
description: !file ../docs/description.txt
config: !file ./config/settings.yamlFile Size Limits: Files are limited to 10MB.
Performance features
File Caching: Files are cached during a single execution to improve performance:
apis:
- ref: api-1
name: !file ./common.yaml#api.name # File loaded and cached
description: !file ./common.yaml#api.desc # Uses cached version
- ref: api-2
team: !file ./common.yaml#team.name # Uses cached versionValue extraction
You can extract specific values from structured data loaded from the file tag
with this hash (#) notation:
apis:
- ref: users-api
name: !file ./specs/openapi.yaml#info.title # loads info.title field from the openapi.yaml file
description: !file ./specs/openapi.yaml#info.description
version: !file ./specs/openapi.yaml#info.version
versions:
- ref: v1
spec: !file ./specs/openapi.yamlAlternatively, values can be extracted using this map format:
apis:
- ref: products-api
name: !file
path: ./specs/products.yaml
extract: info.title
labels:
contact: !file
path: ./specs/products.yaml
extract: info.contact.emailLoading values from environment variables
Use !env to load a value from an environment variable into a string
field:
portals:
- ref: env-portal
name: env-portal
description: !env PORTAL_DESCRIPTIONScalar syntax supports extraction with #:
api_documents:
- ref: env-doc
api_id: petstore-api
title: !env DOC_METADATA#title
content: !env DOC_METADATA#content
slug: getting-startedMap syntax is also supported:
api_documents:
- ref: env-doc
api_id: petstore-api
title: !env
var: DOC_METADATA
extract: title
content: !env
var: DOC_METADATA
extract: content
slug: getting-started!env extraction parses the environment variable as YAML or JSON before
reading the requested field path.
A runnable example is available in docs/examples/declarative/env/.
!env behavior
- Deferred
!envsupports string-typed fields only. - Unset environment variables are treated as errors.
- Empty-but-set environment variables are allowed except for namespaces.
- During planning, kongctl resolves the current environment value to calculate changes.
- Saved plan files preserve the deferred
!envreference instead of the resolved plaintext value. - Namespace
!envvalues resolve during loading and remain fixed in saved plans as ownership metadata, including whenstore: falseis used. - During execution, kongctl performs a fresh environment lookup for each
deferred
!envvalue instead of reusing the value observed during planning. - When you run
apply,sync, ordeletedirectly from configuration files, kongctl still plans first and then performs that second lookup during execution in the same command invocation. - In direct
apply,sync, anddeleteruns, both lookups happen within the same kongctl process, so they will usually observe the same process environment. - When execution uses a saved plan with
--plan, planning and execution happen in separate command invocations, so environment values may differ between them and the executed value may differ from what was observed while planning. - Human-readable plan and diff output redact deferred
!envvalues.
Storing typed environment values in plans
Use store: true when a saved plan should retain the environment value approved during planning.
Values are read during configuration loading, before decoding resource fields.
Execution uses the stored values even when the environment has changed or the variables are absent.
Stored values are plaintext in plan artifacts and can appear in plan and diff output. Use this for non-sensitive configuration. No separate secret store is involved.
store is a boolean and defaults to false.
These are alternative declarations of the same field:
description: !env {var: DESCRIPTION, store: true}description: !env
var: DESCRIPTION
store: trueStored values support structured extraction using the mapping form:
description: !env
var: METADATA
extract: description
store: true
type: stringThe loader infers the type from an unambiguous resource/SDK field, including typed map values and array elements.
Unknown destinations such as arbitrary policy configuration, and conflicting union branches, require an explicit type.
Error messages name the field and the variable.
Custom decoding layouts that cannot be inferred also require type.
Inference does not duplicate policy schemas or select an arbitrary union branch.
|
Type |
Conversion |
|---|---|
string
|
Preserve raw environment text, including whitespace and quotes |
boolean
|
Parse a YAML boolean (true or false, also title/upper case)
|
integer
|
Parse a whole number. Fractional values are errors |
number
|
Parse a finite decimal number, including exponent notation |
null
|
Parse a YAML null value |
array
|
Parse a YAML/JSON sequence with typed nested values |
object
|
Parse a YAML/JSON mapping with string keys and typed values |
Quote the null type name in type: "null".
An unquoted YAML null is not a string option.
An explicit type must match a known destination.
The type integer is also accepted for a numeric destination, and null for nullable destinations.
The normal SDK and resource validation still applies.
For a string destination, an environment value of 42, true, or null remains text.
For an unknown destination, specify type: string to preserve that text.
Inside parsed objects and arrays, quote strings that resemble numbers, booleans, or null.
For example, an AI Gateway rate-limiting policy’s dynamic config can use:
config:
redis:
ssl: !env {store: true, var: REDIS_SSL, type: boolean}
ssl_verify: !env {var: REDIS_SSL_VERIFY, store: true, type: boolean}
sync_rate: !env {store: true, var: SYNC_RATE, type: number}Set REDIS_SSL=true, REDIS_SSL_VERIFY=false, and SYNC_RATE=0.5.
A known boolean field, such as a portal’s auto_approve_developers, can instead use !env {var: AUTO_APPROVE, store: true} without a type option.
Extraction parses the source as YAML/JSON before selecting an existing dot-separated mapping path. The selected value may be any supported type and must match the inferred or explicit type. Extraction does not convert booleans, numbers, null, arrays, or objects to strings. Array-index extraction is not supported. Missing paths and traversal through scalar/null values fail.
Parsing and serialization boundaries
- Unset variables always fail.
Empty text is valid only for a direct
string. Structured parsing and extraction reject empty/whitespace-only input. YAML null spellings includenull,Null,NULL, and~. - Structured input must be one YAML/JSON document.
YAML 1.2 boolean semantics apply:
yes,no,on, andoffare strings. - Numbers use JSON decimal syntax. Hexadecimal, octal spellings with leading zeros, underscores, leading plus signs, NaN, and infinity are rejected.
- Numbers are limited to ±9,007,199,254,740,991 (±(2^53−1)). Integers within this range remain exact after JSON plan round trips. Fractional numbers use binary64 rounding. Underflow to zero or loss of an entire fractional part is rejected. Normal SDK decoding enforces narrower integer destinations.
- Objects require unique string keys. Anchors, aliases, merge keys, custom tags, timestamps, binary values, and other non-JSON YAML types are rejected. Quote dates to represent them as strings.
- Nested arrays, objects, and null retain their JSON meanings. Resource rules still determine whether a field accepts null or an empty collection.
- Strings beginning with reserved internal prefixes
__ENV__:,__SECRET__:,__REF__:, or__EXTERNAL__:are rejected, including inside structured values, so execution cannot reinterpret stored data as deferred references.
Compatibility and saved plans
Ordinary !env VAR and !env {var: VAR, store: false} retain their string-only, deferred behavior: read during planning and again at execution.
Namespace values are the exception: they resolve during loading and remain
fixed in saved plans, including when store: false is used.
Existing saved plans and deferred-value redaction are unchanged.
type is only valid with stored values.
Unknown options are errors.
Stored tags cannot be nested inside !secret, !lookup, or !external, and cannot contain other custom tags.
Existing deferred nesting remains supported.
Stored tags cannot supply resource ref.
Namespace values resolve during loading with either stored or deferred syntax.
Write-only fields continue to require their existing deferred secret sources or supported vault references.
The !secret tag is unchanged.
For a manifest that contains description: !env {var: DESCRIPTION, store: true}:
DESCRIPTION="Approved description" kongctl plan --mode apply -f config.yaml --output-file plan.jsonApply the saved plan after changing the environment value:
DESCRIPTION="Changed description" kongctl apply --plan plan.json --auto-approveAlternatively, apply the saved plan without the original variable:
env -u DESCRIPTION kongctl apply --plan plan.json --auto-approveThe two apply commands are alternatives.
Both use Approved description from the plan.
Re-plan from the manifest to approve a different value.
Write-only secret fields
Some Konnect APIs accept secret values but don’t return them
from get or list. Common examples include:
- Portal identity provider
config.client_secret - DCR provider secrets such as
dcr_token,api_key, andinitial_client_secret - AI Gateway Model Provider authentication values
- AI Gateway Auth Strategy OpenID Connect
config.client_secret - AI Gateway Vault authentication credentials
- AI Gateway Consumer Credential
api_key - Event Gateway schema registry authentication
password
Secret material in a write-only field must use !secret. Literal secret
values and eager !file values are rejected because they could enter a saved
plan:
client_secret: !secret
source: !env PORTAL_OIDC_CLIENT_SECRETCompose public text with deferred secret sources by using parts:
value: !secret
parts:
- "Bearer "
- !env AI_PROVIDER_TOKENDon’t place secret material in literal parts. Saved plans retain source metadata, such as environment variable names, but never resolved values. Planning doesn’t require the secret environment variables. Execution validates every source before making the first API change.
Declaring a secret source and authorizing a write are separate operations. Creates send each configured secret once. To rotate an existing write-only field, select it while generating the plan:
kongctl plan -f config.yaml \
--write-secret workforce-idp#config.client_secret \
--output-file rotation.jsonSelect every configured secret on one resource by omitting the field:
kongctl plan -f config.yaml \
--write-secret workforce-idp \
--output-file rotation.jsonSelect every eligible secret in the configuration:
kongctl plan -f config.yaml \
--write-secrets \
--output-file rotation.jsonExact --write-secret selectors fail if the requested field isn’t writable.
--write-secrets is best effort and reports ineligible fields as warnings.
A saved plan already contains its write intents, so write-selection flags can’t
be combined with --plan. Delete mode doesn’t accept secret selection.
The planner can’t compare a write-only field with its remote value. Without a
write selector, it omits the field and remains idempotent. Human-readable plans
report write requested without displaying the value.
AI Gateway Config Store Secrets are managed children. New secrets require a
value: !secret; an existing secret can omit value to retain its current
value. Use --write-secret or --write-secrets to rotate it. Imperative
get and list operations return safe metadata and never reveal values.
AI Gateway Consumer Credential api_key is create-only. Omit it to let
Konnect generate a key, or provide it with !secret
while creating the credential. Rotate it by creating a replacement credential
and deliberately removing the old one.
Sync scope and deletion safety
sync reconciles only collections whose YAML keys are present:
- An omitted root or child collection is ignored.
- A populated collection creates or updates the declared resources and removes other managed resources in that collection and namespace.
- An empty root list, such as
apis: [], requests deletion of managed resources in the selected namespace. - Parent and child collections are independent. Omitting
pagesleaves Portal Pages untouched;pages: []requests that the identified Portal have no managed Pages. - Map-shaped children use
{}as the empty collection. For example,email_templates: {}requests no customized email templates. - Optional, delete-capable singletons such as
custom_domain,email_config, andaudit_log_webhookuse{}to request deletion. Omission ignores the singleton andnullis invalid. - Update-only singletons such as
customizationcan’t be deleted with{}. - Empty child collections must be nested under an identified parent. A
root-level
api_documents: []is invalid.
For federated ownership, declare the managed or _external parent and include
only the child collection owned by that configuration:
portals:
- ref: shared-docs-portal
_external:
selector:
matchFields:
name: Shared Docs Portal
pages: []The external parent is never changed or deleted, but its explicitly scoped Pages are fully reconciled. Namespace defaults don’t apply to the external parent; managed collections in the same input still use their namespaces.
Preview every destructive sync before approving it:
kongctl sync -f config.yaml --dry-runFor more examples, see Synchronize configurations with kongctl.
Commands reference
kongctl includes many commands for declarative configuration management. Start with the following commands for most use cases:
|
Command |
Description |
When to use |
|---|---|---|
adopt
|
Adds a namespace label to an existing Konnect resource that was created outside of kongctl, bringing it under declarative management without modifying any other fields. |
Use before your first dump or plan, when you need to bring a manually created or UI-created resource into your configuration.
|
dump
|
Exports the current state of Konnect resources to a declarative YAML configuration file. | Use when bootstrapping a new declarative configuration from existing live resources, or when generating a starting point for a new configuration file. |
plan
|
Compares your local configuration files against live Konnect state and generates a JSON plan artifact describing the changes to be made. | Use before applying changes, especially in CI/CD pipelines, to produce a reviewable and reusable plan artifact. |
diff
|
Displays a human-readable preview of the changes between the current live state and the desired state in your configuration files, or from a saved plan artifact. |
Use during development to inspect what apply or sync would change before committing.
|
apply
|
Creates and updates resources to match the desired state. Does not delete resources. |
Use to incrementally apply configuration without risk of deleting anything. Use sync instead when you want deletes as well.
|
sync
|
Applies the full desired state from your configuration files. Creates, updates, and deletes resources. |
Use for full reconciliation between your configuration and live state, including deletions. Use apply if you only want creates and updates.
|
delete
|
Plans and executes deletion of all resources defined in the input configuration files. | Use for tearing down a known set of resources, such as resetting a test environment. Not a typical step in the day-to-day declarative workflow. |
get
|
Retrieves Konnect resources. | Use to inspect live state after applying configuration, or to look up resource IDs and names. |
Use the execution command that matches a saved plan’s mode:
kongctl apply --planaccepts apply-mode plans.kongctl sync --planaccepts sync-mode plans.kongctl delete --planaccepts delete-mode plans.kongctl diff --plancan inspect a plan from any mode.
Use adopt --overwrite-namespace only when you intend to transfer a resource
that already has a kongctl namespace label:
kongctl adopt api billing-api \
--namespace platform \
--overwrite-namespaceUse --skip-defaults to omit literal API defaults from a declarative dump.
Use --include-child-resources to include children of the selected parent
types:
kongctl dump declarative \
--resources portal,api,ai_gateways \
--include-child-resources \
--skip-defaults > konnect.yaml--skip-defaults preserves explicit null and non-default values. It
doesn’t change planning, apply, sync, or Terraform import output.
See the CLI help at kongctl --help for all possible commands, or check out the kongctl CLI reference documentation.
CI/CD integration
Key principles for CI/CD integration:
- Plan on PR: Generate and review plans in pull requests
- Apply on Merge: Apply reviewed plans when merged to target branch
- Environment Separation: Different configs for dev/staging/prod
- Approval Gates: Require human approval for production
Best practices
Multi-team setup
Each team manages their own namespace:
# team-alpha/config.yaml
_defaults:
kongctl:
namespace: team-alpha
apis:
- ref: frontend-api
name: "Frontend API"
# Automatically in team-alpha namespaceEnvironment management
Use distinct namespaces for environments that are synchronized independently,
optionally scoped by both team and environment, such as payments-dev and
payments-prod. Namespaces define kongctl ownership and reconciliation
boundaries. A profile selects credentials and configuration; it does not by
itself isolate ownership or sync deletion scope.
Reuse one manifest to target separate control planes and AI Gateway instances:
# environments.yaml
_defaults:
kongctl:
namespace: !env ENV_NS
control_planes:
- ref: gateway
name: !env CONTROL_PLANE_NAME
ai_gateways:
- ref: ai-gateway
name: !env AI_GATEWAY_NAME
display_name: !env AI_GATEWAY_NAMEexport ENV_NS=payments-dev
export CONTROL_PLANE_NAME=payments-dev
export AI_GATEWAY_NAME=payments-ai-dev
kongctl plan -f environments.yaml --profile dev --mode sync \
--require-namespace="$ENV_NS" --output-file dev-plan.json
kongctl sync --plan dev-plan.json --profile dev
export ENV_NS=payments-prod
export CONTROL_PLANE_NAME=payments-prod
export AI_GATEWAY_NAME=payments-ai-prod
kongctl plan -f environments.yaml --profile prod --mode sync \
--require-namespace="$ENV_NS" --output-file prod-plan.json
kongctl sync --plan prod-plan.json --profile prod--require-namespace checks the resolved namespace against the expected
value. Use an independently configured expected value in CI to catch an
incorrect ENV_NS. --require-any-namespace requires an explicit resource
namespace or file default. Defaults-only files also select sync namespaces;
external-only file defaults satisfy guardrails without adding deletion scope.
Depending on your goals, you can create the plan in apply or sync mode:
- Use
--mode applyto create and update without pruning when the supplied resources are only part of the environment’s desired state. - Use
--mode syncwhen the input represents the complete desired set for the selected collections and namespaces.
A plan created in a particular mode can only be used with that command. --mode sync creates a plan for kongctl sync; --mode apply creates a plan for kongctl apply.
Do not share a team namespace across independent environment sync runs: omitted sibling resources can become deletion candidates. A namespace does not provide API authorization or protect against a parent gateway’s cascade deletion.
Both _defaults.kongctl.namespace and explicit kongctl.namespace accept
!env ENV_NS and mapping forms such as !env {var: ENV_NS} or
!env {var: ENV_SETTINGS, extract: namespace}. Explicit resource namespaces
override file defaults. Unset, empty, or invalid namespaces fail before
planning API operations; existing namespace naming rules still apply.
Namespace values are ownership metadata, not secrets. They resolve during
loading and remain fixed as plaintext in saved plans, even if ENV_NS
changes before apply --plan. Ordinary deferred !env fields, including
resource names, are read again during execution and can differ from the
reviewed values. Keep those variables consistent between planning and
execution, or use store: true for supported fields when the approved value
must be fixed. A namespace using store: false still resolves at load time.
AI Gateway children have resource-specific ownership rules. You can see all independently owned families with kongctl list ai-gateway --help.
Their namespaces are independent of the
parent gateway and inherit file defaults rather than its explicit namespace.
Consumer credentials inherit consumer ownership. Runtime certificate
federation remains deferred; config stores, their secrets, and data plane
certificates remain excluded. See AI Gateway ownership tracking
for the current support and limitations.
Security best practices
- Protect production resources:
```yaml
apis:
- ref: payment-api kongctl: namespace: production protected: true ```
- Use namespaces for isolation:
- One namespace per team
- Separate namespaces for environments
- Clear namespace ownership documentation
- Version control everything:
- Configuration files
- OpenAPI specifications
- Documentation
- Review plans before applying:
- Use
planin production - Save plans for audit trail
- Implement approval workflows
- Use
Plan artifact workflows
Basic plan review workflow
Developer creates plan:
kongctl plan -f config.yaml --output-file proposed-changes.jsonReview changes visually:
kongctl diff --plan proposed-changes.jsonShare plan for review (commit to git, attach to PR, etc.):
git add proposed-changes.json
git commit -m "Plan for adding new API endpoints"After approval, apply the plan:
kongctl apply --plan proposed-changes.jsonProduction deployment with approval
# CI/CD Pipeline Stage 1: Plan Generation
kongctl plan -f production-config.yaml \
--output-file plan-$(date +%Y%m%d-%H%M%S).json
# Stage 2: Manual approval gate
# - Plan artifact is stored as build artifact
# - Team reviews plan details
# - Approval triggers next stage
# Stage 3: Plan Execution
kongctl sync --plan plan-20240115-142530.json --auto-approveEmergency rollback using previous plan
List recent plans (assuming you store them):
ls -la plans/Review what the previous state included:
kongctl diff --plan plans/last-known-good.jsonRevert to previous state:
kongctl sync --plan plans/last-known-good.json --auto-approveCommon mistakes to avoid
Setting kongctl on child resources:
# WRONG
apis:
- ref: my-api
kongctl:
namespace: team-a
versions:
- ref: v1
kongctl: # ERROR - not supported on child resources
protected: trueCorrect approach:
# RIGHT
apis:
- ref: my-api
kongctl:
namespace: team-a
protected: true
versions:
- ref: v1Using name as identifier:
# WRONG - using display name
api_publications:
- ref: pub1
api: "Users API"Use ref for references:
# RIGHT - using ref
api_publications:
- ref: pub1
api: users-apiField validation
kongctl uses strict YAML validation to catch configuration errors early:
# This will cause an error
portals:
- ref: my-portal
name: "My Portal"
lables: # ERROR: Unknown field 'lables'. Did you mean 'labels'?
team: platformCommon field name errors:
lables→labelsdescriptin→descriptiondisplayname→display_namestrategytype→strategy_type
Troubleshooting and debugging
kongctl provides a global --log-level flag that you can pass with any command.
See the troubleshooting reference for help resolving common issues. See the debugging reference for more information on the debugging workflow.