Skip to main content

Overview

This guide shows you how to automatically deploy your APIs to Apinizer using a modern CI/CD pipeline. The example scenario covers building Docker images with GitHub Actions, Kubernetes deployment with Jenkins, and API proxy management on Apinizer.

Technologies and Versions Used

TechnologyVersion
Jenkinsjenkins/jenkins:lts
Apinizerv2026.01.5
GitHub Actions
KubernetesDepends on your environment

Pipeline Flow

GitHub Push → GitHub Actions (Build & Tag) → Jenkins Trigger → 
Kubernetes Deploy → Health Check → Apinizer API Proxy Sync → Apinizer Deploy

Architecture Overview

This integration scenario uses the following components:
  • GitHub Actions: Docker image build and versioning operations are handled on the GitHub side.
  • Jenkins: Orchestration and deployment management runs on your Jenkins server.
  • Kubernetes: Runs on your container orchestration cluster.
  • Apinizer: API Gateway and API proxy management is handled through your Apinizer instance.

1. GitHub Actions Workflow

On every push to the main branch, GitHub Actions automatically:
  • Creates a new semantic version
  • Builds a Docker image and pushes it to Docker Hub
  • Triggers the Jenkins pipeline

Workflow File

The workflow file must be located in your project’s GitHub repository. Create the following file path in your local development environment (IDE or text editor) and push it to your repository: .github/workflows/docker-build-push.yml
name: Docker Build & Push
on:
  push:
    branches: [ "main" ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: '0'
      
      # Automatic version creation
      - name: Bump version and push tag
        id: tag_version
        uses: anothrNick/github-tag-action@1.64.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          WITH_V: true
          DEFAULT_BUMP: patch
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      # Docker build and push
      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ secrets.DOCKERHUB_USERNAME }}/YOUR_IMAGE_NAME:latest
            ${{ secrets.DOCKERHUB_USERNAME }}/YOUR_IMAGE_NAME:${{ steps.tag_version.outputs.new_tag }}
      
      # Jenkins trigger
      - name: Trigger Jenkins Pipeline
        run: |
          curl -X POST ${{ secrets.JENKINS_URL }}/job/${{ secrets.JENKINS_JOB_NAME }}/buildWithParameters \
            --user ${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }} \
            --data IMAGE_TAG=${{ steps.tag_version.outputs.new_tag }}

GitHub Secrets Configuration

Add the following secrets from Repository Settings > Secrets and Variables > Actions:
SecretDescription
DOCKERHUB_USERNAMEYour Docker Hub username
DOCKERHUB_TOKENDocker Hub access token
JENKINS_URLYour Jenkins instance URL
JENKINS_JOB_NAMEJenkins job name
JENKINS_USERJenkins username
JENKINS_TOKENJenkins API token

2. Jenkins Pipeline Configuration

The Jenkins pipeline performs the following stages:
  1. K8s Deploy: Deploys the new Docker image to Kubernetes
  2. Health Check: Verifies the API is running correctly
  3. Sync Apinizer API Proxy: Updates or creates the API Proxy
  4. Deploy to Apinizer: Deploys the proxy to the specified environment

Jenkinsfile

