Build a CI/CD Pipeline with Jenkins: Practical Tutorial

Khimananda Oli 7 min read Database
Build a CI/CD Pipeline with Jenkins: Practical Tutorial

By Khimananda Oli | Last reviewed: August 2026

Automating software delivery is the difference between shipping weekly and shipping on demand, yet many teams still struggle to build a CI/CD pipeline with Jenkins that is both reliable and maintainable. While newer tools exist, Jenkins remains the industry standard for complex, customizable automation due to its massive plugin ecosystem and flexibility. This guide moves beyond basic "Hello World" examples to show you how to architect a production-grade pipeline using Declarative syntax, ephemeral Docker agents, and secure credential handling.

How do you architect a secure Jenkins CI/CD pipeline?

A common mistake I see when teams first evaluate CI/CD tools or migrate legacy scripts is treating the Jenkins controller as a build server. In 2026, the controller should only orchestrate; it must never execute builds directly. Architecting for security and scalability requires separating concerns strictly.

Jenkins ControllerOrchestration OnlyCredentials StoreDocker Agent ABuild & Test(Ephemeral)Docker Agent BSecurity Scan(Ephemeral)ProductionArtifact RegistryTarget Servers
Secure Jenkins architecture isolates the controller from build execution, using ephemeral Docker agents to prevent environment drift and credential leakage.

Your pipeline architecture should follow three non-negotiable principles:

  • Ephemeral Agents: Every build runs in a fresh container. This eliminates "it works on my machine" issues caused by leftover state from previous builds.
  • Pipeline as Code: The entire workflow lives in a Jenkinsfile committed to your repository. Configuration via the web UI is forbidden for production jobs because it cannot be audited or rolled back.
  • Least Privilege Execution: Agents receive only the specific credentials needed for their current stage, injected at runtime and masked in logs.

How do you write a robust Declarative Jenkinsfile?

Declarative Pipeline syntax provides a structured, opinionated way to define workflows. It enforces validation before execution and integrates cleanly with Blue Ocean and other visualization tools. When you build a CI/CD pipeline with Jenkins, always prefer Declarative over Scripted unless you have a specific metaprogramming requirement.

Defining stages and Docker agents

The following example demonstrates a modern pipeline structure. Note the use of specific image tags—never use :latest in production pipelines, as it breaks reproducibility.

