Back to all articles
API Gateway

CI/CD for API Gateways: Pipeline Templates and Multi-Environment Deployment

February 26, 2026
  • Introduction
  • Why CI/CD for API Gateways Matters
  • Pipeline Architecture
  • GitHub Actions Template
  • GitLab CI Template
  • Multi-Environment Deployment
  • Branch Preview Environments
  • Multi-Region Deployment
  • Rollback Strategies
  • Testing in the Pipeline
  • Get Started with Zuplo

Introduction

API gateway configuration has traditionally lived outside of version control. Teams log into admin dashboards, click through forms, toggle settings, and hope that staging matches production. When something breaks, the question is always the same: "Who changed what, and when?"

This is the exact problem CI/CD solves for application code, and there is no reason your API gateway should be different. Your routes, policies, rate limits, and authentication rules are configuration that deserves the same rigor as your source code: version-controlled, peer-reviewed, automatically tested, and deployed through a repeatable pipeline.

Modern API gateways like Zuplo are built around this principle. Zuplo is git-native by design, meaning your gateway configuration lives in a Git repository and deploys through standard CI/CD workflows. Even the Zuplo portal syncs with Git, so there is no configuration that can drift out of sync with reality. The repo is the source of truth.

In this guide, you will set up complete CI/CD pipelines for API gateway deployment using GitHub Actions and GitLab CI. You will learn how to manage multiple environments, automate preview deployments for pull requests, deploy across regions, and implement rollback strategies that keep your APIs reliable.

Why CI/CD for API Gateways Matters

Deploying API gateway changes manually introduces the same risks as deploying application code manually: inconsistency, human error, and a lack of accountability. CI/CD pipelines eliminate these risks and bring several concrete benefits.

Consistency Across Environments

When your gateway configuration deploys through a pipeline, every environment gets exactly the same configuration, transformed only by environment-specific variables. There is no "I forgot to update staging" or "production has a different rate limit than what we tested." The pipeline enforces parity.

Audit Trail

Every change to your API gateway is a Git commit. You can see who changed what, when they changed it, and why (through commit messages and PR descriptions). This is not just good practice; it is a compliance requirement for many organizations operating in regulated industries.

Automated Testing

A pipeline can validate your OpenAPI specification, run contract tests against preview environments, and verify that rate-limiting policies behave as expected before a single request hits production. Manual processes cannot match this level of consistency.

Rollback Capability

When your gateway configuration is in Git, rolling back is as simple as reverting a commit and letting the pipeline redeploy. No one needs to remember what the previous dashboard settings were.

Team Collaboration Through Pull Requests

Pull requests give your team a structured way to propose, review, and approve API changes. A new route, a modified authentication policy, or a rate limit adjustment all go through the same review process as any other code change.

Pipeline Architecture

A well-structured API gateway CI/CD pipeline follows a predictable flow that balances speed with safety.

text
Commit --> Pull Request --> Preview Deploy --> Review & Test
                                                   |
                                              Merge to main
                                                   |
                                           Staging Deploy --> Integration Tests
                                                   |
                                          Production Deploy

Each stage serves a specific purpose:

  1. Commit -- The developer pushes gateway configuration changes to a feature branch.
  2. Pull Request -- A PR triggers validation checks: OpenAPI linting, schema validation, and policy checks.
  3. Preview Deploy -- The pipeline deploys the changes to an isolated preview environment where reviewers and automated tests can verify behavior against a live gateway.
  4. Merge to main -- After approval and passing checks, the PR merges into the main branch.
  5. Staging Deploy -- The merge triggers a deployment to the staging environment for final integration testing.
  6. Production Deploy -- After staging validation passes, the pipeline deploys to production.

This architecture ensures that no change reaches production without being validated at multiple stages. Let's implement this with real pipeline templates.

GitHub Actions Template

The following GitHub Actions workflow handles the complete lifecycle: validation on pull requests, preview deployments, and production deployment on merge.

