DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    CI/CD PipelinesLesson 8 of 12
    Your progress · 0%
    On this page
    1. Why learn Jenkins
    2. The controller and agents
    3. The Jenkinsfile
    4. Plugins: power and risk
    5. Shared libraries
    6. Jenkins or GitHub Actions?
    ↖ Course roadmap
    Lesson 8 · Jenkins

    Jenkins: the controller, agents, Jenkinsfile and shared libraries

    Jenkins still runs a huge share of company pipelines: a controller handing work to agents, shop-api's pipeline as a declarative Jenkinsfile, plugins and their risks, and shared libraries.

    40 min read · intermediate · hands-on

    By the end of this lesson you'll be able to

    0 of 6 complete

    Why learn Jenkins

    Jenkins is the open-source automation server most companies started their CI on, and many still run it: you host it yourself, it connects to any Git server, and thousands of plugins extend it. You'll meet it in banks, telecoms and any team with years of pipelines behind it. The ideas are the ones you already know; the spelling is Groovy.

    The controller and agents

    The controller plans and stores; agents build. Keep builds off the controller, where every credential lives.© Diagram: DevOps Academy

    The controller plans and stores; agents build. Keep builds off the controller, where every credential lives.© Diagram: DevOps Academy
    PartDoesGitHub Actions equivalent
    ControllerUI, jobs, plugins, credentials, queue, logsGitHub itself
    Agentruns builds, connected by SSH or JNLP, or created per builda runner
    Executorone build slot on an agentone job on a runner
    Labelwhich agents may run a stage (linux && docker)runs-on: labels

    Set the controller's number of executors to 0 (Manage Jenkins → Nodes → Built-In Node). A build on the controller runs with access to JENKINS_HOME, where every job, credential and secret is stored. With the Kubernetes plugin, each build gets a fresh pod that's deleted afterwards: the same "clean machine every time" that GitHub-hosted runners give you.

    The Jenkinsfile

    A Jenkinsfile in the repository defines the pipeline as code, like .github/workflows/. Use the declarative syntax: structured and validated. Here's shop-api's pipeline from the earlier lessons:

    // Jenkinsfile
    pipeline {
      agent { docker { image 'node:22-alpine' } }        // every stage runs in this container
    
      options {
        timeout(time: 20, unit: 'MINUTES')
        disableConcurrentBuilds()
        buildDiscarder(logRotator(numToKeepStr: '30'))
      }
    
      environment {
        VERSION = "1.0.${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
      }
    
      stages {
        stage('Install') {
          steps { sh 'npm ci' }
        }
        stage('Checks') {
          parallel {                                       // lint and unit at the same time
            stage('Lint') { steps { sh 'npm run lint' } }
            stage('Unit') { steps { sh 'npm run test:coverage' } }
          }
        }
        stage('Integration') {
          steps { sh 'npm run test:integration' }
        }
        stage('Package') {
          steps {
            sh 'mkdir -p dist && cp -r src package.json dist/ && tar -czf shop-api-${VERSION}.tgz -C dist .'
            archiveArtifacts artifacts: 'shop-api-*.tgz', fingerprint: true
          }
        }
        stage('Deploy to production') {
          when { branch 'main' }
          input { message 'Deploy to production?'; ok 'Deploy' }   // waits before taking an agent
          agent { label 'deploy' }                         // an agent that can reach the servers
          steps {
            withCredentials([sshUserPrivateKey(credentialsId: 'deploy-key', keyFileVariable: 'SSH_KEY')]) {
              sh './scripts/deploy-ssh.sh deploy@shop.example.com "$VERSION" shop-api-*.tgz'
            }
          }
        }
      }
    
      post {
        failure { echo "Build ${env.BUILD_NUMBER} failed: ${env.BUILD_URL}" }
        always  { cleanWs() }
      }
    }
    
    JenkinsfileMeansActions equivalent
    agentwhere the pipeline or a stage runsruns-on: / container:
    stages { stage {} }ordered steps of the pipelinejobs and steps
    parallel { }stages at the same timejobs without needs
    environment { }variables for every stepenv:
    when { branch 'main' }run a stage only sometimesif:
    input { } on a stagepause for approval, before an agent is takenenvironment reviewers
    withCredentialsa secret, only inside the block${{ secrets.X }} in env:
    post { always / failure }after the stagesif: always() / failure()
    archiveArtifactskeep files with the buildupload-artifact

    The docker agent needs the Docker Pipeline plugin and agents with Docker; cleanWs() comes from the Workspace Cleanup plugin. Note the quotes: Groovy expands "${VERSION}" in double quotes itself, while in sh '…$VERSION…' (single quotes) the shell does, which is the safe way to pass values, for the same reason as env: in Actions (lesson 11).

    Multibranch pipelines find every branch and pull request with a Jenkinsfile and build each one, and a webhook from GitHub or GitLab triggers them on push. The job's Pipeline Syntax page generates the code for any step from a form.

    Plugins: power and risk

    Almost everything in Jenkins is a plugin: Git, credentials, pipelines, Docker, Kubernetes agents, Slack messages. That flexibility is also its weakest point:

    • Each plugin is maintained separately, and several have had serious vulnerabilities over the years.
    • Plugins depend on each other and on the Jenkins version, so upgrades can break pipelines.
    • They all run inside the controller, with its access to every credential.

    Install only what you use, update Jenkins and plugins regularly (test upgrades on a copy first), and remove plugins nobody needs. Follow the official security advisories (linked below). Manage the controller itself as code: the Configuration as Code plugin describes its settings in YAML, and a Dockerfile can pin the plugin list, so a broken controller can be rebuilt instead of repaired by hand.

    Shared libraries

    With fifty services, fifty copies of the same Jenkinsfile drift apart, just like workflow files (lesson 12). A shared library is a Git repository of pipeline code that Jenkinsfiles load:

    jenkins-shared-library/
    └── vars/
        └── nodePipeline.groovy      # becomes a step called nodePipeline
    
    // vars/nodePipeline.groovy
    def call(Map config = [:]) {
      pipeline {
        agent { docker { image "node:${config.node ?: '22'}-alpine" } }
        stages {
          stage('Install') { steps { sh 'npm ci' } }
          stage('Lint')    { steps { sh 'npm run lint' } }
          stage('Test')    { steps { sh 'npm run test:coverage' } }
        }
        post { always { cleanWs() } }
      }
    }
    
    // Jenkinsfile in each service
    @Library('company-pipelines@v1') _
    nodePipeline(node: '22')
    

    Register the library once under Manage Jenkins → System → Global Pipeline Libraries, pin it to a tag such as @v1, and version it like any shared code.

    Jenkins or GitHub Actions?

    Choose Jenkins whenChoose a hosted CI when
    builds must stay inside your network, with no cloud service involvedyour code is on GitHub or GitLab already
    you need unusual hardware, OSes or toolsyou'd rather not run and patch a CI server
    years of pipelines and plugins already depend on ityou want fresh runners and a marketplace of actions

    Plenty of companies run both: Jenkins for legacy and on-premises builds, Actions for new services. The pipeline shape you've built in this course works in either.

    In production

    Put the controller behind SSO with role-based access (the Role-based Authorization Strategy plugin), run builds only on ephemeral agents, keep JENKINS_HOME backed up, and store credentials in Jenkins's credentials store or an external vault, never in job configuration or Jenkinsfiles.

    ⌘
    Mini mission

    Run shop-api's pipeline on Jenkins

    Start Jenkins locally in Docker, create a Pipeline job that reads shop-api's Jenkinsfile from Git, and get Install, Checks (lint and unit in parallel), Integration and Package passing, with the package archived on the build page.

    Input: Docker, and the shop-api repository with the Jenkinsfile from this lessonOutput: A green Jenkins build with shop-api-<version>.tgz archived
    Reveal one safe solution
    $ docker volume create jenkins_home
    $ docker run -d --name jenkins -p 8080:8080 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts-jdk21
    $ docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword   # paste into http://localhost:8080
    
    docker volume create jenkins_home
    • dockerbuild and run containers
    • volumemanage volumes
    • createcreate one
    docker run -d --name jenkins -p 8080:8080 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts-jdk21
    • runstart a new container
    • -drun in the background
    • --name jenkinsname the container
    • -p 8080:8080publish a port, host:container
    • -v jenkins_home:/var/jenkins_homemount a volume or folder, source:path-inside
    • jenkins/jenkins:lts-jdk21the image to start
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    • execrun a command in a running container
    • jenkinsthe container
    • cat /var/jenkins_home/secrets/initialAdminPasswordthe command to run inside it

    Install the suggested plugins, plus Docker Pipeline if your agent has Docker. Create New Item → Pipeline, choose Pipeline script from SCM, give it the repository URL and branch main, and save. The controller in this container has no Docker, so for the first run change the top line to agent any and install Node on the agent, or add a Docker-capable agent with the label docker. Build Now: the stage view shows Install, then Lint and Unit side by side, then Integration and Package, and the build page lists the archived .tgz. Delete the Deploy stage, or leave it: when { branch 'main' } and input stop it until you approve.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    Why should builds run on agents, not on the Jenkins controller?

    Go deeper

    Official reading for this lesson

    • DocsJenkins: pipeline syntaxJenkins ↗
    • DocsJenkins: using a JenkinsfileJenkins ↗
    • DocsJenkins: extending with shared librariesJenkins ↗
    • DocsJenkins: securing JenkinsJenkins ↗
    • ArticleJenkins security advisoriesJenkins ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousDeploy: static sites, servers over SSH, Kubernetes, and rollbacksNext lesson →Other CI tools: GitLab CI, CircleCI and Buildkite