Posts

CI/CD Enterprise Guide: GitHub Actions, Jenkins, GitLab CI and Production Pipelines

CI/CD

CI/CD Enterprise Guide: GitHub Actions, Jenkins, GitLab CI and Production Pipelines

A practical enterprise CI/CD guide with pipeline architecture, real YAML and Jenkinsfile examples, Docker builds, Terraform plans, Ansible deployments, Kubernetes rollouts, security gates, troubleshooting and production checklists.

🧱 → ⚙️ → 🚀

Architecture → Automation → Production

Enterprise DevOps

CI/CD Overview

CI/CD is the automation layer between code and production. Continuous Integration validates every change with build, test, lint and security checks. Continuous Delivery prepares a release so it can be deployed safely. Continuous Deployment goes one step further and deploys automatically when all checks pass.

In enterprise DevOps, CI/CD connects Git, Docker, Terraform, Ansible, Kubernetes, security scanning, artifact registries and monitoring into one repeatable workflow.

Developer → Git Pull Request → CI Checks → Artifact → Security Gates → Deployment → Monitoring
ConceptMeaningProduction Rule
CIBuild and test every changeRun on every pull request
CDPrepare and deploy releasesUse approvals for production
ArtifactImmutable build outputBuild once, promote many times
Runner/AgentMachine executing pipeline jobsHarden and isolate runners

Pipeline Design

Enterprise CI/CD Architecture

A good pipeline is split into clear stages. Each stage has one responsibility. The pipeline should fail early, keep logs, protect secrets and create traceable artifacts.

Git Repository
     │
     ▼
Pull Request
     │
     ├── Lint
     ├── Unit Tests
     ├── Build
     ├── SAST / Dependency Scan
     └── Docker Image Build
              │
              ▼
        Container Registry
              │
              ▼
      Deployment Pipeline
              │
     ├── Terraform Plan / Apply
     ├── Ansible Configuration
     ├── Kubernetes Rollout
     └── Smoke Test + Monitoring
LayerTool ExamplesRisk if Missing
Source ControlGitHub, GitLabNo audit trail
CI EngineGitHub Actions, Jenkins, GitLab CIManual validation
Artifact StoreHarbor, Nexus, GitHub PackagesUntrusted releases
DeploymentHelm, kubectl, AnsibleManual production changes
ObservabilityPrometheus, Grafana, logsNo feedback after deploy

Workflow

Pipeline Stages

Do not make one huge script. Use clear stages. This makes failures easier to understand and makes the pipeline easier to maintain.

StagePurposeExample Command
FormatConsistent styleterraform fmt -check
LintStatic quality checkansible-lint
TestValidate logicnpm test
BuildCreate package/imagedocker build
ScanSecurity validationtrivy image
DeployRelease to environmentkubectl apply

GitHub

GitHub Actions Pipeline

GitHub Actions workflows are YAML files stored in .github/workflows/. A workflow contains jobs. Jobs run on runners. Jobs contain steps. Use permissions, environments and secrets carefully.

# .github/workflows/ci.yml
name: CI

on:
  pull_request:
  push:
    branches: ["main"]

permissions:
  contents: read
  packages: write
  security-events: write

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    name: Test application
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

  docker:
    name: Build Docker image
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t ghcr.io/my-org/my-app:${{ github.sha }} .

      - name: Login to registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

      - name: Push image
        run: docker push ghcr.io/my-org/my-app:${{ github.sha }}

Production advice: never give broad workflow permissions by default. Start with read-only permissions and add only what each job needs.

Jenkins

Jenkins Declarative Pipeline

Jenkins pipelines are often defined in a Jenkinsfile. Declarative Pipeline gives a structured syntax with stages, agents, environment variables and post actions.

