Skip to content

Latest commit

 

History

History
1764 lines (1379 loc) · 56.5 KB

File metadata and controls

1764 lines (1379 loc) · 56.5 KB

Declarative Configuration

This page covers what you need to know for managing Kong Konnect resources using the kongctl declarative configuration approach. For supported resource types and field-level resource definitions see the Declarative Resource Reference.

Table of Contents

Overview

kongctl's declarative management feature enables you to manage your Kong Konnect resources with simple YAML declaration files and a simple state free CLI tool.

Key Principles

  1. 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.
  2. Plan-Based: Plans are objects that represent required changes to move a set of resources from one state to another, desired, state. In kongctl, plan artifacts are first class concepts that can be created, stored, reviewed, and executed. Plans are represented as JSON objects and can be generated and stored as files for later execution. When running declarative commands, if plans are not provided they are generated implicitly and executed immediately.
  3. State-Free: kongctl does not use a state file or database to store the current state. The system relies on querying of the online Konnect state in order to calculate plans and apply changes.
  4. 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 default namespace 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 skills

Preview the files and symlinks before writing them:

kongctl install skills --dry-run

Agent-generated configuration should still be reviewed before it changes Konnect. Use kongctl diff --mode apply or kongctl plan to preview proposed changes before running kongctl apply or kongctl sync.

Quick Start

Prerequisites

  1. Kong Konnect Account: Sign up for free
  2. kongctl installed: See installation instructions
  3. Authenticated with Konnect: Run kongctl login

Create Your First Configuration

Create a working directory:

mkdir kong-portal && cd kong-portal

Create 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-portal

Preview changes:

kongctl diff -f portal.yaml

Apply configuration:

kongctl apply -f portal.yaml

You can also load a single configuration file from an HTTP or HTTPS URL:

kongctl apply -f https://get.konghq.com/example-kongctl.yaml

To save remote files locally and run the command from the saved copies in one operation, use --remote-file-save-dir or its shorthand, -s:

kongctl apply \
  -f https://get.konghq.com/portal.yaml \
  -f https://get.konghq.com/api.yaml \
  --remote-file-save-dir ./kongctl-example

Remote files are saved into the directory using the filename from each URL path. If multiple remote URLs would save to the same filename, the command fails before fetching them. Existing files are left intact by default. Use --remote-file-save-force or -F with --remote-file-save-dir to replace existing saved files.

When --remote-file-auth=auto is enabled, which is the default, kongctl sends the current profile's Konnect bearer token only to HTTPS remote sources on Konnect hosts, such as *.cloud.konghq.com or the configured Konnect API host. Authentication is never sent to arbitrary hosts. Use --remote-file-auth=none to fetch a remote file without adding Konnect authentication headers.

Review remote configuration before running mutating commands in production. Prefer HTTPS URLs and pin examples to immutable versions when using them in CI.

Verify resources with kongctl get commands:

kongctl get portals
kongctl get apis

Your 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. ref is used to identify the resource uniquely within a given set of declarative configurations. ref is not 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 to create inter-configuration references between resources.
  • id: Most Konnect resources have an id field which is a Konnect assigned UUID. This field is not stored in declarative configuration files but will be used internally by the declarative engine.
  • name: Many Konnect resources have a name field 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 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#id

Plan 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 executed 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) commands use the planning engine internally:

Implicit Planning (direct execution):

# Internally generates plan and executes it
kongctl apply -f config.yaml

Explicit 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.json

Saved plans have strict execution ownership. apply --plan, sync --plan, and delete --plan accept only plans generated in apply, sync, and delete mode, respectively. diff --plan can inspect a plan generated in any mode.

Why 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

Parent 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:

  • apis
  • portals
  • application_auth_strategies
  • control_planes
  • analytics.dashboards
  • organization.teams
  • event_gateways

Child Resource Examples:

  • api.versions
  • api.publications
  • api.implementations
  • api.documents
  • portal.pages
  • portal.snippets
  • portal.customization
  • portal.custom_domain
  • portal.email_config
  • portal.email_templates

See the Declarative Resource Reference for more details on supported resources.

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: true

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: public

Flattened 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-portal

