Zuplo
API Gateway

Managing API Gateways with Terraform: When You Need It and When GitOps Is Better

Nate TottenNate Totten
May 12, 2026
9 min read

Learn when Terraform makes sense for API gateway management and when a GitOps-native approach is faster, simpler, and less prone to drift.

Terraform has become the default way to manage cloud infrastructure. If you’re provisioning VPCs, IAM roles, DNS records, or database clusters, chances are you’re writing HCL and running terraform apply. So when it’s time to manage your API gateway, the instinct is to reach for the same tool.

That instinct makes sense — but it can lead you down a path that adds unnecessary complexity. API gateway configuration is fundamentally different from infrastructure provisioning, and treating route definitions and policy configs the same way you treat a VPC can create more problems than it solves.

This article breaks down the Infrastructure as Code (IaC) landscape for API gateways, explains where Terraform genuinely excels, identifies where it struggles with gateway-specific workloads, and shows when a GitOps-native approach delivers better results.

The IaC Landscape for API Gateways

Teams managing API gateways today have several declarative tooling options, each with different trade-offs.

Terraform is a provider-agnostic infrastructure provisioning tool. You define resources in HCL, Terraform builds a dependency graph, and plan/apply cycles reconcile desired state against actual state using a state file. Major API gateway vendors — including Kong, AWS API Gateway, and Azure API Management — ship Terraform providers.

Kubernetes CRDs let you define gateway resources as native Kubernetes objects. If your team already manages everything through kubectl and Helm charts, CRDs keep API gateway config in the same workflow. Kong’s Gateway Operator and the Kubernetes Gateway API both take this approach.

Vendor-specific declarative tools like Kong’s decK or Tyk’s tyk-sync let you define gateway config in YAML files and sync them to a running gateway instance. These work well for single-gateway setups but add operational overhead as environments multiply.

GitOps-native platforms treat the Git repository itself as the deployment mechanism. There is no sync step, no state file, and no reconciliation loop. Pushing to a branch deploys the gateway. Reverting a commit rolls it back. The configuration files in your repo are the gateway.

Each approach has a legitimate place. The question is which one fits the operational characteristics of API gateway management — high-frequency config changes, multi-environment deployments, and tight coupling between API routes and application code.

Where Terraform Excels

Terraform is excellent at managing resources that share three characteristics: they change infrequently, they have stable provider APIs, and they represent long-lived infrastructure.

Cloud Infrastructure Provisioning

VPCs, subnets, security groups, load balancers, DNS zones — these resources are created once and modified rarely. Terraform’s plan/apply cycle is perfectly suited here because the cost of a full state reconciliation is low when changes happen weekly or monthly.

Identity Provider Configuration

Setting up Auth0 tenants, Cognito user pools, or Okta applications involves creating interconnected resources that rarely change after initial setup. Terraform’s dependency graph handles the creation order cleanly.

Cross-Provider Orchestration

Need to provision an AWS RDS instance, configure a Cloudflare DNS record pointing to it, and create a monitoring dashboard in Datadog? Terraform handles all three in a single plan, with proper dependency ordering and rollback semantics.

The common thread: these are infrastructure resources that exist at the platform level, change on a slow cadence, and benefit from centralized state tracking.

Where Terraform Struggles with API Gateways

API gateway configuration doesn’t fit neatly into Terraform’s model. Here’s why.

High-Churn Configuration

API routes, policies, and authentication rules change frequently — often multiple times per day on active teams. Every change triggers a full terraform plan that must diff the entire state against the desired configuration. As the number of managed resources grows, plan times increase and the blast radius of each apply expands.

Compare this to application code, where deploying a change to one endpoint doesn’t require re-evaluating every other endpoint in the system.

State File Complexity

Terraform tracks every managed resource in a state file. For API gateways with hundreds of routes, dozens of policies, and multiple environments, that state file becomes a liability. State locking, remote backend configuration, and state migration all add operational burden that doesn’t exist when your configuration lives directly in Git.

Teams often discover this when two developers try to apply gateway changes simultaneously and run into state lock conflicts — a problem that simply doesn’t occur with Git-based workflows where branches provide natural isolation.

Drift in Fast-Moving Teams

