ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop on the web 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
Development
Policies
Handlers
API Keys
Rate Limiting
MCP Server
MCP Gateway
AI Gateway
Developer Portal
Monetization
Deploying & Source Control
    Overview
    GitHub
      SetupTesting 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 LimitsSecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Custom CI/CD

GitHub Actions: Automatic Cleanup

Delete Zuplo environments automatically when branches are deleted. This keeps your environment list clean and avoids accumulating unused preview environments.

.github/workflows/cleanup-on-branch-delete.yaml
name: Cleanup on Branch Delete on: delete: jobs: cleanup: # Only run for branch deletions, not tag deletions if: github.event.ref_type == 'branch' runs-on: ubuntu-latest env: ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: npm install - name: Delete environment run: | # The deleted branch name BRANCH_NAME="${{ github.event.ref }}" # Deployment names use the format {project}-{branch}-{hash}, where # the branch segment is normalized (lowercase, special characters # replaced by hyphens) and truncated to 10 characters ENV_NAME=$(echo "$BRANCH_NAME" | tr '/_' '--' | tr '[:upper:]' '[:lower:]' | cut -c1-10 | sed 's/-*$//') # Find the deployment for the deleted branch and delete it by URL npx zuplo list --api-key "$ZUPLO_API_KEY" \ --output json --show-details | jq -r --arg env "$ENV_NAME" \ '.[] | select(.name | test("-" + $env + "-[a-z0-9]+$")) | .url' | while read -r URL; do echo "Deleting environment: $URL" npx zuplo delete --url "$URL" --api-key "$ZUPLO_API_KEY" --wait || true done

This workflow:

  1. Triggers when any branch is deleted
  2. Converts the branch name to the deployment-name branch segment format
  3. Looks up the matching deployment and deletes it by URL
  4. Continues without error if no matching environment exists

Combining with PR Cleanup

Use this as a backup for PR preview environments. The PR workflow handles cleanup when PRs close, but this catches cases where:

  • Someone deletes a branch without closing the PR first
  • A branch was pushed but never had a PR opened
  • The PR cleanup job failed

Scheduled Cleanup

For additional safety, run periodic cleanup to catch any orphaned environments:

.github/workflows/scheduled-cleanup.yaml
name: Scheduled Cleanup on: schedule: # Run daily at midnight UTC - cron: "0 0 * * *" jobs: cleanup: runs-on: ubuntu-latest env: ZUPLO_API_KEY: ${{ secrets.ZUPLO_API_KEY }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch all branches - uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: npm install - name: Cleanup stale environments run: | # Get all remote branches, converted to the deployment-name branch # segment format: lowercase, special characters replaced by hyphens, # truncated to 10 characters BRANCHES=$(git branch -r | sed 's|origin/||' | tr -d ' ' | tr '/_' '--' | tr '[:upper:]' '[:lower:]' | cut -c1-10 | sed 's/-*$//') # List deployed environments as JSON. Each entry has the shape # {"projectName": "...", "name": "...", "url": "..."} npx zuplo list --api-key "$ZUPLO_API_KEY" \ --output json --show-details > environments.json # Deployment names follow the format {project}-{branch}-{hash} jq -r '.[] | "\(.name) \(.url)"' environments.json | while read -r NAME URL; do # Skip protected environments case "$NAME" in *-main-* | *-production-* | *-staging-*) continue ;; esac # Delete only if no branch matches the deployment name STALE=true for BRANCH in $BRANCHES; do if [[ "$NAME" == *"-$BRANCH-"* ]]; then STALE=false break fi done if [[ "$STALE" == "true" ]]; then echo "Deleting stale environment: $NAME" npx zuplo delete --url "$URL" --api-key "$ZUPLO_API_KEY" --wait || true fi done

Next Steps

  • Set up PR preview environments with built-in cleanup
  • Implement multi-stage deployment for production
Edit this page
Last modified on June 10, 2026
Multi-Stage DeploymentGitLab
On this page
  • Combining with PR Cleanup
  • Scheduled Cleanup
  • Next Steps
YAML
YAML