pipeline {
    agent none

    environment {
        APP_NAME = 'my-service'
        REGISTRY = 'registry.example.com'
    }

    stages {
        stage('Test') {
            agent {
                docker {
                    image 'node:20-alpine'
                    args '-v /tmp:/tmp'
                }
            }
            steps {
                sh 'npm ci'
                sh 'npm run test:unit'
            }
        }

        stage('Build & Push') {
            agent {
                docker {
                    image 'docker:24-dind'
                    args '--privileged'
                }
            }
            steps {
                script {
                    def tag = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
                    sh "docker build -t ${REGISTRY}/${APP_NAME}:${tag} ."
                    withCredentials([usernamePassword(credentialsId: 'registry-creds', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
                        sh "echo $PASS | docker login ${REGISTRY} -u $USER --password-stdin"
                        sh "docker push ${REGISTRY}/${APP_NAME}:${tag}"
                    }
                }
            }
        }
    }

    post {
        always {
            cleanWs()
        }
        failure {
            echo 'Pipeline failed. Check logs above.'
        }
    }
}

This configuration uses agent none at the top level, which forces each stage to declare its own execution environment. This prevents wasting resources on idle containers during stages that don't need them and allows mixing different tech stacks (e.g., Node for testing, Docker-in-Docker for building) within a single pipeline.

How do you manage secrets and credentials safely?

Credential mismanagement is the leading cause of security incidents in CI/CD systems. Never hardcode passwords, API keys, or SSH private keys in your Jenkinsfile or environment variables defined in the UI. Instead, leverage the Credentials Binding Plugin and Jenkins Credentials Store.

  1. Store centrally: Add credentials via Manage Jenkins → Credentials. Use scoped credentials (folder-level) to limit access between teams.
  2. Inject dynamically: Use withCredentials blocks to bind secrets only for the duration of specific shell commands.
  3. Mask automatically: Jenkins automatically redacts bound variables from console output. However, avoid echoing variables or writing them to files that might be archived as artifacts.
  4. Rotate regularly: Treat CI/CD credentials like production secrets. Implement rotation schedules and audit usage via the Credentials Usage report.

For infrastructure provisioning tasks within your pipeline, consider integrating external secret managers rather than storing cloud provider keys directly in Jenkins. If you are deploying infrastructure alongside your application, referencing patterns from Infrastructure as Code with Terraform ensures your pipeline secrets align with your state management strategy.

What are the essential Jenkins plugins in 2026?

Jenkins core is minimal; functionality comes from plugins. Installing too many creates maintenance debt, but skipping essentials cripples usability. Based on current production environments, these are the non-negotiable plugins for any team learning how to build a CI/CD pipeline with Jenkins:

PluginPurposeWhy It Matters
PipelineDeclarative & Scripted supportFoundation for Jenkinsfile workflows
Docker PipelineNative Docker agent integrationEnables ephemeral, isolated build environments
Credentials BindingSecure secret injectionPrevents credential leakage in logs
Git / GitHub Branch SourceSCM integration & multibranchAuto-discovers PRs and branches
TimestamperAdds timestamps to console outputCritical for debugging slow builds
Blue OceanModern pipeline visualizationMakes pipeline status readable for non-DevOps staff
Configuration as CodeJenkins system config via YAMLDisaster recovery and reproducible controller setup

Avoid installing plugins solely for convenience features if they duplicate existing functionality. Each plugin increases your attack surface and upgrade complexity. Audit your plugin list quarterly and remove anything unused.

How do you optimize pipeline performance and reliability?

Slow pipelines kill developer productivity. After helping multiple organizations reduce build times by 40–60%, I've found that optimization usually comes down to caching, parallelization, and right-sizing agents.

CheckoutShallow CloneUnit TestsCached DepsLint & SASTParallelBuild ImageLayer CacheDeployStaging
Optimized pipeline execution uses parallel stages for independent tasks and aggressive caching to minimize redundant work.

Implement dependency and layer caching

Docker builds benefit enormously from BuildKit cache mounts. Instead of reinstalling dependencies every run, mount the package manager cache directory:

stage('Build') {
    agent { docker { image 'docker:24-dind' } }
    steps {
        sh '''
            DOCKER_BUILDKIT=1 docker build \
              --build-arg BUILDKIT_INLINE_CACHE=1 \
              --cache-from ${REGISTRY}/${APP_NAME}:cache \
              --target production \
              -t ${REGISTRY}/${APP_NAME}:${TAG} .
        '''
    }
}

For non-containerized builds, use the stash/unstash steps or workspace caching plugins to preserve node_modules, vendor, or compiled binaries between stages. Network I/O for dependency resolution is often the single largest time sink.

Parallelize independent stages

Wrap independent stages in a parallel block. Unit tests, linting, and static analysis can typically run simultaneously. This doesn't reduce total compute cost, but it dramatically reduces wall-clock feedback time for developers waiting on merge checks.

How do you handle deployment approvals and rollbacks?

Continuous delivery doesn't mean unattended deployment to production. Mature pipelines include explicit gates and automated rollback capabilities. Use the input step to pause execution until manual approval is granted:

stage('Deploy to Production') {
    when { branch 'main' }
    options { timeout(time: 1, unit: 'HOURS') }
    input {
        message "Deploy ${APP_NAME}:${TAG} to production?"
        ok "Deploy"
        submitter "release-approvers"
    }
    steps {
        sh "./deploy.sh production ${TAG}"
    }
}

Always pair deployment steps with health checks and rollback logic. If your target platform supports it (like Kubernetes), integrate readiness probes into your deployment script. For traditional VPS deployments, ensure you have a verified rollback procedure before automating forward deployments. Teams managing Laravel applications on Ubuntu servers will find relevant patterns in guides on zero-downtime deployment strategies that complement Jenkins orchestration.

Track deployment metadata. Tag successful builds in your artifact registry and record which commit SHA corresponds to each production release. When incidents occur, this traceability reduces mean-time-to-resolution significantly.

Next Steps for Your Jenkins Pipeline

You now have the architectural foundation to build a CI/CD pipeline with Jenkins that meets 2026 production standards. Start with the Declarative template provided, enforce Docker agent isolation from day one, and treat your Jenkinsfile as first-class application code subject to review and testing. Avoid the temptation to over-engineer early; get a reliable green build flowing before adding advanced matrix builds or custom shared libraries.

If your team needs help designing compliant, scalable Jenkins infrastructure or migrating from fragile legacy setups, reach out to discuss your specific requirements. Whether you're optimizing for SOC 2 audit readiness or simply trying to cut build times in half, getting the foundation right prevents costly rework later.

Frequently Asked Questions

Yes, 4GB RAM and 2 vCPUs suffice for small teams.

Add the official Jenkins apt repository, import the GPG key, then run apt install jenkins. Ensure Java 21 is installed first as it is the current LTS requirement for stable operation.

Yes, Jenkins is entirely open source and free.

Jenkins offers superior self-hosted control and plugin extensibility for complex Laravel deployments. GitHub Actions provides easier SaaS integration but lacks granular infrastructure management. Choose Jenkins when custom build agents or private network access are mandatory for your pipeline.

Use the Credentials Binding plugin with encrypted storage. Never hardcode API keys or database passwords in Jenkinsfiles. Configure scoped credentials per project and integrate with HashiCorp Vault for enterprise secret rotation and audit logging in production environments.

Define stages for composer install, phpunit, and artifact archiving in your Jenkinsfile. Use Docker agents to ensure consistent PHP versions across builds. Cache vendor directories between runs using the stash/unstash mechanism to reduce build times significantly.

Verify JAVA_HOME points to JDK 21. Older versions lack required module support. Check agent configurations match controller Java version. Update global tool installations and restart agents after upgrades to resolve compatibility issues during compilation or test execution phases.

Yes, using the Kubernetes CLI and Helm plugins. Configure service account tokens with least-privilege RBAC roles. Store kubeconfig securely in credentials manager. Use post-deployment health checks and rollback stages to ensure safe production releases without manual intervention.

Enable parallel downloads and use a persistent volume for vendor caching. Configure Composer mirror proxies like Packagist Toran. Mount cached directories via Docker volumes instead of downloading dependencies fresh each build. This typically reduces install time by sixty percent.

Disable CLI over remoting, enable CSRF protection, and restrict script approval permissions. Regularly update plugins and core. Implement role-based access control. Audit installed plugins quarterly and remove unused ones to minimize attack surface and prevent known vulnerability exploitation.

Use the ThinBackup plugin for scheduled incremental backups. Include config.xml, credentials, and job directories. Store backups externally on S3 or NFS. Test restoration monthly. Avoid full workspace backups to save storage while preserving critical pipeline definitions and build metadata.

Yes, configure Docker Cloud plugin for dynamic agents. Define agent templates with required tools pre-installed. Containers spawn on demand and terminate after builds complete. This eliminates idle resource waste and ensures clean build environments for every pipeline execution.

Check build console output and agent system logs simultaneously. Verify disk space and memory availability. Review thread dumps via Manage Jenkins. Inspect Docker container status if using containerized agents. Restart unresponsive agents and validate network connectivity to external dependency sources.

Install Pipeline, Git, Credentials Binding, Docker Workflow, and Blue Ocean. Add JUnit and HTML Publisher for test reporting. Include Slack or Teams notifier for alerts. These provide foundational CI/CD capabilities without excessive bloat or maintenance overhead.

Convert shell scripts into staged Jenkinsfile blocks. Parameterize hardcoded values. Move build logic into shared libraries for reuse. Test converted pipelines in parallel before disabling legacy jobs. Maintain version control for all pipeline code to enable rollback and collaboration.