Terraform assumes it is the sole manager of resources it creates. But API gateways often sit at the intersection of multiple teams: platform engineers configure global policies, backend developers add routes, and security teams update authentication rules. When someone makes a change through a dashboard or CLI outside of Terraform, the next plan shows unexpected drift that must be manually reconciled.

Plugin and Policy Configuration Sprawl

Traditional API gateways like Kong manage behavior through plugins, each with its own configuration block. In Terraform, this means defining kong_plugin resources with JSON-encoded configuration strings embedded in HCL. The result is configuration that’s hard to read, hard to review in a pull request, and impossible to test in isolation.

The Kong/Konnect Terraform Approach

Kong’s investment in Terraform illustrates both the appeal and the challenges. Kong ships two Terraform providers: terraform-provider-konnect (GA, with over 300,000 installs) for their Konnect SaaS platform, and terraform-provider-kong-gateway (beta) for self-hosted deployments.

The Konnect provider exists because Kong’s platform has significant surface area beyond gateway routing. Konnect manages control planes, mesh planes, API products, developer portals, teams, roles, and runtime groups. These organizational resources are a natural fit for Terraform — they’re created once, modified rarely, and benefit from centralized state management.

Kong also supports Kubernetes CRDs through the Kong Gateway Operator, giving Kubernetes-native teams a way to manage Konnect resources from within their existing cluster workflows.

The challenge appears at the boundary between platform infrastructure and day-to-day gateway configuration. Route definitions, plugin configurations, and consumer groups change frequently. Managing them through Terraform’s plan/apply cycle introduces friction that Kong’s own declarative tool, decK, was designed to avoid. Many Kong users end up using Terraform for control plane provisioning and decK for route and plugin management — splitting their configuration across two tools with different state models.

GitOps-Native API Management

A GitOps-native API gateway takes a different architectural approach: the gateway configuration lives in your application repository, deploys through your existing Git workflow, and requires no external state management or reconciliation tools.

Zuplo was built this way from the ground up. Every aspect of the gateway — routes, policies, custom logic, and OpenAPI specifications — is stored as files in a Git repository. There is no imperative API to call, no state file to manage, and no sync tool to run.

Here’s what that looks like in practice:

Configuration as Code

Routes are defined in config/routes.oas.json, a standard OpenAPI specification file. Policies attach to routes declaratively. Custom request and response handling is written in TypeScript and lives alongside the configuration:

plaintext
my-api/
├── config/
│   ├── routes.oas.json      # OpenAPI route definitions
│   └── policies.json        # Policy configuration
├── modules/
│   └── auth-handler.ts      # Custom TypeScript logic
└── docs/                    # Developer portal (optional)
    ├── zudoku.config.ts     # Portal configuration
    └── pages/               # Custom portal pages

Every file is human-readable, diffable, and reviewable in a standard pull request. There’s no HCL-encoded JSON, no state file, and no resource graph to debug.

Branch-Based Preview Environments

This is where GitOps-native platforms provide something Terraform fundamentally cannot: automatic preview environments for every branch.

When a developer creates a feature branch and pushes a change, Zuplo automatically deploys a fully functional preview environment with its own URL. Reviewers can send real HTTP requests to test authentication, rate limiting, and routing behavior before any change reaches production.

With Terraform, creating a preview environment means provisioning a complete infrastructure stack — which can take minutes to hours — and destroying it afterward. With a GitOps-native gateway, the preview exists as long as the branch exists, with zero infrastructure overhead.

Atomic Deployments with Zero Drift

Every Zuplo deployment is atomic: the entire configuration either deploys successfully or the previous version remains in place. There are no partial states, no half-applied changes, and no drift between what’s in your repository and what’s running in production.

Rollback is a git revert followed by a push. No terraform destroy, no state surgery, no manual cleanup.

Programmable Policies in Code

Instead of configuring plugins through JSON blobs in HCL, Zuplo’s policies are either selected from a catalog of built-in options or written as TypeScript functions:

TypeScripttypescript
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";

export default async function (request: ZuploRequest, context: ZuploContext) {
  const customerId = request.headers.get("x-customer-id");

  if (!customerId) {
    return new Response("Missing customer ID", { status: 400 });
  }

  // Custom logic lives in code, not in HCL config blocks
  context.log.info(`Processing request for customer: ${customerId}`);

  return request;
}

This code is version-controlled, testable, and reviewable in a pull request — the same workflow your team already uses for application code.

When to Use What: A Decision Framework