Kongctl Metadata

The kongctl section provides metadata for resource management. This metadata is stored in Kong 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 removed

Namespace Management

The namespace field enables resource isolation:

apis:
  - ref: billing-api
    name: "Billing API"
    kongctl:
      namespace: finance-team  # Owned by finance team
      protected: false

A namespace must contain 1–63 lowercase alphanumeric characters or hyphens and must start and end with an alphanumeric character. Consecutive hyphens are permitted and are preserved exactly.

File-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 defaults

Namespace 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-namespace forces every managed resource to declare a namespace via kongctl.namespace or _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 the kongctl declarative engine for use by managed resources. Use _external when the object needs a reusable declarative ref or managed children. Use the !lookup tag 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 declared external. An external API may contain 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.yaml

The API itself remains outside kongctl management. The declared version is managed beneath the resolved API ID.

Because kongctl does not own those resources:

  • External resources cannot declare kongctl metadata. Supplying kongctl.namespace or kongctl.protected on an external resource results in a parsing error. File-level defaults are ignored for externals.
  • External references do not add their namespaces to sync planning. Only namespaces from managed parent resources are considered when sync mode calculates deletes.
  • Child resources (portal pages, API versions, publications, implementations, documents, etc.) are still planned by resolving the external parent's Konnect ID. You do not need to (and cannot) assign a namespace to the external definition itself.
  • An external parent is not changed or deleted, but its child collections explicitly included in sync scope are fully reconciled, including stale child deletion. Child collections omitted from the configuration remain out of scope and are not pruned.

Inline lookups use either a field:value scalar or a mapping. The target type is inferred from the relationship field:

apis:
  - ref: products
    name: Products
    publications:
      - ref: products-publication
        portal_id: !lookup {name: Shared Developer Portal}

ai_gateway_model_providers:
  - ref: shared-provider
    ai_gateway: !lookup {name: shared-ai-gateway}
    name: openai
    type: openai
    display_name: OpenAI
    config: {}

!lookup and !external are exact aliases. A mapping can contain multiple selectors, all of which must match. id:<uuid> and {id: <uuid>} bind a known ID directly and cannot be combined with other selectors. Selector lookups must match exactly one resource.

Some relationship fields, such as portal_id, are fields from the Konnect API schema. Others, such as ai_gateway, are parent selectors added by kongctl for root-level child declarations. Their names remain different for compatibility, but both accept literal IDs, !ref, !external, and !lookup where supported. When a child is nested under its parent, the kongctl parent selector is omitted and inferred from the nesting.

Lookups run during planning with the active Konnect profile. Equivalent lookups, including _external declarations, are cached for that plan. Saved plans contain resolved IDs rather than 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 is not 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:

  • _deck is allowed only on control planes and only one _deck config is allowed per control plane.
  • _deck.files must include at least one state file.
  • _deck.flags can include additional deck flags (but not Konnect auth or output flags).
  • _external.selector.matchFields.name is required for external gateway services and must be the only selector field.
  • kongctl runs exactly one deck gateway apply or deck gateway sync per control plane that declares _deck.
  • Deck state files should include _info.select_tags and matching tags on entities so sync does not delete resources owned by other deck files. kongctl does not inject select tags for you.
  • Relative deck file paths are resolved relative to the declarative config file and must remain within the --base-dir boundary (default: the config file directory).
  • When _deck.files is inherited from a template, its paths remain relative to the file containing the consuming control plane. Unlike a !file tag, a deck file path is not resolved in the template definition's context.
  • 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-file for portable plans). Applying a plan resolves them from the plan file directory (or the current working directory when using --plan -).
  • kongctl plan/diff runs deck gateway diff to decide whether an external tool change is needed. kongctl apply runs deck gateway apply and kongctl sync runs deck 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 is not 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_targets on the _deck change entry, including control plane identifiers and the gateway service selector.

Portal Audit Log Webhooks

Portal audit-log webhooks can reference organization audit-log destinations that are managed outside kongctl. Declare those destinations under audit-logs.destinations with _external, then reference them from a portal webhook with !ref.

