A well-designed CI/CD pipeline converts application changes into tested, secure and deployable releases. Jenkins automates the workflow, Docker creates consistent application packages, and Kubernetes manages container deployment across a cluster.
Using these tools together is not simply a matter of writing a Jenkinsfile. A production-ready design must address source control, test quality, image tagging, secrets, deployment safety, observability and rollback. This guide explains the complete workflow and provides practical examples that DevOps engineers can adapt for real projects.
What Is a CI/CD Pipeline?
Continuous Integration, or CI, is the practice of frequently merging code into a shared repository. Every change triggers automated validation such as compilation, unit testing, static analysis and container image creation.
Continuous Delivery keeps validated releases ready for deployment. Continuous Deployment goes one step further by automatically releasing every successful change to an environment, subject to the organisation's controls.
A typical pipeline follows this sequence:
- A developer pushes code to Git.
- A webhook triggers Jenkins.
- Jenkins checks out the requested commit.
- Automated tests and quality checks run.
- Docker builds an immutable container image.
- The pipeline scans and pushes the image to a registry.
- Kubernetes deploys the selected image.
- Readiness checks confirm whether the release is healthy.
- Monitoring tools observe the application after deployment.
CI/CD does not remove operational control. It makes release steps consistent, repeatable and auditable.
Roles of Jenkins, Docker and Kubernetes
Each component solves a different part of the delivery process.
| Tool | Primary responsibility | Typical pipeline use |
|---|---|---|
| Jenkins | Workflow automation | Checkout, testing, approvals and deployment orchestration |
| Docker | Application packaging | Build a portable container image with dependencies |
| Container registry | Image storage | Store versioned images for deployment |
| Kubernetes | Container orchestration | Scheduling, scaling, health checks and rolling updates |
Jenkins should coordinate tasks rather than become the permanent storage location for build artefacts. Docker images belong in a registry such as Amazon Elastic Container Registry, Azure Container Registry, Google Artifact Registry, Harbor or another approved registry.
Kubernetes then pulls the exact image selected by the pipeline. This separation makes it easier to promote one tested image across development, staging and production.
Recommended Pipeline Architecture
The pipeline should use Git as the source of truth and store its configuration with the application. In Jenkins, this usually means committing a Jenkinsfile to the repository.
A practical architecture contains:
- A Git repository for application code and pipeline configuration
- Jenkins controller and one or more build agents
- Docker-capable or container-based agents
- A private container registry
- Separate Kubernetes namespaces or clusters for each environment
- A secrets management system
- Centralised logging, metrics and alerts
Avoid running builds directly on the Jenkins controller. Dedicated agents provide better isolation and prevent resource-intensive Docker builds from affecting Jenkins administration.
For larger environments, ephemeral Jenkins agents can run as Kubernetes pods. The Jenkins Kubernetes plugin can create an agent for a job and remove it after completion. Agent images should contain only the tools required by the pipeline.
Designing the Pipeline Stages
1. Checkout and Version Identification
The pipeline must check out a specific commit and assign an immutable release identifier. Do not rely only on tags such as latest, because the image behind that tag can change.
A useful tag can combine the Jenkins build number and shortened Git commit:
142-a31f82cFor stronger release traceability, Kubernetes can deploy an image by registry digest. A digest identifies the exact image content even if a human-readable tag is moved.
2. Build and Automated Testing
Run fast tests early so that failures consume the least possible time. Depending on the application, CI checks may include:
- Dependency installation
- Compilation
- Unit tests
- Code formatting or linting
- Static application security testing
- Software composition analysis
- Integration tests
The test command depends on the technology stack. A Java application might use Maven or Gradle, while Node.js projects commonly use npm. Test reports should be published in Jenkins so that teams can inspect failures without searching through raw console output.
3. Docker Image Build
A Dockerfile defines the runtime package. Multi-stage builds help separate compilation tools from the final runtime image.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm test
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/src ./src
USER node
EXPOSE 3000
CMD ["node", "src/server.js"]Pin base images appropriately and review them regularly. A smaller image can reduce download time and attack surface, but size alone does not guarantee security.
Include a .dockerignore file to exclude Git metadata, local dependencies, test output and secrets from the build context.
4. Image Security Scanning
Scan the built image before pushing or deploying it. Tools such as Trivy can detect known vulnerabilities in operating system packages and application dependencies.
trivy image --exit-code 1 --severity HIGH,CRITICAL \
registry.example.com/team/web:142-a31f82cA production policy should define which findings block a build, how exceptions are approved and when exceptions expire. Treating every finding in the same way can create unnecessary pipeline failures, while ignoring all findings creates avoidable risk.
5. Push to a Container Registry
Authenticate with credentials stored in Jenkins Credentials, not in the repository or Jenkinsfile. Push only after required tests and scans pass.
The registry should enforce access controls, encrypted connections, retention policies and image scanning where available. Production clusters normally require pull access, while the CI identity requires push access.
6. Deploy to Kubernetes
Kubernetes deployments support controlled rolling updates. The container name in the deployment must match the name passed to kubectl set image.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: web
image: registry.example.com/team/web:initial
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 20
periodSeconds: 20
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512MiThe readiness probe prevents traffic from reaching a pod until it can serve requests. The liveness probe allows Kubernetes to restart a container that has entered an unhealthy state. These probes should represent real application behaviour without depending on unrelated external services.
Resource requests help the scheduler place pods. Limits prevent one container from consuming uncontrolled resources, although CPU and memory values must be tested for the application rather than copied blindly.
Jenkins Declarative Pipeline Example
The following simplified Jenkinsfile builds, scans, pushes and deploys an image. It assumes that Docker, Trivy and kubectl are installed on the selected agent.
pipeline {
agent { label 'docker' }
options {
disableConcurrentBuilds()
timestamps()
timeout(time: 30, unit: 'MINUTES')
}
environment {
REGISTRY = 'registry.example.com'
IMAGE = 'team/web'
KUBE_NAMESPACE = 'production'
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
def commit = sh(
script: 'git rev-parse --short=7 HEAD',
returnStdout: true
).trim()
env.IMAGE_TAG = "${env.BUILD_NUMBER}-${commit}"
}
}
}
stage('Test') {
steps {
sh 'npm ci'
sh 'npm test'
}
}
stage('Build Image') {
steps {
sh 'docker build -t $REGISTRY/$IMAGE:$IMAGE_TAG .'
}
}
stage('Scan Image') {
steps {
sh 'trivy image --exit-code 1 --severity HIGH,CRITICAL $REGISTRY/$IMAGE:$IMAGE_TAG'
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'registry-credentials',
usernameVariable: 'REGISTRY_USER',
passwordVariable: 'REGISTRY_PASSWORD'
)]) {
sh '''
echo "$REGISTRY_PASSWORD" | docker login \
--username "$REGISTRY_USER" --password-stdin "$REGISTRY"
docker push "$REGISTRY/$IMAGE:$IMAGE_TAG"
docker logout "$REGISTRY"
'''
}
}
}
stage('Deploy') {
steps {
withCredentials([file(
credentialsId: 'kubeconfig-production',
variable: 'KUBECONFIG'
)]) {
sh '''
kubectl --kubeconfig="$KUBECONFIG" \
-n "$KUBE_NAMESPACE" set image deployment/web \
web="$REGISTRY/$IMAGE:$IMAGE_TAG"
kubectl --kubeconfig="$KUBECONFIG" \
-n "$KUBE_NAMESPACE" rollout status deployment/web \
--timeout=180s
'''
}
}
}
}
post {
always {
sh 'docker image rm $REGISTRY/$IMAGE:$IMAGE_TAG || true'
cleanWs()
}
}
}In a mature setup, deployment may use Helm, Kustomize or a GitOps controller instead of directly changing the cluster from Jenkins. The correct choice depends on governance, scale and audit requirements.
Environment Promotion and Approval
Build an image once and promote the same image through development, staging and production. Rebuilding for each environment can produce different binaries or dependencies, even when the source commit is unchanged.
Environment-specific configuration should be supplied at deployment time through ConfigMaps, Secrets or an external secrets system. Do not package database passwords, API keys or environment URLs inside the image.
A common promotion flow is:
- Automatically deploy successful main-branch builds to development
- Run integration and acceptance tests
- Promote the tested image digest to staging
- Require an authorised approval for production
- Deploy the same digest and verify the rollout
Jenkins provides an input step for manual approval, but production access should also be controlled through role-based permissions and protected credentials.
Rollback and Failure Handling
A pipeline should define failure behaviour before the first production release. If Kubernetes cannot complete the rollout, Jenkins must fail the deployment stage and alert the responsible team.
The previous Kubernetes deployment revision can be restored with:
kubectl -n production rollout undo deployment/web
kubectl -n production rollout status deployment/web --timeout=180sRollback is not always safe. A database migration may make the older application incompatible with the new schema. Use backward-compatible migrations, separate schema changes from destructive cleanup and test rollback procedures in non-production environments.
For high-risk systems, consider canary or blue-green deployment. These strategies require traffic management and meaningful application metrics; simply starting a second set of pods is not enough.
Security Best Practices
Secure the complete software supply chain rather than only the Kubernetes cluster.
- Give Jenkins service accounts the minimum required permissions.
- Use separate credentials for development and production.
- Never print passwords, tokens or kubeconfig content in logs.
- Rotate credentials and remove unused access.
- Run containers as non-root where the application supports it.
- Apply Kubernetes RBAC, NetworkPolicies and namespace isolation.
- Scan dependencies, images and Kubernetes manifests.
- Record image tags, digests, commits and deployment results.
- Protect main branches with reviews and required checks.
- Keep Jenkins, plugins, agents and base images patched.
Avoid giving a Jenkins identity unrestricted cluster-admin access. A deployment service account should normally be limited to specific resources in the target namespace.
Monitoring and Pipeline Metrics
A successful Jenkins job does not prove that users are receiving a healthy service. Monitor the application after deployment through metrics, logs and traces.
Useful delivery indicators include build duration, test failure trends, deployment frequency, failed deployment rate and recovery time. Kubernetes events and rollout status help diagnose scheduling or probe failures, while application monitoring detects errors that infrastructure checks may miss.
Jenkins should send clear notifications containing the job name, build number, commit, image identifier, environment and failure stage. Teams in Chennai, Bangalore or distributed locations can then investigate without manually reconstructing release details.
Common Design Mistakes
Frequent problems include using latest in production, embedding secrets in images, skipping readiness probes and allowing concurrent deployments to the same environment. Other mistakes are rebuilding images during promotion, granting excessive Kubernetes permissions and treating a successful pod start as complete application validation.
A reliable pipeline remains understandable. Complex scripts without version control, ownership or documentation become difficult to maintain. Start with clear stages, add controls based on risk and test the entire release and rollback process regularly.
Building Practical DevOps Skills
For DevOps roles in Indian technology companies, candidates should understand more than individual commands. Employers commonly assess Git workflows, Jenkins pipelines, Docker image design, Kubernetes deployments, Linux troubleshooting, networking, cloud services and security fundamentals.
Hands-on practice should include creating a Jenkinsfile, operating build agents, pushing to a private registry, configuring probes, diagnosing failed rollouts and implementing least-privilege access. A lab project that demonstrates these tasks provides stronger evidence of ability than memorising tool definitions.
Conclusion
Jenkins, Docker and Kubernetes form a practical CI/CD toolchain when each component has a clear responsibility. Jenkins automates validation and promotion, Docker creates immutable release artefacts, and Kubernetes performs controlled deployment and runtime management.
The most important design principles are to build once, use traceable image versions, protect credentials, verify every rollout and prepare a tested rollback method. These controls turn a basic automation script into a maintainable delivery system.
