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, deploying to Kubernetes via Jenkins, and managing API proxies on Apinizer.

Technologies and Versions

TechnologyVersion
Jenkins2.541.1
Apinizerv2026.01.5
GitHub Actions
Kubernetesv1.31.6

Pipeline Flow

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

Architecture Overview

This integration scenario uses the following components:
  • GitHub Actions: Docker image building and versioning are handled on the GitHub side.
  • Jenkins: Orchestration and deployment management run on your Jenkins server.
  • Kubernetes: Container orchestration runs on your 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:
  • Generates a new semantic version
  • Builds the Docker image and pushes it to Docker Hub
  • Triggers the Jenkins pipeline

Workflow File

The workflow file must be placed in your GitHub repository. Create the following file in your local development environment (IDE or text editor) and push it to your repo: .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 generation
      - 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 }}
      
      # Trigger Jenkins
      - 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
GITHUB_TOKEN is a temporary token automatically generated by GitHub Actions and does not need to be added manually to Secrets and Variables. The anothrNick/github-tag-action used in the workflow automatically uses this token to push version tags to the repository.

2. Jenkins Pipeline Configuration

The Jenkins pipeline performs the following stages:
  1. Kubernetes Deployment: 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 in the Jenkins UI. Use the following content in the Pipeline script field in the Jenkins UI.
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('Kubernetes Deployment') {
            steps {
                echo "Deploying ${DOCKER_IMAGE}:${params.IMAGE_TAG} to Kubernetes..."
                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 Kubernetes and Apinizer successfully."
        }
        failure {
            echo "❌ Pipeline failed! Check logs above."
        }
    }
}

Jenkins Credentials Configuration

Add the following credential from Manage Jenkins > Credentials:
Credential IDKindDescription
APINIZER_TOKENSecret textApinizer API token
For detailed information on generating 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 kubectl set image and the rollout status is verified.
${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 has been deployed successfully. This step is critical to ensure the API is up and running before deploying to Apinizer. The API_HEALTH_ENDPOINT variable specifies the health check endpoint of the application being deployed. This address is specific to your application; for example:
https://api.example.com/health
If your application uses a different health check endpoint, update the API_HEALTH_ENDPOINT variable accordingly.

Stage 3: Apinizer API Proxy Sync

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

Update 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 more details, refer to the Update API Proxy API reference.

Create 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 more details, refer to the Create API Proxy from URL API reference.
The deploy: false parameter prevents the proxy from being automatically deployed. This ensures controlled deployment is performed 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 more details, refer to the Deploy API Proxy API reference.

Configuration Parameters

Environment Variables

The key environment variables used in the pipeline:
VariableDescriptionExample Value
NAMESPACEKubernetes namespaceexample-service
DEPLOYMENT_NAMEKubernetes deployment nameexample-service
CONTAINER_NAMEKubernetes container namehealth-api
DOCKER_IMAGEDocker image name (username/image)myusername/health-api
KUBECTL_PATHPath to kubectl binary/usr/local/bin/kubectl
API_BACKEND_URLBase URL of the deployed applicationhttps://api.example.com
API_HEALTH_ENDPOINTApplication health check endpointhttps://api.example.com/health
API_SPEC_URLOpenAPI specification URLhttps://api.example.com/api/v1/openapi.json
APINIZER_BASE_URLApinizer platform URLhttps://qa.apinizer.com
APINIZER_GATEWAY_URLApinizer gateway URLhttps://apiqa.apinizer.com/apigateway
APINIZER_PROJECTApinizer project nameprod-cid
APINIZER_ENVIRONMENTTarget deployment environmenttester
APINIZER_PROXY_NAMEAPI Proxy namehealth-api-proxy
APINIZER_PROXY_PATHRelative path on the gateway/health-api

Apinizer API Endpoints

The 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 to Your Own Setup

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

Deployment Mechanism

If you’re using a different orchestrator or direct VM deployment instead of Kubernetes, replace the Kubernetes Deployment stage with your own deployment method.
2

Health Check

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

OpenAPI Spec

Make sure your API’s OpenAPI specification is accessible at a reachable URL. This URL must be accessible 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 secure approach for pipeline security.

Conclusion

This guide has shown 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.