portals:
  - ref: docs-portal
    name: Docs Portal
    audit_log_webhook:
      ref: docs-portal-audit-log-webhook
      enabled: true
      audit_log_destination_id: !ref foo

audit-logs:
  destinations:
    - ref: foo
      _external:
        selector:
          matchFields:
            name: foo

audit-logs.destinations supports _external.id and _external.selector.matchFields.name. Destination resources cannot declare kongctl metadata and are not created, updated, or deleted by declarative apply. In sync mode, omitted portal webhook configuration is ignored unless an audit_log_webhook block is explicitly present for that portal. To remove an existing webhook while retaining the portal, declare audit_log_webhook: {}. audit_log_webhook: null is rejected because null is not a reset or delete signal.

AI Gateway Config Stores and Vaults

AI Gateway Config Stores can back Konnect Vaults declared under the same gateway. Use !ref <config-store-ref>#id for the Vault's config.config_store_id so kongctl orders creation and supplies the remote Config Store ID:

ai_gateways:
  - ref: support-gateway
    name: support-gateway
    display_name: Support Gateway
    config_stores:
      - ref: support-config-store
        name: support-config-store
        display_name: Support-Config-Store
        secrets:
          - ref: support-openai-header
            key: openai-auth-header
            value: !secret {source: !env OPENAI_AUTH_HEADER}
    vaults:
      - ref: support-secrets
        name: support-secrets
        type: konnect
        config:
          config_store_id: !ref support-config-store#id

Secret values are write-only. New secrets require value: !secret with a deferred source and are written once during creation. Existing secrets are not rotated unless --write-secret <ref>#value or --write-secrets is supplied while planning. Omit secrets to leave a store's secrets unmanaged during sync, or use secrets: [] to remove all secrets in that store's sync scope. See the Config Store and Vault example for a complete provider Vault reference.

AI Gateway Runtime TLS

AI Gateway runtime certificates, CA certificates, and SNIs are gateway child resources. They are distinct from data plane certificates, which authenticate data planes to Konnect. Runtime certificate private keys are write-only and must use !secret:

ai_gateways:
  - ref: support-gateway
    name: support-gateway
    display_name: Support Gateway
    certificates:
      - ref: runtime-cert
        name: runtime-cert
        cert: !file ./certs/runtime.pem
        key: !secret {source: !file ./certs/runtime-key.pem}
    ca_certificates:
      - ref: partner-ca
        name: partner-ca
        cert: !file ./certs/partner-ca.pem
    snis:
      - ref: support-sni
        name: support-sni
        display_name: Support hostname
        hostname: support.example.com
        certificate: !ref runtime-cert#name

Omit one of these child keys during sync to leave that collection unmanaged. Use certificates: [], ca_certificates: [], or snis: [] under a gateway to sync-delete that collection. Dumps omit private keys and can be planned again without reporting drift solely because those keys are unavailable.

Configuration Templates

Use a top-level _templates configuration block to define named, reusable configuration blocks. Add _extends to a resource or nested configuration block to merge one named template into that block. Both keys are authoring constructs: kongctl removes them before validation, planning, or sending configuration to an API.

Templates apply to any resource configuration block. For example, a shared Portal parent configuration and Portal Page child configuration can be used together:

_templates:
  standard-developer-portal:
    authentication_enabled: true
    rbac_enabled: true
    default_api_visibility: private
    default_page_visibility: private
    labels:
      managed-by: kongctl
      experience: standard

  published-guide-page:
    visibility: public
    status: published
    description: Developer documentation

portals:
  - _extends: standard-developer-portal
    ref: payments-portal
    name: Payments Developer Portal
    display_name: Payments APIs
    labels:
      business-unit: payments
    pages:
      - _extends: published-guide-page
        ref: payments-getting-started
        slug: getting-started
        title: Getting Started
        content: !file ./pages/payments-getting-started.md

The effective Portal labels contain managed-by, experience, and business-unit. A child template can also be used in a root-level declaration:

portal_pages:
  - _extends: published-guide-page
    ref: payments-authentication
    portal: payments-portal
    slug: authentication
    title: Authentication
    content: !file ./pages/authentication.md

