Overview
This guide demonstrates how to set up a CI/CD infrastructure in which every change pushed to themain branch of an API project is automatically tested, produces a Docker image, and is sequentially deployed to four Kubernetes environments (dev → test → UAT → prod). Each deployment step is synchronized with the corresponding API proxy on Apinizer.
Technologies and Versions Used
| Technology | Version |
|---|---|
| Jenkins | jenkins/jenkins:lts |
| GitHub Actions | — |
| Kubernetes + Kustomize | v1.32.9 - v5.5.0 |
| Apinizer | v2026.01.5 |
Pipeline Flow
Project Structure
1. GitHub Actions — CI Pipeline
The CI pipeline is triggered on every push to themain branch. It contains two jobs: tests run first, and the build does not start until the tests pass.
Workflow File
Create the following content as.github/workflows/ci.yaml and push it to your repository.
If the Jenkins job name contains spaces, it must be URL-encoded using
urllib.parse.quote(). Otherwise, the curl request will be sent incorrectly.GitHub Secrets Configuration
Define the following secrets under the repository’s Settings > Secrets and Variables > Actions screen:| Secret | Description |
|---|---|
DOCKERHUB_USERNAME | Your Docker Hub username |
DOCKERHUB_TOKEN | Docker Hub access token |
JENKINS_URL | Your Jenkins instance URL |
JENKINS_JOB_NAME | Name of the CD job in Jenkins |
JENKINS_USER | Jenkins username |
JENKINS_TOKEN | Jenkins API token |
2. Kubernetes Infrastructure
Base Manifests
Thek8s/base/ directory contains the Deployment and Service manifests shared by all environments. The ENVIRONMENT value is passed to the application through a separate ConfigMap in each environment.
Kustomize Overlays
Each environment has its ownk8s/overlays/<env>/kustomization.yaml file. This file defines the environment-specific namespace, ConfigMap values, and the active image tag. Jenkins automatically updates the newTag value after every successful deployment.
test, uat, and prod overlays; use a different namespace and NodePort value for each.
3. Jenkins — Main CD Pipeline
Trigger: Invoked from GitHub Actions via abuildWithParameters call, with the VERSION_TAG parameter.
Jenkins Job Setup
Create a new Pipeline job
In Jenkins, select New Item > Pipeline. Enter a name that represents your CD pipeline as the job name.
SCM configuration
Under the Pipeline tab, select Pipeline script from SCM. Choose Git as the SCM, enter your repository URL, and specify the branch as
*/main. In the Script Path field, enter jenkins/Jenkinsfile.Shared Library Configuration
This pipeline uses shared library functions (retryWithDelay, apinizerProxySync, apinizerPromoteProd, smokeTest). To register the library with Jenkins:
Open the Global Pipeline Libraries screen
Go to the Global Pipeline Libraries section under Manage Jenkins > System.
Add the library
Click the Add button. Enter the library name in the Name field (e.g.,
shared-lib). Set the Default version to main. Select Modern SCM as the retrieval method and point it to your project repository.Jenkinsfile
Each Deploy stage performs the following three operations sequentially within a singlesh block: update the image tag with kustomize → apply to the cluster → after the rollout completes, push to Git with a [skip ci] commit. The withCredentials block wraps both the kubeconfig and the Git credentials at the same time. Because cd k8s/overlays/<env> is used, git add uses a relative path.
Pipeline Stage Details
Validate Parameters: Verifies that theVERSION_TAG parameter is not empty. This safeguard is critical in case Jenkins is triggered manually (bypassing GitHub Actions) with an empty parameter.
Checkout: The repository containing the Jenkinsfile is checked out. This step is mandatory so that subsequent stages can operate on the Kustomize files under k8s/overlays/.
Deploy (for each environment): The withCredentials block wraps both the kubeconfig and Git credentials simultaneously. Within a single sh block, the following happens in order: kustomize edit set image updates the newTag in the overlay, kubectl apply -k . applies it to the cluster, and after the rollout completes, git add kustomization.yaml → commit → push is performed. Thanks to the [skip ci] tag, GitHub Actions is not re-triggered.
Apinizer Sync (dev / test / uat): The apinizerProxySync shared library function creates or updates the proxy in Apinizer and deploys it to the corresponding environment. The retryWithDelay wrapper applies 3 attempts × 15-second wait for transient errors.
Apinizer Promote Prod: For production, the proxy is not deployed from scratch. The apinizerPromoteProd function moves the configuration approved in UAT to production via Apinizer’s promotion API.
Approve (before Test / UAT / Prod): At each environment transition, the Jenkins pipeline pauses; an authorized user approves through the Jenkins UI to continue.
Smoke Test: After deployment, kubectl rollout status and a /health endpoint check are performed. For details, see the Shared Library — smokeTest section.
4. Jenkins — Targeted Deploy Pipeline
Used to deploy a specific version to a single environment. Preferred for hotfix or rollback scenarios without running the full CI/CD flow.Jenkins Job Setup
Install the required plugin
In Jenkins, install the Active Choices plugin from Manage Jenkins > Plugins. Dynamic parameter dropdowns will not work without this plugin.
Create a new Pipeline job
Select New Item > Pipeline. Enter a job name such as
targeted-deploy or any name you prefer.SCM configuration
Select Pipeline script from SCM, use the same repository and the
*/main branch. Script Path: jenkins/Jenkinsfile.targeted.Dynamic Parameters
The targeted deploy job uses three dynamic parameters. The Active Choices plugin must be installed for these parameters to be defined. ENVIRONMENT (Active Choices Parameter): A dropdown from which the user selects the target environment.newTag value is read from the selected environment’s kustomization.yaml file to show the “currently deployed version” info. A timestamp query parameter is added to bypass caching.
Jenkinsfile.targeted
Unlike the main pipeline, the git operations in this pipeline are performed in a separateUpdate Git stage and run before the cluster changes. kustomize edit and git add/commit are in separate sh blocks; git push is placed in its own withCredentials block to narrow the credential scope. Because git add runs at the workspace root, it uses the full path. The Deploy stage contains no git operations; only kubectl apply and, on failure, an automatic rollback.
Pipeline Stage Details
Validate: In addition to checking parameters, it verifies that the selected tag actually exists by sending a request to the Docker Hub API. This prevents deploying a nonexistent tag at this stage. Update Git: Two separatesh blocks run: the first updates the overlay with kustomize edit set image; the second performs git add → commit. git add uses the full path (k8s/overlays/${ENVIRONMENT}/kustomization.yaml) because it runs at the workspace root. git push is placed in a separate withCredentials block to narrow the credential scope. The Git change is made before the cluster change; this way, even if the pipeline fails, Git reflects the deployment attempt.
Deploy: Only kubectl apply -k . and kubectl rollout status run; there are no git operations in this stage. If the rollout fails, kubectl rollout undo automatically reverts to the previous Kubernetes deployment. When a rollback occurs, the Apinizer sync does not run; the proxy continues to point to the previous version.
Apinizer Sync: Based on the target environment, the correct port and Apinizer project name are selected dynamically, and then apinizerProxySync is called.
5. Shared Library Functions
The Jenkins shared library contains four functions underjenkins/shared-library/vars/. Because the library is configured implicitly under Global Pipeline Libraries in Jenkins, there is no need to add an @Library(...) directive to the Jenkinsfile; the functions can be used directly in all pipeline jobs.
apinizerProxySync
Creates or updates the proxy in Apinizer, then deploys it to the relevant environment. Flow:export/ endpoint before the update and archived as a Jenkins artifact. This allows a manual restore through the Apinizer UI after a failed update. During the update, the existing relativePathList value is read from the API and preserved; it is not written manually.
Proxy creation payload:
For more details, see the Create API Proxy from URL and Update API Proxy references.
apinizerPromoteProd
Moves the proxy configuration from the UAT environment to production using Apinizer’s promotion mechanism. The proxy is not deployed from scratch; it is copied over a previously defined mapping.Promotion mappings must be defined in advance in the Apinizer UI. The
mappingNames parameter references these mappings. For more information, see the Creating API Mappings reference.smokeTest
Performs a two-step verification: first, it checks that the deployment has completed withkubectl rollout status, waits 5 seconds, and then sends up to 3 requests to the /health endpoint. A 5-second wait is applied between attempts. A response of HTTP 200 is considered successful.
retryWithDelay
Retries any closure for the specified number of attempts and wait interval. If all attempts fail, the last error is thrown and the stage is marked as failure.6. Jenkins Credentials Configuration
Define the following credentials under Manage Jenkins > Credentials > System > Global credentials:| Credential ID | Type | Description |
|---|---|---|
apinizer-management-url | Secret text | Apinizer Management API base URL |
YOUR_APINIZER_TOKEN_CREDENTIAL_ID | Secret text | Apinizer API Bearer token |
kubeconfig | Secret file | Kubernetes cluster kubeconfig file |
github-credentials | Username with password | GitHub username and Personal Access Token (repo write permission required) |
For more information on generating an Apinizer API token, see the Token Retrieval Methods documentation.
7. Apinizer API Endpoint Reference
Apinizer Management API endpoints used in this pipeline:| Operation | Method | Endpoint |
|---|---|---|
| Proxy existence check | GET | /apiops/projects/{project}/apiProxies/{proxyName}/ |
| Proxy export (snapshot) | GET | /apiops/projects/{project}/apiProxies/{proxyName}/export/ |
| Proxy creation | POST | /apiops/projects/{project}/apiProxies/url/ |
| Proxy update | PUT | /apiops/projects/{project}/apiProxies/url/ |
| Deploy to environment | POST | /apiops/projects/{project}/apiProxies/{proxyName}/environments/{env}/ |
| Production promotion | POST | /apiops/promotion/executions |
For more information on the Apinizer Management API, see the API Overview documentation.
Adapting Your Own Pipeline
Docker Hub and image name
Update the
IMAGE_BASE variable with your own Docker Hub username and image name. Edit the IMAGE_NAME value in the CI workflow the same way.Kubernetes configuration
Replace the
NODE_IP value with your cluster node IP, and the NodePort values with the ports you use for each environment. Adjust the deployment and namespace names to match your own naming convention.Apinizer projects
Update the
PROJECT_NAME_* variables with the project names you created in Apinizer. Replace PROXY_NAME with your API proxy name.Apinizer promotion mapping
For production promotion, update the
mappingNames list in the apinizerPromoteProd call with the mapping names you defined in the Apinizer UI.
