ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
    Develop in the portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
API Management
AI Gateway
MCP Gateway
MCP Server
Developer Portal
Development
Deploying & Source Control
    Overview
    GitHub
      SetupTest deployments
      Custom CI/CD
        Basic DeploymentDeploy and testPR Preview EnvironmentsLocal testing in CITag-Based ReleasesMulti-Stage DeploymentAutomatic Cleanup
    GitLab
    Bitbucket
    Azure DevOps
    CircleCI
    Custom CI/CDMonorepo DeploymentTroubleshooting DeploymentsRename/Move Project
Analytics
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
powered by Zuplo
Custom CI/CD

GitHub Actions: Local testing in CI

Run tests against a local Zuplo development server before deploying anywhere. Catch issues earlier and avoid deploying broken changes.

Code
name: Local Test Then Deploy on: push: branches: - main pull_request: jobs: local-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 - name: Install dependencies run: npm install - name: Start local server and run tests run: | # Start the local dev server in the background npx zuplo dev & DEV_PID=$! # Poll until the server answers, with a hard deadline. Never use a # fixed sleep: it is flaky when the runner is slow and wasted CI # minutes when it is fast. echo "Waiting for local server to start..." deadline=$((SECONDS + 60)) until curl --fail --silent --output /dev/null http://localhost:9000/health; do if (( SECONDS >= deadline )); then echo "Local server did not start within 60s" >&2 kill $DEV_PID exit 1 fi sleep 1 done # Run tests against local server npx zuplo test --endpoint http://localhost:9000 # Stop the dev server kill $DEV_PID deploy: needs: local-test runs-on: ubuntu-latest # Only deploy on push to main, not on PRs if: github.event_name == 'push' && github.ref == 'refs/heads/main' env: ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 - name: Install dependencies run: npm install - name: Deploy to Zuplo run: npx zuplo deploy --api-key "$ZUPLO_API_KEY" --environment "${{ github.ref_name }}"

This workflow:

  1. Starts a local Zuplo server in the CI environment.
  2. Polls it until it answers, with a bounded deadline.
  3. Runs your test suite against localhost.
  4. Proceeds to deployment only if local tests pass.
  5. Deploys to Zuplo (only on pushes to main).

The poll replaces a fixed sleep. A sleep is a guess about startup time that is wrong in both directions, and it is the most common reason a gateway test suite "needs" a retry. The example polls /health — add an unauthenticated health route if your gateway does not have one. See health checks.

Why test locally first

  • Faster feedback — local tests run without waiting for deployment.
  • Catch syntax errors — the local server validates your configuration.
  • Test policies — verify authentication, rate limiting, and other policies work correctly.
  • No wasted deployments — don't deploy changes that fail tests.

Combine with remote tests

For maximum confidence, test both locally and against the deployed environment:

Code
jobs: local-test: # ... local testing job ... deploy-and-test: needs: local-test # ... deploy and run tests against live environment ...

Next steps

  • Add PR preview environments for review
  • Set up multi-stage deployment with staging
Edit this page
Last modified on August 21, 2026
PR Preview EnvironmentsTag-Based Releases
On this page
  • Why test locally first
  • Combine with remote tests
  • Next steps
YAML
YAML