Expansion is independent of field names and resource types. The following AI Gateway example reuses both policy fields and the policy's nested config configuration block. The config key has no special template behavior.

_templates:
  corporate-oidc-config:
    issuer: https://auth.example.com
    auth_methods: [bearer]
    bearer_token_param_type: [header]
    consumer_optional: false
    hide_credentials: true

  standard-oidc-policy:
    type: openid-connect
    enabled: true
    global: false
    config:
      _extends: corporate-oidc-config

ai_gateways:
  - ref: shared-ai-gateway
    name: shared-ai-gateway
    display_name: Shared AI Gateway
    policies:
      - _extends: standard-oidc-policy
        ref: payments-oidc
        name: payments-oidc
        display_name: Payments OIDC
        config:
          groups_required: [payments-api-users]

      - _extends: standard-oidc-policy
        ref: reporting-oidc
        name: reporting-oidc
        display_name: Reporting OIDC
        config:
          groups_required: [reporting-api-users]

Template discovery and inheritance

All files supplied to one command share one template registry. This includes explicit files, recursively discovered directory files, standard input, and HTTP or HTTPS sources. A template-only file is valid when it is loaded with at least one resource file:

kongctl plan -f templates.yaml -f portals.yaml
kongctl plan -f ./configuration --recursive

kongctl does not search files outside the supplied source set. Template names must be non-empty strings and must be unique across that source set. Duplicate names are errors even when the definitions are identical or unused.

A template can extend another template:

_templates:
  standard-portal:
    authentication_enabled: true
    default_api_visibility: private

  restricted-portal:
    _extends: standard-portal
    rbac_enabled: true

Each configuration block can extend exactly one template. Circular inheritance and unknown template names are errors. Templates may define identity fields such as ref and name, but each consumer must override them when uniqueness is required.

Merge rules

The consumer configuration block takes precedence over the template:

Template and consumer values Result
Both configuration blocks Recursively merge their keys
Consumer scalar Replace the template value
Consumer sequence Replace the complete template sequence
Consumer explicit null Replace the template value with null
Consumer key omitted Retain the template value
Different value types Replace with the consumer value

Sequences never append or merge by element. A template definition must be a configuration block; a sequence can be inherited only as a value within that block. Individual configuration blocks inside a sequence can use _extends.

Templates are expanded before normal schema validation and sync-scope capture. An inherited empty child collection therefore has the same sync behavior as an empty collection written directly on the resource. YAML tags in a template use the template definition file's context, so a relative !file path is relative to that file. Deferred !env and !secret values retain their existing behavior after expansion.

_templates is separate from _defaults: templates are selected explicitly and shared across the source set, while defaults remain automatic and file-local. _extends is not interpreted inside _defaults.

YAML Tags

Patching tagged configuration

kongctl patch file preserves custom tags in the input when writing YAML, including !ref, !file, !env, !secret, !lookup, and !external. Patching does not resolve references, read tagged files, evaluate environment variables, or fetch secrets. Tags survive even when no selector matches or the patch makes no changes.

Updating a child of a tagged mapping preserves the mapping's tag and any untouched child tags. Setting a field replaces its entire value, including its old tag; replacement objects are not recursively merged. Appending to a tagged sequence preserves its tag and existing entries. Inline values and patch-file values use JSON-compatible types; introducing custom tags through patch values is unsupported.

--format json fails if custom tags remain after patching, because JSON cannot represent them. Use YAML output, or explicitly remove or replace the tagged fields before requesting JSON output. Failed serialization does not overwrite the output file.

Input must contain a single mapping document. Additional YAML documents, including an empty document introduced by a trailing ---, are rejected instead of silently discarded. YAML output also fails before writing if a patch removes or replaces an anchor that a remaining alias still needs.

Tag preservation is a semantic guarantee. Comments, key ordering, anchors, and byte-for-byte formatting are not guaranteed to remain unchanged.

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.

The supported relationship tags are:

  • !ref: reference a resource declared in the same configuration.
  • !lookup: resolve an existing remote resource during planning.
  • !external: exact alias for !lookup.

kongctl explain <resource>.<field> reports the relationship target, whether the field is an API foreign key or a kongctl parent selector, supported tags, selectors, and any required scope field.