// Jenkinsfile
pipeline {
  agent any

  options {
    timestamps()
    disableConcurrentBuilds()
    buildDiscarder(logRotator(numToKeepStr: '20'))
  }

  environment {
    REGISTRY = 'registry.example.com'
    IMAGE = 'platform/my-app'
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Build') {
      steps {
        sh 'mvn -B clean package'
      }
    }

    stage('Unit Test') {
      steps {
        sh 'mvn test'
      }
      post {
        always {
          junit 'target/surefire-reports/*.xml'
        }
      }
    }

    stage('Docker Build') {
      steps {
        sh 'docker build -t $REGISTRY/$IMAGE:$BUILD_NUMBER .'
      }
    }

    stage('Deploy to Test') {
      steps {
        sh 'kubectl set image deployment/my-app my-app=$REGISTRY/$IMAGE:$BUILD_NUMBER -n test'
        sh 'kubectl rollout status deployment/my-app -n test'
      }
    }
  }

  post {
    success {
      echo 'Pipeline completed successfully'
    }
    failure {
      echo 'Pipeline failed. Check logs and rollback if needed.'
    }
  }
}

Jenkins is powerful but needs strong governance. Keep plugins updated, restrict admin access, isolate agents and store credentials in Jenkins credentials or an enterprise secret manager.

GitLab

GitLab CI Pipeline

GitLab CI uses a .gitlab-ci.yml file. Pipelines contain stages and jobs. Jobs run on GitLab runners.

# .gitlab-ci.yml
stages:
  - validate
  - test
  - build
  - scan
  - deploy

variables:
  IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"

validate:
  stage: validate
  image: alpine:3.20
  script:
    - echo "Validate repository"

unit_test:
  stage: test
  image: node:22-alpine
  script:
    - npm ci
    - npm test
  artifacts:
    when: always
    reports:
      junit: junit.xml

build_image:
  stage: build
  image: docker:27
  services:
    - docker:27-dind
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker build -t "$IMAGE_TAG" .
    - docker push "$IMAGE_TAG"

production_deploy:
  stage: deploy
  image: bitnami/kubectl:latest
  when: manual
  environment:
    name: production
  script:
    - kubectl set image deployment/my-app my-app="$IMAGE_TAG" -n production
    - kubectl rollout status deployment/my-app -n production

Use protected branches, protected variables and manual production deployments for high-risk environments.

Containers

Docker Image Pipeline

A production container pipeline should build the image once, scan it, push it to a trusted registry and deploy the exact same digest or tag to each environment.

# Dockerfile example
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# Pipeline commands
docker build -t registry.example.com/platform/my-app:${GIT_SHA} .
docker image inspect registry.example.com/platform/my-app:${GIT_SHA}
docker push registry.example.com/platform/my-app:${GIT_SHA}
Bad PracticeBetter Practice
Use latest in productionUse Git SHA or semantic version
Rebuild per environmentBuild once and promote
No image scanningScan before deployment
Secrets in imageUse secret manager

Infrastructure

Terraform Pipeline

Terraform should run in a controlled pipeline with remote state, state locking, validation and approval before production apply.

# Terraform CI steps
terraform fmt -check -recursive
terraform init -backend-config=backend-prod.hcl
terraform validate
terraform plan -out=tfplan
terraform show -no-color tfplan
# Apply should be protected
terraform apply tfplan

Recommended pull request workflow:

Pull Request
  ├── terraform fmt
  ├── terraform validate
  ├── terraform plan
  └── Comment plan result on PR
Merge to main
  ├── Approval gate
  └── terraform apply

Configuration

Ansible Pipeline

Ansible pipelines should validate YAML, run ansible-lint, test syntax, run check mode and then execute with limited scope before production rollout.

# CI validation
ansible-lint
yamllint .
ansible-playbook playbooks/site.yml --syntax-check
ansible-playbook playbooks/site.yml --list-hosts
# Safe production run
ansible-playbook playbooks/site.yml \
  -i inventories/prod/hosts.yml \
  --check --diff \
  --limit web01.example.com

ansible-playbook playbooks/site.yml \
  -i inventories/prod/hosts.yml \
  --limit webservers \
  --tags nginx

Use Vault or an enterprise secrets tool. Do not print secrets in pipeline logs.

Deployment

Kubernetes Deployment Pipeline

A Kubernetes pipeline should deploy manifests or Helm charts, wait for rollout status and automatically stop if the deployment fails.

kubectl apply -f k8s/deployment.yaml -n production
kubectl rollout status deployment/my-app -n production --timeout=180s
kubectl get pods -n production -l app=my-app
kubectl logs deployment/my-app -n production --tail=100
# Rollback commands
kubectl rollout history deployment/my-app -n production
kubectl rollout undo deployment/my-app -n production
kubectl rollout status deployment/my-app -n production

