---
title: "How to Manage Your API Gateway with GitOps"
description: "Why GitOps matters for your API gateway: PR reviews, branch environments, and a single source of truth. Learn how Zuplo gives you all of it by default, with no extra tooling."
canonicalUrl: "https://zuplo.com/blog/2026/02/18/manage-your-apis-with-gitops"
pageType: "blog"
date: "2026-02-18"
authors: "martyn"
tags: "API Gateway"
image: "https://zuplo.com/og?text=Manage%20Your%20APIs%20with%20GitOps"
---
Your application code lives in Git. Your infrastructure is Terraform. Your
Kubernetes configs are Helm charts in a repo somewhere. But your API gateway is
still configured through a UI, and nobody's really sure what's running in
production versus staging.

GitOps fixes this. And if you're using Zuplo, you get it without any extra
tooling.

<CalloutAudience
  variant="useIf"
  items={[
    `Your API gateway configuration lives in a dashboard and not in source control`,
    `You've had \`it worked in staging\` incidents caused by environment drift`,
    `You want PR-based reviews for API changes the same way you review code`,
    `You need an audit trail of who changed what on your API`,
  ]}
/>

## What GitOps means for an API gateway

GitOps is the practice of using Git as the single source of truth for
configuration. Instead of clicking around a dashboard or running API calls to
configure your gateway, you declare the desired state in files, commit them, and
let the system handle deployment.

For an API gateway, that means:

- Routes defined in files, not a database
- Auth and rate limiting policies configured in code
- Every change goes through a pull request
- Deployment triggers automatically on merge
- Rollback is `git revert` and push

Most legacy API gateways weren't designed this way. They use imperative APIs:
you call an endpoint to create a route, the tool stores the state internally,
and you need something like Terraform to make it feel declarative. That works,
but you're now maintaining Terraform state files, dealing with drift, and
running plan/apply cycles on top of your actual gateway configuration.

Zuplo skips all of that. Configuration is just files in your repository.

## What Zuplo configuration looks like in practice

Every aspect of your Zuplo gateway is stored in your repo. Routes are defined in
`config/routes.oas.json`, a standard OpenAPI file. Policies attach to routes
declaratively. Custom logic is TypeScript sitting alongside everything else.

Here's what a rate-limited, authenticated route looks like in `routes.oas.json`:

```json
{
  "paths": {
    "/api/v1/orders": {
      "get": {
        "x-zuplo-route": {
          "corsPolicy": "anything-goes",
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "forwardSearch": true,
              "baseUrl": "https://your-backend.example.com"
            }
          },
          "policies": {
            "inbound": ["api-key-inbound", "rate-limit-inbound"]
          }
        }
      }
    }
  }
}
```

That's the whole configuration for a route with API key auth and rate limiting.
It's in Git. It gets reviewed in a PR. It deploys when you push.

A typical Zuplo project looks like this:

```
my-api/
├── config/
│   ├── routes.oas.json       # Route definitions (OpenAPI format)
│   └── policies.json         # Policy configuration
├── modules/
│   └── my-custom-handler.ts  # Custom TypeScript handlers and policies
├── docs/                     # Developer portal (optional)
│   ├── zudoku.config.ts
│   └── pages/
├── .env                      # Local environment variables (do not commit)
├── .env.zuplo                # Generated by `zuplo link` (do not commit)
├── zuplo.jsonc               # Project metadata
└── package.json
```

Everything that defines how your API behaves is in `config/` and `modules/`. The
rest is standard Node project scaffolding. Clone the repo, run `npm run dev`,
and you have a local gateway running against these files.

### Connecting to GitHub

Connect your project to GitHub from the **Settings > Source Control** tab in the
Zuplo portal. You'll authorize the Zuplo GitHub app and grant it access to the
repositories you want to work with. From there, create a new repository or
connect an existing one.

Once connected, you get four things automatically:

- **Automatic deployments**: every push deploys to Zuplo, no CI/CD configuration
  required