Nested YAML Tags

Nested tags are supported only for combinations whose resolution order and security behavior are defined explicitly. !env may be used as a direct mapping selector value inside !lookup or !external:

# Block mapping syntax
portal_id: !lookup
  name: !env PORTAL_NAME

# Compact flow syntax
control_plane: !lookup {name: !env CONTROL_PLANE_NAME}

Both scalar and map forms of the nested !env tag are supported. Multiple lookup selectors may use !env, subject to the selectors supported by the target resource. The existing rule that id cannot be combined with other selectors still applies.

The current nested-tag support matrix is:

Outer tag Inner tag Status
!lookup / !external !env Direct mapping values only
!secret !env, !file Direct source value or parts element
!lookup / !external !file, !ref, lookup tags Unsupported
!env, !file, !ref Any custom tag Unsupported

The scalar field:value lookup form cannot contain a nested YAML tag; use a mapping form instead. Tags are also rejected in mapping keys, nested lookup objects, and control fields such as var, extract, or path.

Nested !file values are supported only as deferred sources inside !secret. They are not supported inside lookup tags. Nested !ref values are not enabled because references resolve after resources load, while remote lookups must resolve during planning. A tag contained in data loaded by !file continues to be treated as file content and is not processed recursively.

For a supported nested !env, the environment value is read before the planner performs the remote lookup. Diagnostics redact that selector value. After a match is found, only the resolved resource ID is written to the plan. Saved-plan execution does not read the environment variable again or repeat the lookup, even if the environment later changes.

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.md

All 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.txt

In config.yaml:

apis:
  - ref: users-api
    name: !file ./specs/users-api.yaml#info.title
    description: !file ./docs/descriptions.txt

Supported 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 and URL sources, 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). When URL sources are loaded with --remote-file-save-dir, subsequent relative paths are resolved like normal file sources from the save directory.

# ❌ 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.yaml

File 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 version

Value 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.yaml

Alternatively 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.email

Loading 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_DESCRIPTION

Scalar 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-started

Map 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

  • !env is supported on string-typed fields in this release.
  • Unset environment variables are treated as errors.
  • Empty-but-set environment variables are allowed.
  • During planning, kongctl resolves the current environment value to calculate changes.
  • Saved plan files preserve the deferred !env reference instead of the resolved plaintext value.
  • The exception is !env nested inside !lookup or !external: it is a planner-only selector input, so the saved plan retains the resolved resource ID and does not defer that environment value to execution.
  • During execution, kongctl performs a fresh environment lookup for each deferred !env value instead of reusing the value observed during planning.
  • When you run apply, sync, or delete directly from configuration files, kongctl still plans first and then performs that second lookup during execution in the same command invocation.
  • In direct apply, sync, and delete runs, 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 !env values.

Write-only Secret Fields

For a complete create, rotation, saved-plan, composition, and aggregate-write walkthrough, see the Secrets example.

Some Konnect APIs accept secret values on create or update but do not return them from get or list responses. Common examples include:

  • Portal identity provider config.client_secret
  • DCR provider secrets such as dcr_token, api_key, and initial_client_secret
  • AI Gateway Model Provider authentication values such as config.auth.headers[].value, client_secret, secret_access_key, and service_account_json
  • AI Gateway Auth Strategy OpenID Connect config.client_secret, config.http_proxy_authorization, and config.https_proxy_authorization
  • AI Gateway Vault authentication credentials
  • AI Gateway runtime certificate key and key_alt
  • Portal custom domain ssl.custom_private_key
  • Event Gateway backend cluster authentication.password
  • Event Gateway schema registry authentication password
  • AI Gateway Consumer Credential api_key

Secret material in write-only fields must use !secret. Literal values and eager !file values are rejected because they could enter a saved plan:

client_secret: !secret
  source: !env PORTAL_OIDC_CLIENT_SECRET

Deferred files are also supported inside !secret. The file is validated when the manifest is loaded and read again immediately before execution, without placing its contents in a saved plan:

key: !secret
  source: !file ./certs/runtime-key.pem