Workflow File

Create .github/workflows/api-gateway-deploy.yml:

YAMLyaml
name: API Gateway Deploy

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

env:
  ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }}

jobs:
  validate:
    name: Validate OpenAPI
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Lint OpenAPI specification
        run: npx @redocly/cli lint openapi.json

      - name: Validate gateway configuration
        run: |
          npx zuplo dev &
          sleep 10
          npx zuplo test --endpoint http://localhost:9000
          kill %1

  deploy-preview:
    name: Deploy Preview
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    needs: validate
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Deploy to preview environment
        id: preview
        run: |
          OUTPUT=$(npx zuplo deploy --api-key "$ZUPLO_API_KEY" 2>&1)
          echo "$OUTPUT"
          DEPLOYMENT_URL=$(echo "$OUTPUT" | grep -oP 'Deployed to \K(https://[^ ]+)')
          echo "url=$DEPLOYMENT_URL" >> "$GITHUB_OUTPUT"

      - name: Comment preview URL on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `Preview environment deployed: ${{ steps.preview.outputs.url }}`
            })

  deploy-staging:
    name: Deploy to Staging
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Deploy to staging
        run: npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment staging

  test-staging:
    name: Integration Tests (Staging)
    runs-on: ubuntu-latest
    needs: deploy-staging
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Run integration tests against staging
        run: npm run test:integration
        env:
          API_BASE_URL: ${{ vars.STAGING_API_URL }}

  deploy-production:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: test-staging
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Deploy to production
        run:
          npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment production

What This Workflow Does

On pull requests, the pipeline runs two jobs in sequence:

  1. validate -- Lints the OpenAPI specification and runs gateway configuration tests to catch errors before deployment.
  2. deploy-preview -- Deploys to an isolated preview environment and comments the preview URL on the PR so reviewers can test against a live gateway.

On merge to main, the pipeline runs three jobs sequentially:

  1. deploy-staging -- Deploys the merged configuration to the staging environment.
  2. test-staging -- Runs integration tests against the staging deployment.
  3. deploy-production -- Deploys to production, gated by a GitHub environment protection rule that can require manual approval.

The environment: production declaration on the final job enables GitHub's environment protection rules, so you can require approvals, restrict which branches can deploy, and set deployment wait timers.

GitLab CI Template

Here is the equivalent pipeline for GitLab CI. Create .gitlab-ci.yml in your repository root:

YAMLyaml
stages:
  - validate
  - deploy-preview
  - deploy-staging
  - test-staging
  - deploy-production

variables:
  NODE_VERSION: "20"

.node-setup: &node-setup
  image: node:${NODE_VERSION}
  before_script:
    - npm ci

validate:
  <<: *node-setup
  stage: validate
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - npx @redocly/cli lint openapi.json
    - npx zuplo dev &
    - sleep 10
    - npx zuplo test --endpoint http://localhost:9000

deploy-preview:
  <<: *node-setup
  stage: deploy-preview
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - npx zuplo deploy --api-key "$ZUPLO_API_KEY"
  environment:
    name: preview/$CI_MERGE_REQUEST_IID
    url: $PREVIEW_URL
    on_stop: stop-preview

stop-preview:
  stage: deploy-preview
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual
  script:
    - echo "Preview environment cleaned up"
  environment:
    name: preview/$CI_MERGE_REQUEST_IID
    action: stop

deploy-staging:
  <<: *node-setup
  stage: deploy-staging
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  script:
    - npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment staging
  environment:
    name: staging
    url: $STAGING_API_URL

test-staging:
  <<: *node-setup
  stage: test-staging
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  script:
    - npm run test:integration
  variables:
    API_BASE_URL: $STAGING_API_URL

deploy-production:
  <<: *node-setup
  stage: deploy-production
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
  script:
    - npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment production
  environment:
    name: production
    url: $PRODUCTION_API_URL