The right tool depends on what you’re managing. Here’s a practical framework:

Use Terraform For

  • Cloud infrastructure: VPCs, subnets, security groups, load balancers, CDN configuration
  • Identity providers: Auth0 tenants, Cognito user pools, Okta applications and authorization servers
  • DNS and certificates: Route 53 hosted zones, Cloudflare DNS records, ACM certificates
  • Secrets backends: Vault configuration, AWS Secrets Manager setup
  • Network policy: Firewall rules, VPN configuration, private link setup
  • Monitoring infrastructure: Datadog dashboards, PagerDuty services, alerting rules

These resources change infrequently, benefit from dependency graphs, and are naturally managed at the infrastructure level.

Use GitOps-Native For

  • API routes and endpoints: Path definitions, HTTP method configuration, upstream targets
  • Gateway policies: Rate limiting, authentication, request validation, response transformation
  • OpenAPI specifications: The contract between your API and its consumers
  • Custom gateway logic: Request/response handlers, middleware, data transformation
  • Developer portal content: API documentation, getting-started guides, changelogs

These resources change frequently, are tightly coupled to application behavior, and benefit from the same review and deployment workflow as application code.

Combine Both

The most effective pattern is using Terraform and GitOps together, each managing the layer it’s best suited for:

  1. Terraform provisions the platform: identity providers, DNS records, secrets backends, and monitoring infrastructure
  2. GitOps drives day-2 gateway configuration: routes, policies, custom logic, and API documentation deploy through Git

This separation is clean because the two layers have different change cadences. Infrastructure changes happen weekly or monthly. Gateway configuration changes happen daily. Using the right tool for each cadence eliminates friction without sacrificing control.

Practical Example: Terraform + GitOps Together

Here’s how a real team might combine Terraform for infrastructure with a GitOps-native gateway for API management.

Terraform manages the identity layer:

hcl
# terraform/main.tf — Provisions the identity provider
resource "auth0_client" "api_client" {
  name        = "My API Client"
  app_type    = "non_interactive"
  grant_types = ["client_credentials"]
}

resource "auth0_resource_server" "api" {
  name        = "My API"
  identifier  = "https://api.example.com"
  signing_alg = "RS256"
}

The GitOps-native gateway consumes that identity provider in config/routes.oas.json:

JSONjson
{
  "paths": {
    "/v1/users": {
      "get": {
        "x-zuplo-route": {
          "handler": {
            "module": "$import(./modules/users)",
            "export": "default"
          },
          "policies": {
            "inbound": ["jwt-auth", "rate-limit-50rpm"]
          }
        }
      }
    }
  }
}

Environment variables bridge the two layers:

Terraform outputs — like the Auth0 domain and client ID — are stored as environment variables in Zuplo, where gateway policies reference them at runtime. The infrastructure layer and the gateway configuration layer are cleanly separated, each managed by the tool best suited to its change cadence.

Moving Beyond Terraform for API Configuration

If your team currently manages API gateway configuration through Terraform and you’re feeling the friction — long plan times, state conflicts, drift after manual changes — the path forward doesn’t require abandoning Terraform entirely.

Keep Terraform for what it does best. Your VPCs, IAM roles, DNS records, and identity provider configuration should stay in Terraform. These resources are stable, change infrequently, and benefit from Terraform’s dependency management.

Move gateway configuration to Git. Routes, policies, authentication rules, and custom logic belong in a repository where changes are reviewed in pull requests, deployed automatically on merge, and rolled back with git revert. This is the workflow your developers already know — there’s no new tool to learn.

Use preview environments to test safely. Instead of provisioning a full Terraform stack for every feature branch, use a gateway that creates preview environments automatically. Reviewers can verify API behavior with real HTTP requests before changes reach production.

The IaC landscape for API gateways isn’t one-size-fits-all. Terraform is the right tool for infrastructure provisioning. For the fast-moving, high-churn world of API routes and policies, a GitOps-native approach is simpler, faster, and less prone to the drift and state management headaches that come with managing everything through terraform apply.

Zuplo’s GitOps-native architecture eliminates the need for a separate Terraform provider, CRD controller, or sync tool. Your API configuration lives in your repository, deploys on push, and runs at the edge across 300+ data centers — no infrastructure management required. Get started and see how a GitOps-native gateway simplifies your API management workflow.