Saved plans store deferred secret-file paths relative to the plan file. Keep the referenced files in the plan directory or one of its subdirectories, and move the plan and those files together when executing on another host. Apply resolves the path within the plan directory and rejects absolute paths, parent traversal, and symlinks that escape that boundary. When a plan is written to standard output, the current working directory is the boundary.

Recognized Konnect vault references can be written literally because they identify secret material without containing it. They remain visible in plans, and Konnect resolves them when applying the configuration:

value: "{vault://support-secrets/openai-auth-header}"

!secret can also compose public decorations with one or more deferred sources. Parts are concatenated exactly as written:

value: !secret
  parts:
    - "Bearer "
    - !env AI_PROVIDER_TOKEN

Do not put secret material in literal parts. Literal parts, source kinds, and environment variable names are stored as metadata in saved plans. Resolved values are not. Planning does not require the secret environment variables; execution requires every source to exist and resolve to a non-empty string. All sources are checked before the first API mutation.

Bare !env remains accepted on reviewed secret fields for one deprecation cycle. It has the same deferred behavior as !secret {source: !env ...} and produces a warning. New configuration should use the explicit wrapper.

Declaring a source and authorizing a write are separate operations. Creates send each configured secret once. Existing resources write a secret only when selected during plan generation:

# One field
kongctl plan -f config.yaml \
  --write-secret workforce-idp#config.client_secret \
  --output-file rotation.json

# Every configured secret on one resource
kongctl plan -f config.yaml \
  --write-secret workforce-idp \
  --output-file rotation.json

# Every eligible configured secret
kongctl plan -f config.yaml --write-secrets \
  --output-file rotation.json

--write-secrets is best-effort across the complete configuration. It writes every eligible configured secret and reports skipped create-only or otherwise ineligible fields as warnings on standard error. The warnings are also kept in saved plan metadata for later review. If the flag finds no writable secrets, the command succeeds with a warning. An exact --write-secret selector remains strict and fails if its requested field cannot be written.

The current Konnect API permits at most one AI Gateway Model Provider authentication header. The selector still uses config.auth.headers[].value because it follows the array-shaped OpenAPI field without coupling configuration to index 0.

