---
title: "Managing API Gateways with Terraform: When You Need It and When GitOps Is Better"
description: "Learn when Terraform makes sense for API gateway management and when a GitOps-native approach is faster, simpler, and less prone to drift."
canonicalUrl: "https://zuplo.com/learning-center/terraform-api-gateway-management"
pageType: "learning-center"
authors: "nate"
tags: "API Gateway, API Best Practices"
image: "https://zuplo.com/og?text=Managing%20API%20Gateways%20with%20Terraform"
---
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](/learning-center/what-is-gitops) 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`](https://registry.terraform.io/providers/Kong/konnect/latest)
(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](https://zuplo.com) 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](/docs/articles/open-api) specification file. Policies attach to routes
declaratively. Custom request and response handling is written in TypeScript and
lives alongside the configuration:

```
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](/docs/articles/branch-based-deployments) 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](/docs/articles/policies) are either selected from a catalog of
built-in options or written as TypeScript functions:

```typescript
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`:

```json
{
  "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](/docs/articles/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](/learning-center/api-gateway-gitops-cicd) 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](/docs/articles/source-control) 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](https://portal.zuplo.com) and see how a GitOps-native gateway
simplifies your API management workflow.