The GitLab template mirrors the GitHub Actions workflow. The production deployment is set to when: manual, requiring an explicit click in the GitLab UI to promote from staging to production. The preview environment uses dynamic environment names tied to the merge request, so each MR gets its own isolated deployment.

Multi-Environment Deployment

Managing multiple environments requires a clear strategy for handling configuration that varies between them. The gateway logic (routes, policies, handlers) stays the same, but connection strings, upstream URLs, API keys, and feature flags differ.

Environment Variables and Secrets

Store environment-specific values as CI/CD secrets and variables, never in your repository:

Terminalbash
# GitHub Actions - set via repository settings or gh CLI
gh secret set ZUPLO_API_KEY --body "zpka_your_api_key"
gh variable set STAGING_API_URL --body "https://staging-api.example.com"
gh variable set PRODUCTION_API_URL --body "https://api.example.com"

In your gateway configuration (config/routes.oas.json), reference these through environment variables rather than hardcoding values:

JSONjson
{
  "paths": {
    "/v1/users": {
      "get": {
        "x-zuplo-route": {
          "handler": {
            "module": "$import(@zuplo/runtime)",
            "export": "urlRewriteHandler",
            "options": {
              "rewritePattern": "${env.UPSTREAM_URL}/users"
            }
          },
          "policies": {
            "inbound": []
          }
        }
      }
    }
  }
}

Environment-Specific Configuration

For more involved configuration differences, use Zuplo's environment system to manage settings per environment:

TypeScripttypescript
// modules/config.ts
import { environment } from "@zuplo/runtime";

export function getUpstreamUrl(): string {
  return environment.UPSTREAM_URL ?? "http://localhost:3000";
}

export function getRateLimitConfig() {
  const isProd = environment.ZUPLO_ENVIRONMENT_STAGE === "production";
  return {
    requestsPerMinute: isProd ? 100 : 1000,
    windowMs: 60_000,
  };
}

This pattern keeps your gateway logic identical across environments while allowing the operational parameters to vary. Development environments get generous rate limits for testing, while production enforces tighter controls.

Branch Preview Environments

Preview environments are one of the most powerful capabilities of a git-native API gateway. Every pull request gets its own live, isolated gateway deployment that reviewers and automated tests can interact with.

How Zuplo Preview Environments Work

When you open a pull request, Zuplo automatically deploys a preview environment with its own unique URL. This preview has the full gateway configuration from your branch, running against your configured upstream services.

This means reviewers can:

  • Send real HTTP requests to the preview gateway to verify new routes
  • Test authentication and authorization policies against a live endpoint
  • Verify that rate limiting behaves as expected
  • Confirm that request/response transformations produce the correct output

Why This Matters for API Testing

API changes are notoriously hard to review by reading configuration files alone. A route definition in JSON or YAML looks correct until you send a request and discover that a path parameter is not being passed through, or that a transformation drops a required header.

Preview environments turn API gateway reviews from "does this config look right" into "does this actually work." Your team can curl the preview URL, run automated test suites against it, or point a frontend development environment at it to test the full integration.

Terminalbash
# Test the preview environment directly
curl -H "Authorization: Bearer $TEST_TOKEN" \
  https://your-preview-abc123.zuplo.app/v1/users

# Run your API test suite against the preview
API_BASE_URL=https://your-preview-abc123.zuplo.app \
  npm run test:integration

This feedback loop catches issues that static analysis and configuration validation cannot detect. It moves the discovery of integration problems from staging (or worse, production) to the pull request stage.

Multi-Region Deployment

API gateways sit on the critical path of every request. Latency matters, and deploying to a single region means users on the other side of the world pay a round-trip penalty on every API call.

The Traditional Approach

With traditional API gateways, multi-region deployment is an infrastructure project. You provision gateway instances in each region, configure load balancers, manage health checks, handle configuration synchronization, and deal with the operational complexity of maintaining multiple deployments. Your CI/CD pipeline grows proportionally:

YAMLyaml
# The painful way -- deploying to each region individually
deploy-us-east:
  script: deploy --region us-east-1
deploy-eu-west:
  script: deploy --region eu-west-1
deploy-ap-southeast:
  script: deploy --region ap-southeast-1
# ... repeat for every region

Zuplo's Edge Deployment Model

Zuplo takes a fundamentally different approach. When you run npx zuplo deploy, your gateway configuration deploys to over 300 edge locations worldwide automatically. There is no region selection, no multi-region pipeline configuration, and no infrastructure to manage.

Terminalbash
# One command. 300+ locations. Every deployment.
npx zuplo deploy --environment production

Every deployment is global by default. A user in Tokyo, a user in London, and a user in Sao Paulo all hit the nearest edge location. Your CI/CD pipeline stays simple because multi-region is not a deployment concern -- it is built into the platform.

This architectural choice also simplifies your rollback story. A single git revert and redeploy updates every edge location simultaneously rather than requiring coordinated rollbacks across individual regional deployments.

Rollback Strategies

Even with thorough testing and preview environments, issues will occasionally reach production. Your rollback strategy determines whether this means minutes of downtime or hours of scrambling.

Git Revert and Redeploy

The simplest and most reliable rollback strategy for a git-native gateway is to revert the problematic commit and let the pipeline redeploy:

Terminalbash
# Identify the problematic commit
git log --oneline -10

# Revert it
git revert abc1234

# Push to trigger the pipeline
git push origin main

The pipeline deploys the reverted configuration through the same stages as any other change. This approach is fast, auditable, and uses the exact same deployment path as forward changes.

Blue-Green Deployments

For zero-downtime rollbacks, a blue-green pattern maintains two production environments. Traffic is routed to the active environment while the inactive one receives the new deployment. If the new deployment is healthy, traffic switches over. If not, traffic stays on the previous version.

YAMLyaml
deploy-production:
  steps:
    - name: Deploy to inactive environment
      run: |
        INACTIVE=$(get-inactive-environment)
        npx zuplo deploy --environment $INACTIVE

    - name: Health check
      run: |
        curl --fail https://$INACTIVE_URL/health

    - name: Switch traffic
      run: |
        switch-traffic --to $INACTIVE

Canary Deployments

Canary deployments route a small percentage of traffic to the new version while the majority continues hitting the previous version. If error rates or latency increase, the canary is rolled back before it affects most users.

This is particularly valuable for API gateways because you can monitor:

  • Error rates on the canary vs. the stable version
  • P50/P95/P99 latency differences
  • Upstream error rates that might indicate a misconfigured proxy rule

Start with a small traffic percentage (5-10%), monitor for a defined period, and gradually increase if metrics look healthy.

Testing in the Pipeline

Automated testing is what makes CI/CD pipelines trustworthy. Without tests, a pipeline is just automated deployment -- it moves code faster, including broken code. Here are the testing stages to integrate into your API gateway pipeline.

OpenAPI Validation

Your OpenAPI specification is the contract your API exposes to consumers. Validate it on every pull request to catch breaking changes early:

Terminalbash
# Validate the OpenAPI spec is structurally correct
npx @redocly/cli lint openapi.json

# Check for breaking changes against the main branch
npx @redocly/cli diff openapi.json --base main

Contract Testing

Contract tests verify that your gateway's actual behavior matches the OpenAPI specification. They send requests to a live gateway (the preview environment) and validate that responses conform to the documented schemas:

TypeScripttypescript
// tests/contract.test.ts
import { test, expect } from "@playwright/test";

test("GET /v1/users returns valid response", async ({ request }) => {
  const response = await request.get(`${process.env.API_BASE_URL}/v1/users`, {
    headers: {
      Authorization: `Bearer ${process.env.TEST_TOKEN}`,
    },
  });

  expect(response.status()).toBe(200);

  const body = await response.json();
  expect(body).toHaveProperty("data");
  expect(Array.isArray(body.data)).toBe(true);

  // Validate each user object has required fields
  for (const user of body.data) {
    expect(user).toHaveProperty("id");
    expect(user).toHaveProperty("email");
  }
});