Selectors use [resource-type:]resource-ref[#field]. plan, diff, and direct configuration-based apply and sync accept them. A saved plan already contains its secret-write intents, so write-selection flags cannot be combined with --plan. Delete mode does not accept secret selection.

The planner cannot compare a write-only field with its remote value. Without a selector, it continues to omit the field and remains idempotent. With a selector, it records a write intent, merges it into an ordinary update, or creates a secret-only update. Human output reports write requested without showing a value.

AI Gateway Consumer Credential api_key is create-only. Omit it to let Konnect generate a key, or supply it with !secret on a new credential. Selecting it on an existing credential fails. Rotate it by declaring a new credential and deliberately retiring the old one; kongctl never silently recreates it.

Commands Reference

The following are high level descriptions of commands for declarative configuration management. See the command usage text for details on command usage, flags and options.

plan

Create a plan - a JSON file containing the set of planned changes to a set of resources. Plans are generated with --mode apply, --mode sync, or --mode delete. Apply mode creates and updates configured resources only. Sync mode also deletes managed resources, but only for resource collections that are explicitly present in the input configuration. Delete mode targets matching resources for deletion.

Generate an apply plan and output to STDOUT:

kongctl plan -f config.yaml --mode apply

Generate a sync plan and output to STDOUT:

kongctl plan -f config.yaml --mode sync

apply

Applying a configuration will create or update resources to match the desired state and will not delete resources. Because apply does not delete resources, it can be used for incremental application of resource configurations. For example, you could apply a portal in one command and then later apply apis in a separate command.

Apply directly from config:

kongctl apply -f config.yaml

Apply from saved plan:

kongctl plan --mode apply -f config.yaml --output-file plan.json
kongctl apply --plan plan.json

Preview changes without applying:

kongctl apply -f config.yaml --dry-run

sync

sync applies a set of configurations including deleting managed resources that are missing from explicitly scoped collections.

Sync scope is based on YAML key presence:

  • Omitted resource collections are ignored.
  • Explicit empty root lists mean the desired count is zero. For example, apis: [] deletes managed APIs in the selected namespace.
  • Parent and child collections are scoped separately. A portal block without pages does not delete portal pages. Use pages: [] under that portal to declare that the portal should have no pages.
  • Map-shaped child collections use an empty object as the empty collection. For example, email_templates: {} means the portal should have no customized email templates.
  • Singleton child sections use the same key-presence rule, but {} and null are intentionally different. Omit a singleton key to ignore that child. Provide an object with fields to manage or update it. For optional, delete-capable portal singletons such as custom_domain, email_config, and audit_log_webhook, an empty object scopes the child with desired count zero: custom_domain: {} deletes any existing managed custom domain for that portal during sync. null is rejected because sync does not infer reset or delete semantics from null. Update-only singleton sections, such as customization, cannot be deleted by declaring {}.
  • Empty child collections must be nested under a parent resource. Root-level api_documents: [] is rejected because it does not identify which API owns the desired zero count.

For federated ownership, include the parent resource entry in the team configuration and scope only the child collection that team owns. When the parent is managed elsewhere and the resource type supports _external, declare the parent as external and nest the child collection under that parent. This allows sync to plan the child collection without treating the managed parent collection in the team's namespace as desired state.

apis:
  - ref: orders-api
    name: Orders API
    documents: []
portals:
  - ref: shared-docs-portal
    _external:
      selector:
        matchFields:
          name: "Shared Docs Portal"
    pages: []

The external-parent pattern should not be combined with a namespace default unless the team also intends to scope managed parent resources in that namespace.

Preview sync changes:

kongctl sync -f config.yaml --dry-run

Sync configuration with a prompt confirmation:

kongctl sync -f team-config.yaml

Skip confirmation prompt (caution!):

kongctl sync -f config.yaml --auto-approve

Sync from a plan artifact:

kongctl plan --mode sync -f config.yaml --output-file plan.json
kongctl sync --plan plan.json

delete

delete removes the resources selected by the supplied declarative configuration. Saved delete plans must be generated in delete mode.

Delete directly from config:

kongctl delete -f config.yaml

Generate, review, and execute a delete plan:

kongctl plan --mode delete -f config.yaml --output-file delete-plan.json
kongctl diff --plan delete-plan.json
kongctl delete --plan delete-plan.json

diff

Display preview of changes between current and desired state:

Preview changes in apply mode (CREATE and UPDATE only):

kongctl diff -f config.yaml --mode apply

Preview changes in sync mode (CREATE, UPDATE, and DELETE):

kongctl diff -f config.yaml --mode sync

Preview targeted deletions in delete mode (DELETE only for matching resources):

kongctl diff -f config.yaml --mode delete

Preview changes from a plan artifact:

kongctl diff --plan plan.json

Note: --mode cannot be used with --plan because mode is stored in the plan artifact metadata. Unlike execution commands, diff --plan accepts apply-, sync-, and delete-mode plans.

For UPDATE actions, text diff shows only the fields that would be changed. JSON and YAML outputs expose the same detail in each change's changed_fields object while keeping fields as the execution payload.

adopt

kongctl declarative configuration engine will only consider resources that are part of the list of kongctl.namespace values given to it during planning and execution of changes. There may be cases where you want to bring an existing Konnect resource into configuration that was created outside of the configuration management process. The adopt command enables you to add the proper namespace label to an existing Konnect resources without modifying any other fields. Once you adopt a resource, you need to add the configuration for it to your configuration set to ensure it is managed going forward.

Adopt a portal by name:

kongctl adopt portal my-portal --namespace team-alpha

Adopt a control plane by ID:

kongctl adopt control-plane 22cd8a0b-72e7-4212-9099-0764f8e9c5ac \
  --namespace platform

Adopt a custom dashboard by ID:

kongctl adopt analytics dashboard 22cd8a0b-72e7-4212-9099-0764f8e9c5ac \
  --namespace analytics

If the resource already has a KONGCTL-namespace label, the command fails without making changes.

dump

Export current Konnect resource state to various formats.

# Export all APIs with their child resources and include debug logging
# to tf-import format
kongctl dump tf-import --resources=api --include-child-resources
# Export all portal and api resources to 
# kongctl declarative configuration with format and the team-alpha namespace
kongctl dump declarative --resources=portal,api --default-namespace=team-alpha

Add --skip-defaults to omit values that equal defaults declared by the Konnect API SDK:

kongctl dump declarative --resources=portal,api \
  --include-child-resources --skip-defaults > konnect.yaml

This option makes dumps smaller while preserving non-default values. It applies to parent and nested child resources. Only literal API defaults from the generated SDK are omitted; kongctl conveniences such as deriving name from ref are not considered API defaults and remain in the output when present. Explicit null values are also preserved.

Without --skip-defaults, dump behavior and output are unchanged. Default discovery and YAML filtering are not run. The option affects only dump declarative; it does not change plan, apply, diff, or dump tf-import behavior.

For custom dashboards created in the Konnect UI, adopt the dashboard first, then dump it with the same namespace:

kongctl adopt analytics dashboard 22cd8a0b-72e7-4212-9099-0764f8e9c5ac \
  --namespace analytics
kongctl dump declarative --resources=analytics.dashboards \
  --default-namespace=analytics > dashboards.yaml
kongctl plan -f dashboards.yaml --mode apply

CI/CD Integration

Start with the GitHub Actions quickstart for a complete Dev Portal and API example: show diffs on pull requests, then run kongctl apply on pushes to main. It includes an inline OpenAPI specification, the manifest, the workflow, and GitHub secret configuration. The apply command calculates a fresh plan before execution.

Key principles for CI/CD integration:

  1. Plan on PR: Generate and review plans in pull requests
  2. Apply on Merge: Apply merged configuration on the target branch; use saved plans when the workflow requires a separate artifact review
  3. Environment Separation: Different configs for dev/staging/prod
  4. 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 namespace

Environment Management

Use configuration profiles for different environments:

# Development environment
kongctl apply -f config.yaml --profile dev

# Production environment
kongctl apply -f config.yaml --profile prod

Security Best Practices

  1. Protect production resources:

    apis:
      - ref: payment-api
        kongctl:
          namespace: production
          protected: true
  2. Use namespaces for isolation:

    • One namespace per team
    • Separate namespaces for environments
    • Clear namespace ownership documentation
  3. Version control everything:

    • Configuration files
    • OpenAPI specifications
    • Documentation
  4. Review plans before applying:

    • Use plan in production
    • Save plans for audit trail
    • Implement approval workflows

Plan Artifact Workflows

Basic Plan Review Workflow

Developer creates plan:

kongctl plan --mode apply -f config.yaml \
  --output-file proposed-changes.json

Review changes visually:

kongctl diff --plan proposed-changes.json

Share 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.json

Production Deployment with Approval

# CI/CD Pipeline Stage 1: Plan Generation
kongctl plan --mode sync -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-approve

Emergency 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.json

Execute the previously approved sync-mode plan:

kongctl sync --plan plans/last-known-good.json --auto-approve

Common 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: true

Correct approach:

# RIGHT
apis:
  - ref: my-api
    kongctl:
      namespace: team-a
      protected: true
    versions:
      - ref: v1

Using 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-api

Field 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: platform

Common field name errors:

  • lableslabels
  • descriptindescription
  • displaynamedisplay_name
  • strategytypestrategy_type

Troubleshooting

Common Issues

Authentication Failures:

  • Verify PAT is not expired
  • Check authentication: kongctl get me
  • Ensure proper credential storage

Plan Generation Failures:

  • Validate YAML syntax
  • Check file paths are correct
  • Verify network connectivity

Apply Failures:

  • Review plan for conflicts
  • Check for protected resources
  • Verify dependencies exist

File Loading Errors:

Error: failed to process file tag: file not found: ./specs/missing.yaml
  • Verify the file path is correct
  • Check that the file exists
  • Ensure proper relative path from config file location

Debug Mode

Enable verbose logging:

kongctl apply -f config.yaml --log-level debug

Enable trace logging for HTTP requests:

kongctl apply -f config.yaml --log-level trace

For more troubleshooting help, see the Troubleshooting Guide.

Examples

Browse the examples directory

Related Documentation