- **Branch environments**: each branch gets its own isolated environment
- **Deployment status checks**: deploy results show as GitHub checks on commits
  and PRs, with a link to the live environment
- **Portal sync**: make changes in the Zuplo UI and commit them back to Git, or
  pull in changes your teammates pushed

After that, the loop is just Git:

```bash
git add .
git commit -m "Add rate limiting to /api/v1/orders"
git push origin main
# Zuplo deploys globally in ~20 seconds
```

You'll see a green check on the commit in GitHub when the deployment succeeds.
If you want to add approval gates, run tests before deploying, or integrate with
existing pipelines, that's supported via custom GitHub Actions.

<CalloutDoc
  title="GitHub setup guide"
  description="Full connection instructions. See also the source control overview for GitLab, Bitbucket, and Azure DevOps."
  href="https://zuplo.com/docs/articles/source-control-setup-github"
/>

## Branch-based environments

With Zuplo's GitHub integration, every branch automatically gets its own
isolated environment. Push to `feature/new-auth-flow` and a
`feature-new-auth-flow` environment spins up in seconds. A full, live API
running your changes.

This makes reviewing API changes concrete. Instead of reading a diff and hoping
the policy logic is correct, reviewers can hit the preview environment directly.
Frontend teams can test against real API behavior before anything merges.

The workflow:

1. Create a feature branch
2. Edit routes, policies, or custom handlers
3. Push. A preview environment deploys automatically
4. Open a PR. Reviewers get a live URL to test against
5. Merge to `main`. Production deploys

No shared staging environment that slowly drifts out of sync with production.
Every branch is its own clean environment.

<CalloutDoc
  title="Branch-based deployments"
  description="Details on how environments map to branches."
  href="https://zuplo.com/docs/articles/branch-based-deployments"
/>

## PR reviews and protected branches for API changes

Because configuration is code, you can apply the same controls you use for
application code.

Require PR reviews before anything merges to `main`. Your security team can
review changes to authentication policies. Your platform team can sign off on
rate limiting adjustments. Nobody makes ad-hoc changes to production that
disappear into the void.

Protected branches block direct pushes. Combined with Zuplo's GitHub
integration, this means every change to your production API gateway has a paper
trail: who proposed it, who approved it, what the diff was.

You can also run automated checks before deployment. Validate your OpenAPI
schema, run integration tests against the preview environment, lint your
TypeScript policies, all before a change goes live.

## Why not Terraform?

If you're coming from a platform that uses imperative APIs (Kong, Azure API
Management, and others), Terraform is often the path to GitOps. You write a
provider that wraps API calls in declarative syntax, and now your gateway
configuration is theoretically in code.

The tradeoffs:

- State files that need to be stored, locked, and kept in sync
- Drift when someone makes a change outside Terraform (it happens)
- Plan/apply cycles that add friction to every deployment
- A second system to learn and maintain alongside your gateway

Zuplo doesn't offer a Terraform provider because it doesn't need to. There's no
internal state to reconcile. What's in your repository is what's deployed.
Adding Terraform would just be a wrapper around a system that's already
declarative.

If you're evaluating API gateways and GitOps is a requirement, it's worth asking
whether the tool you're looking at is GitOps-native or whether it needs
Terraform to approximate that behavior.

<CalloutDoc
  title="Native GitOps: Better Than Terraform"
  description="A deeper look at how Zuplo's approach differs from tools that need external IaC layers."
  href="https://zuplo.com/docs/articles/terraform"
/>

## Setting up GitOps with Zuplo

**1. Create a project and push to GitHub**

```bash
npx create-zuplo-api@latest
cd your-project
git remote add origin https://github.com/your-org/your-zuplo-project
git push -u origin main
```

**2. Connect to GitHub in the Zuplo portal**

Go to project settings, connect your repository. From this point, every push
deploys automatically and every branch gets its own environment.

**3. Set up branch protection**