The Jenkins pipeline can be defined by creating a new Pipeline job through the Jenkins UI. You can use the following content in the Pipeline script field in the Jenkins UI. Jenkinsfile
pipeline {
    agent any
    
    parameters {
        string(name: 'IMAGE_TAG', defaultValue: 'latest', description: 'Docker image tag from GitHub Actions')
    }
    
    environment {
        // Kubernetes configuration
        NAMESPACE = "YOUR_NAMESPACE" 
        DEPLOYMENT_NAME = "YOUR_DEPLOYMENT_NAME"
        CONTAINER_NAME = "YOUR_CONTAINER_NAME" 
        DOCKER_IMAGE = "YOUR_DOCKERHUB_USERNAME/YOUR_IMAGE_NAME"
        KUBECTL_PATH = "/usr/local/bin/kubectl"
        
        // API information
        API_BACKEND_URL = "YOUR_BACKEND_URL"
        API_HEALTH_ENDPOINT = "${API_BACKEND_URL}/health"
        API_SPEC_URL = "${API_BACKEND_URL}/api/v1/openapi.json"
        
        // Apinizer configuration
        APINIZER_BASE_URL = "YOUR_APINIZER_URL"
        APINIZER_GATEWAY_URL = "YOUR_APINIZER_GATEWAY_URL"
        APINIZER_PROJECT = "YOUR_PROJECT_NAME"
        APINIZER_ENVIRONMENT = "YOUR_ENVIRONMENT_NAME"
        APINIZER_PROXY_NAME = "YOUR_PROXY_NAME"
        APINIZER_PROXY_PATH = "YOUR_PROXY_RELATIVE_PATH"
    }
    
    stages {
        stage('K8s Deploy') {
            steps {
                echo "Deploying ${DOCKER_IMAGE}:${params.IMAGE_TAG} to K8s..."
                sh """
                    ${KUBECTL_PATH} set image deployment/${DEPLOYMENT_NAME} \
                        ${CONTAINER_NAME}=${DOCKER_IMAGE}:${params.IMAGE_TAG} \
                        -n ${NAMESPACE}
                    
                    ${KUBECTL_PATH} rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE}
                """
            }
        }
        
        stage('Health Check') {
            steps {
                echo "Checking API health at ${API_HEALTH_ENDPOINT}..."
                sh """
                    sleep 10
                    curl -f ${API_HEALTH_ENDPOINT}
                """
            }
        }
        
        stage('Sync Apinizer API Proxy') {
            steps {
                withCredentials([string(credentialsId: 'APINIZER_TOKEN', variable: 'TOKEN')]) {
                    script {
                        echo "Checking if API proxy exists..."
                        
                        def proxyCheckCode = sh(
                            script: """
                                curl -s -o /dev/null -w '%{http_code}' \
                                    -H 'Authorization: Bearer ${TOKEN}' \
                                    '${APINIZER_BASE_URL}/apiops/projects/${APINIZER_PROJECT}/apiProxies/${APINIZER_PROXY_NAME}/'
                            """,
                            returnStdout: true
                        ).trim()
                        
                        echo "Proxy check returned: ${proxyCheckCode}"
                        def proxyExists = (proxyCheckCode == '200')
                        
                        if (proxyExists) {
                            echo "API proxy exists, updating spec..."
                            sh """
                                curl -X PUT \
                                    '${APINIZER_BASE_URL}/apiops/projects/${APINIZER_PROJECT}/apiProxies/url/' \
                                    -H 'Authorization: Bearer ${TOKEN}' \
                                    -H 'Content-Type: application/json' \
                                    -d '{
                                        "apiProxyName": "${APINIZER_PROXY_NAME}",
                                        "apiProxyCreationType": "OPEN_API",
                                        "specUrl": "${API_SPEC_URL}",
                                        "clientRoute": {
                                            "relativePathList": ["${APINIZER_PROXY_PATH}"]
                                        },
                                        "reParse": true,
                                        "deploy": false
                                    }'
                            """
                        } else {
                            echo "API proxy doesn't exist, creating..."
                            sh """
                                curl -X POST \
                                    '${APINIZER_BASE_URL}/apiops/projects/${APINIZER_PROJECT}/apiProxies/url/' \
                                    -H 'Authorization: Bearer ${TOKEN}' \
                                    -H 'Content-Type: application/json' \
                                    -d '{
                                        "apiProxyName": "${APINIZER_PROXY_NAME}",
                                        "apiProxyDescription": "Auto-generated proxy",
                                        "apiProxyCreationType": "OPEN_API",
                                        "specUrl": "${API_SPEC_URL}",
                                        "clientRoute": {
                                            "relativePathList": ["${APINIZER_PROXY_PATH}"]
                                        },
                                        "routingInfo": {
                                            "routingAddressList": [
                                                {
                                                    "address": "${API_BACKEND_URL}",
                                                    "weight": 100
                                                }
                                            ]
                                        },
                                        "deploy": false,
                                        "reParse": false
                                    }'
                            """
                        }
                    }
                }
            }
        }
        
        stage('Deploy to Apinizer') {
            steps {
                withCredentials([string(credentialsId: 'APINIZER_TOKEN', variable: 'TOKEN')]) {
                    echo "Deploying API proxy to Apinizer ${APINIZER_ENVIRONMENT}..."
                    sh """
                        curl -X POST \
                            '${APINIZER_BASE_URL}/apiops/projects/${APINIZER_PROJECT}/apiProxies/${APINIZER_PROXY_NAME}/environments/${APINIZER_ENVIRONMENT}/' \
                            -H 'Authorization: Bearer ${TOKEN}'
                    """
                }
            }
        }
    }
    
    post {
        success {
            echo "✅ Pipeline completed! API deployed to K8s and Apinizer successfully."
        }
        failure {
            echo "❌ Pipeline failed! Check logs above."
        }
    }
}

Jenkins Credentials Configuration

Add the following credential from Manage Jenkins > Credentials:
Credential IDTypeDescription
APINIZER_TOKENSecret textApinizer API token
For detailed information on creating an Apinizer API token, refer to the Token Retrieval Methods documentation.

3. Pipeline Stage Details

Stage 1: Kubernetes Deployment

This stage runs on the Jenkins server. The Docker image built by GitHub Actions is deployed to the Kubernetes cluster. The deployment is updated using the kubectl set image command and the rollout status is monitored.
${KUBECTL_PATH} set image deployment/${DEPLOYMENT_NAME} \
    ${CONTAINER_NAME}=${DOCKER_IMAGE}:${params.IMAGE_TAG} \
    -n ${NAMESPACE}