Integration Tests Against Preview Environments

Run your full integration test suite against the preview environment in the PR pipeline. This validates the complete request flow: authentication, rate limiting, request transformation, upstream routing, and response handling.

YAMLyaml
# Add to your GitHub Actions PR workflow
test-preview:
  name: Integration Tests (Preview)
  needs: deploy-preview
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: "20"

    - name: Install dependencies
      run: npm ci

    - name: Run integration tests
      run: npm run test:integration
      env:
        API_BASE_URL: ${{ needs.deploy-preview.outputs.url }}

Policy Testing

Test your gateway policies in isolation to verify they behave correctly before deployment. For example, verify that rate limiting rejects requests after the threshold:

TypeScripttypescript
// tests/rate-limit.test.ts
import { test, expect } from "@playwright/test";

test("rate limiting enforces request threshold", async ({ request }) => {
  const baseUrl = process.env.API_BASE_URL;

  // Send requests up to the limit
  for (let i = 0; i < 10; i++) {
    const response = await request.get(`${baseUrl}/v1/health`);
    expect(response.status()).toBe(200);
  }

  // The next request should be rate limited
  const limited = await request.get(`${baseUrl}/v1/health`);
  expect(limited.status()).toBe(429);
});

Get Started with Zuplo

If you are still configuring your API gateway through a dashboard, you are leaving reliability on the table. Every manual change is a risk, every environment inconsistency is a future incident, and every undocumented modification is a compliance gap.

Zuplo is built for the workflow described in this guide. Your API gateway configuration lives in Git, deploys through your existing CI/CD pipelines, creates preview environments for every pull request, and deploys to 300+ edge locations on every push. There is no separate infrastructure to manage and no dashboard configuration to drift.

Sign up for Zuplo and deploy your first git-native API gateway in minutes. Your CI/CD pipeline will thank you.

Tags:#API Gateway#API Best Practices

Related Articles

Continue learning from the Zuplo Learning Center.

API Key Authentication

How to Implement API Key Authentication: A Complete Guide

Learn how to implement API key authentication from scratch — generation, secure storage, validation, rotation, and per-key rate limiting with practical code examples.

API Documentation

Developer Portal Comparison: Customization, Documentation, and Self-Service

Compare developer portal platforms — Zuplo/Zudoku, ReadMe, Redocly, Stoplight, and SwaggerHub — across customization, auto-generated docs, self-service API keys, and theming.

On this page

IntroductionWhy CI/CD for API Gateways MattersPipeline ArchitectureGitHub Actions TemplateGitLab CI TemplateMulti-Environment DeploymentBranch Preview EnvironmentsMulti-Region DeploymentRollback StrategiesTesting in the PipelineGet Started with Zuplo

Scale your APIs with
confidence.

Start for free or book a demo with our team.
Book a demoStart for Free
SOC 2 TYPE 2High Performer Spring 2025Momentum Leader Spring 2025Best Estimated ROI Spring 2025Easiest To Use Spring 2025Fastest Implementation Spring 2025

Get Updates From Zuplo

Zuplo logo
© 2026 zuplo. All rights reserved.
Products & Features
API ManagementAI GatewayMCP ServersMCP GatewayDeveloper PortalRate LimitingOpenAPI NativeGitOpsProgrammableAPI Key ManagementMulti-cloudAPI GovernanceMonetizationSelf-Serve DevX
Developers
DocumentationBlogLearning CenterCommunityChangelogIntegrations
Product
PricingSupportSign InCustomer Stories
Company
About UsMedia KitCareersStatusTrust & Compliance
Privacy PolicySecurity PoliciesTerms of ServiceTrust & Compliance
Docs
Pricing
Sign Up
Login
ContactBook a demoFAQ
Zuplo logo
DocsPricingSign Up
Login