In GitHub, require PR reviews before merging to `main`. Now all production API
changes go through code review.

**4. Add automated tests (optional but recommended)**

Run your test suite against the preview environment before merging:

```bash
# In your GitHub Actions workflow
- name: Run API tests
  run: npm test
  env:
    API_BASE_URL: ${{ steps.deploy.outputs.environment-url }}
```

<CalloutDoc
  title="Deployment testing guide"
  description="How to wire up test suites to Zuplo deployments."
  href="https://zuplo.com/docs/articles/github-deployment-testing"
/>

**Using a different Git host or CI system**

Zuplo supports GitLab, Bitbucket, and Azure DevOps for source control. For
deployments from any CI system, use the Zuplo CLI:

```bash
npx zuplo deploy \
  --api-key $ZUPLO_API_KEY \
  --project your-project-name \
  --environment production
```

<CalloutDoc
  title="Custom CI/CD Guide"
  description="CircleCI, GitLab CI, and other pipeline setups."
  href="https://zuplo.com/docs/articles/custom-ci-cd"
/>

## CI/CD Pipeline Templates

If you want full control over your deployment pipeline, the Zuplo CLI integrates
cleanly with any CI system. Here's a GitHub Actions workflow that deploys
preview environments on pull requests and ships to production when you merge to
`main`:

```yaml
name: Deploy API Gateway
on:
  push:
    branches: [main]
  pull_request:

jobs:
  deploy-preview:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm install
      - run: npx zuplo deploy
        env:
          ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }}

  deploy-production:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm install
      - run: npx zuplo deploy --environment production
        env:
          ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }}
```

The workflow runs two jobs. `deploy-preview` fires on every pull request and
spins up an isolated preview environment so reviewers can test changes against a
live API before anything hits production. `deploy-production` only runs when
code lands on `main`, keeping your production deploys gated behind your merge
process. This means every PR gets a live URL that teammates (or automated tests)
can hit, and production only updates after review and approval.

If you're on GitLab CI, Bitbucket Pipelines, CircleCI, or any other CI system,
the same `npx zuplo deploy` command works. Swap out the GitHub Actions syntax
for your platform's equivalent and the deployment model stays identical.

## Going Deeper

GitOps for your API gateway opens up a lot of territory once the basics are in
place. If you're evaluating how different platforms handle source control,
environment promotion, and rollback, take a look at our
[GitOps for API Management: Platform Comparison](/learning-center/gitops-api-management-comparison)
for a side-by-side breakdown. And if you're designing a multi-environment
pipeline with staging gates, canary deploys, or automated rollback triggers, our
guide on [CI/CD for API Gateways](/learning-center/ci-cd-api-gateway-deployment)
covers the patterns and tradeoffs in detail.

## What changes once your API gateway is in Git

The individual benefits of GitOps for an API gateway are straightforward: better
reviews, cleaner rollbacks, no environment drift. But the bigger payoff is what
happens to your team's workflow over time.

When a new engineer joins, they clone the repo and run `npm run dev`. The entire
gateway (routes, policies, custom handlers) is running locally in minutes. No
tracking down who has dashboard access, no "let me show you where the settings
are." The configuration is just there, in the code, like everything else.

When something breaks in production, you have an actual timeline. Git history
tells you what changed, when, and who approved it. A bad deploy is a
`git revert` away from being fixed, not a frantic session clicking through UI
panels trying to remember what the previous setting was.

When your security team asks who can make changes to authentication
configuration and what the approval process looks like, you have a concrete
answer: pull requests, required reviewers, protected branches. The same answer
you'd give for any other part of your codebase.

And when you're scaling the team and adding new environments, each one is a
branch. There's no "set up staging to match production" work to do. Consistency
is built into the deployment model.

Most of this isn't specific to Zuplo. It's what you get when you take GitOps
seriously for any part of your infrastructure. What Zuplo does is make it the
default for your API gateway, with no external tooling required.

[Get started with Zuplo for free](https://portal.zuplo.com/signup)