Production deployments should include readiness probes, liveness probes, resource requests, resource limits and a rollback procedure.

Release Control

Deployment Strategies

StrategyDowntimeRollbackBest Use
Rolling UpdateNoEasyStandard Kubernetes apps
Blue/GreenNoVery fastCritical enterprise services
CanaryNoControlledHigh-traffic systems
RecreateYesManualSmall internal apps
# Kubernetes rolling update example
kubectl set image deployment/my-app my-app=registry.example.com/my-app:1.2.0 -n production
kubectl rollout status deployment/my-app -n production

# Rollback
kubectl rollout undo deployment/my-app -n production

Hardening

CI/CD Security

CI/CD systems are powerful because they can deploy to production. That also makes them high-value attack targets. Treat pipelines like production systems.

  • Use least privilege tokens.
  • Use short-lived cloud credentials with OIDC where possible.
  • Never store secrets in repository files.
  • Protect production environments with approvals.
  • Use protected branches.
  • Pin third-party actions and dependencies.
  • Scan dependencies and container images.
  • Separate build runners from production deployment runners.
  • Disable debug logs when secrets may be printed.
  • Rotate credentials regularly.
RiskControl
Secret leakUse secret manager and masked variables
Compromised action/pluginPin versions and review dependencies
Over-privileged tokenJob-level least privilege permissions
Unsafe production deployManual approval and environment protection

Release Quality

Artifacts and Promotion

Artifacts should be immutable. Do not rebuild the same application separately for test and production. Build once, sign or scan it, then promote the same artifact through environments.

Build artifact: my-app-1.2.0.jar
Docker image: registry.example.com/my-app:1.2.0
Image digest: sha256:abc123...
Environment promotion: dev → test → staging → production

This gives traceability. If production fails, you know exactly which commit, build and image caused it.

Feedback

Pipeline Observability

Measure pipeline health. A CI/CD system is not only successful when it runs. It is successful when it gives fast, reliable and actionable feedback.

MetricMeaningWhy It Matters
Lead TimeCommit to production timeDelivery speed
Change Failure RateDeployments causing incidentsRelease quality
MTTRTime to recoverOperational resilience
Deployment FrequencyHow often releases happenTeam flow
Pipeline DurationTime for pipeline completionDeveloper feedback speed

Operations

Troubleshooting

ProblemLikely CauseCheck
Pipeline does not startWrong trigger or branch ruleCheck workflow on or GitLab rules
Build failsDependency or compile errorCheck build logs
Docker push failsRegistry auth issueCheck token and registry URL
Terraform plan failsProvider/backend issueRun init and validate
Ansible unreachableSSH, DNS or firewallRun ansible ping
Kubernetes rollout failsImage, probe or resource issueDescribe pod and check events
# Debug commands
git status
docker build --no-cache -t test .
terraform validate
terraform plan
ansible all -m ping -i inventories/prod/hosts.yml
kubectl describe pod my-app -n production
kubectl get events -n production --sort-by=.lastTimestamp
kubectl logs deployment/my-app -n production

Cheat Sheet

Useful Commands

# Git
git status
git log --oneline --graph
git diff
git push origin feature/my-change

# Docker
docker build -t app:dev .
docker run --rm app:dev
docker push registry.example.com/app:1.0.0

# Terraform
terraform fmt -recursive
terraform init
terraform validate
terraform plan
terraform apply

# Ansible
ansible-lint
ansible-playbook site.yml --syntax-check
ansible-playbook site.yml --check --diff
ansible-playbook site.yml

# Kubernetes
kubectl apply -f k8s/
kubectl rollout status deployment/app
kubectl rollout undo deployment/app
kubectl describe pod app-123
kubectl logs deployment/app

Summary

Key Takeaways

A strong CI/CD platform is not just automation. It is controlled, secure and observable delivery. The best pipelines are readable, version-controlled, repeatable and safe. They build once, test early, scan continuously, deploy with approvals and monitor after release.

Small changes → Fast feedback → Secure artifact → Controlled deploy → Measured production