${KUBECTL_PATH} rollout status deployment/${DEPLOYMENT_NAME} -n ${NAMESPACE}

Stage 2: Health Check

The health endpoint is checked to verify that the API was deployed successfully. This step is critical to ensure the API is running before deploying to Apinizer. The API_HEALTH_ENDPOINT variable specifies the health check endpoint of the application you are deploying. This address is specific to your application, for example:
https://YOUR_BACKEND_URL/health
If your application uses a different health check endpoint, update the API_HEALTH_ENDPOINT variable accordingly.

Stage 3: Apinizer API Proxy Synchronization

In this critical stage:
  1. Proxy Check: Verifies whether the relevant API Proxy exists
  2. Update or Create:
    • If proxy exists: Updates the OpenAPI spec (PUT request)
    • If proxy doesn’t exist: Creates a new proxy (POST request)

Updating a Proxy (PUT)

When updating an existing proxy, the OpenAPI specification is re-parsed using the reParse: true parameter:
{
  "apiProxyName": "YOUR_PROXY_NAME",
  "apiProxyCreationType": "OPEN_API",
  "specUrl": "YOUR_BACKEND_URL/api/v1/openapi.json",
  "clientRoute": {
    "relativePathList": ["YOUR_PROXY_RELATIVE_PATH"]
  },
  "reParse": true,
  "deploy": false
}
For detailed information, refer to the Update API Proxy API reference.

Creating a New Proxy (POST)

When creating a new proxy, backend routing information is also defined:
{
  "apiProxyName": "YOUR_PROXY_NAME",
  "apiProxyDescription": "Auto-generated proxy",
  "apiProxyCreationType": "OPEN_API",
  "specUrl": "YOUR_BACKEND_URL/api/v1/openapi.json",
  "clientRoute": {
    "relativePathList": ["YOUR_PROXY_RELATIVE_PATH"]
  },
  "routingInfo": {
    "routingAddressList": [
      {
        "address": "YOUR_BACKEND_URL",
        "weight": 100
      }
    ]
  },
  "deploy": false
}
For detailed information, refer to the Create API Proxy from URL API reference.
With the deploy: false parameter, the proxy is not automatically deployed. This ensures a controlled deployment in the next stage.

Stage 4: Deploy to Apinizer Environment

In the final stage, the updated or newly created proxy is deployed to the specified environment using Apinizer’s deployment API:
POST /apiops/projects/{projectName}/apiProxies/{proxyName}/environments/{environmentName}/
For detailed information, refer to the Deploy API Proxy API reference.

Configuration Parameters

Environment Variables

Key environment variables used in the pipeline:
VariableDescriptionExample Value
APINIZER_BASE_URLApinizer platform URLYOUR_APINIZER_URL
APINIZER_PROJECTApinizer project nameYOUR_PROJECT_NAME
APINIZER_ENVIRONMENTTarget deployment environmentYOUR_ENVIRONMENT_NAME
APINIZER_PROXY_NAMEAPI Proxy nameYOUR_PROXY_NAME
APINIZER_PROXY_PATHRelative path on the gatewayYOUR_PROXY_RELATIVE_PATH
API_SPEC_URLOpenAPI specification URLYOUR_BACKEND_URL/openapi.json

Apinizer API Endpoints

Apinizer API endpoints used in this pipeline:
OperationEndpoint
Proxy CheckGET /apiops/projects/{project}/apiProxies/{proxyName}/
Update ProxyPUT /apiops/projects/{project}/apiProxies/url/
Create ProxyPOST /apiops/projects/{project}/apiProxies/url/
Environment DeployPOST /apiops/projects/{project}/apiProxies/{proxyName}/environments/{env}/
For detailed information about the Apinizer Management API, refer to the API Overview documentation.

Adapting the Pipeline for Your Own Use

This example scenario is designed for an API running on Kubernetes. To adapt it to your own infrastructure:
1

Deployment Mechanism

If you use a different orchestrator instead of Kubernetes or direct VM deployment, replace the K8s Deploy stage with your own deployment method.
2

Health Check

If your API uses a different health check mechanism, update the API_HEALTH_ENDPOINT variable with the relevant endpoint.
3

OpenAPI Spec

Make sure your API’s OpenAPI specification is available at an accessible URL. This URL must be reachable by Apinizer.
4

Environment

Specify the environment you want to use in Apinizer (dev, test, prod, etc.) in the APINIZER_ENVIRONMENT variable.
5

Security

Managing your credentials through Jenkins Credentials Manager is a more appropriate approach from a pipeline security standpoint.

Conclusion

This guide has demonstrated how to set up a fully automated CI/CD pipeline using GitHub Actions, Jenkins, and Apinizer. Every code change is automatically built and deployed to Apinizer. This approach minimizes manual operations, speeds up your deployment process, and reduces the likelihood of errors.