Chapter 01Lesson 02~125 minutes

Jenkins and CI/CD Foundations, Controller-Agent Architecture, Jobs, Builds, and Automation Boundaries: Guided Hands-On Workflow and Core Operations

Build the Chapter 01 mental model with a disposable controller and a separate inbound agent. You will pin the Jenkins baseline, complete the setup wizard safely, remove routine build execution from the built-in node, run a two-stage Pipeline, preserve build evidence, and then connect the same Pipeline to SCM so a source change can become a distinct build cause.

Hands-onJenkins LTSInbound agentPipelineSCM trigger

Learning objectives

  • Launch a disposable Jenkins 2.568.3 LTS controller with persistent lab-only state and verify its Java/core baseline before setup.
  • Create a separate single-executor inbound agent over WebSocket and confirm that routine builds do not use the built-in node.
  • Create and run a minimal Declarative Pipeline while inspecting queue, build, node, workspace, console, and artifact evidence.
  • Convert the Pipeline to “Pipeline script from SCM,” record the exact checked-out SHA, and distinguish manual and SCM-triggered causes.
  • Clean up only the controller, agent, Docker network/volume, and disposable repository created by this lab.

1. Preflight: define the lab boundary before creating resources

This lab is intentionally local-first. The controller and agent run as separate Docker containers on a dedicated Docker network. The controller keeps state in a named volume so a container restart does not silently erase the lab. The agent has one executor and a dedicated work directory. No production credentials, cloud accounts, internal repositories, or privileged Docker socket mounts are required.

Assumption Pinned/required value Why it matters
Controller jenkins/jenkins:2.568.3-lts-jdk21 Pins current LTS core and Java 21 runtime for the lab.
Controller image index sha256:c1e4c349365f6d16d88595b2c5f7e8ff39b8ae1d061f62420bac193b4b9616d0 Documents the multi-platform image identity observed at verification time.
Agent jenkins/inbound-agent:3391.va_37fa_a_305d6d-2-jdk21 Pins current inbound-agent/Remoting packaging for Java 21.
Pipeline workflow-aggregator 608.v67378e9d3db_1 plus current dependencies Provides core Pipeline steps; record actual resolved dependency versions.
Declarative pipeline-model-definition 2.2293.v6e7193cec599 Provides Declarative Pipeline syntax used by the lab.
Host Docker Engine/Desktop, at least ~4 GB free memory and ~8 GB free disk Prevents resource pressure from being confused with Jenkins behavior.
Do not paste the inbound-agent secret into screenshots, chat, Git commits, or build logs. It is a real credential for this disposable controller even though the controller is local. Read it into a temporary shell variable and unset it after the agent starts.

2. Start the disposable controller and prove what version actually ran

Create names first so cleanup can target exact resources instead of broad Docker commands. Port 18080 avoids assuming the host has nothing on 8080.

set -eu

docker network create jenkins-ch01-net
docker volume create jenkins-ch01-home

docker run -d \
  --name jenkins-ch01-controller \
  --network jenkins-ch01-net \
  --restart=no \
  -p 127.0.0.1:18080:8080 \
  -v jenkins-ch01-home:/var/jenkins_home \
  jenkins/jenkins:2.568.3-lts-jdk21

# Preserve startup evidence before opening the UI.
docker logs --tail 120 jenkins-ch01-controller

docker exec jenkins-ch01-controller java -version
docker exec jenkins-ch01-controller java -jar /usr/share/jenkins/jenkins.war --version

Expected observation: Jenkins reports 2.568.3 and Java 21. If either differs, stop and resolve the image/tag mismatch before building lesson evidence. Open http://localhost:18080/ only after the logs show that Jenkins is ready.

3. Complete initial setup without weakening security

Read the one-time setup password directly from the disposable controller, complete the setup wizard, install suggested plugins, and create a lab-only administrator. Do not disable authentication, CSRF, Script Security, or TLS verification to “make the lab easier.” The local HTTP endpoint is acceptable only because it is bound to the learner's machine for this disposable exercise; production exposure is a later architecture topic.

docker exec jenkins-ch01-controller \
  cat /var/jenkins_home/secrets/initialAdminPassword

After setup, open Manage Jenkins → Plugins → Installed plugins. Verify that Pipeline and Declarative Pipeline support are present. Record the actual versions in a small text note. On the verification date for this lesson, the current reference versions are workflow-aggregator 608.v67378e9d3db_1 and pipeline-model-definition 2.2293.v6e7193cec599; dependencies can resolve to newer compatible versions in the future, so the observation matters more than memorizing a number.

4. Remove build execution from the built-in node

Navigate to Manage Jenkins → Nodes → Built-In Node → Configure. Set Number of executors to 0 and save. Do this only after you understand the next step: with no other agent, builds will queue rather than run.

Expected state: the controller remains healthy and serves the UI, but it has no routine executor capacity. If you create a job now, it can be configured and scheduled yet remain queued. That is a useful demonstration that “configured,” “queued,” and “executing” are different states.

5. Create a single-executor inbound agent over WebSocket

Go to Manage Jenkins → Nodes → New Node. Create a permanent agent named lab-agent with remote root /home/jenkins/agent, one executor, labels lab linux, usage “Only build jobs with label expressions matching this node” if available, and launch method Launch agent by connecting it to the controller. Save it and open the agent page to obtain its secret.

In a Bash-compatible terminal, read the secret without echoing it, start the pinned agent image on the same Docker network, then remove the shell variable:

read -rsp 'Disposable Jenkins agent secret: ' JENKINS_AGENT_SECRET
echo

docker run -d --init \
  --name jenkins-ch01-agent \
  --network jenkins-ch01-net \
  -e JENKINS_URL=http://jenkins-ch01-controller:8080/ \
  -e JENKINS_SECRET="$JENKINS_AGENT_SECRET" \
  -e JENKINS_AGENT_NAME=lab-agent \
  -e JENKINS_AGENT_WORKDIR=/home/jenkins/agent \
  -e JENKINS_WEB_SOCKET=true \
  jenkins/inbound-agent:3391.va_37fa_a_305d6d-2-jdk21

unset JENKINS_AGENT_SECRET

docker logs --tail 80 jenkins-ch01-agent

Expected observation: the node becomes online, reports one executor, and carries the lab/linux labels. The WebSocket path means the lab does not need to publish Jenkins' inbound TCP agent port. Record the agent image tag, node name, labels, and Java/Remoting details shown by Jenkins. The disposable secret is still present in the agent container's environment and can be read by a local Docker administrator; remove the lab agent/container when the exercise is complete and never treat container environment variables as a secret vault.

6. Create the smallest Pipeline that produces inspectable evidence

Create a new Pipeline item named jenkins-ch01-observe. For the first run, store the Pipeline script in the job configuration so source-control integration does not hide the scheduling model you are learning. Use this script:

pipeline {
  agent { label 'lab' }

  stages {
    stage('Observe') {
      steps {
        sh '''
          set -eu
          mkdir -p out
          {
            printf 'job=%s\n' "$JOB_NAME"
            printf 'build=%s\n' "$BUILD_NUMBER"
            printf 'build_url=%s\n' "$BUILD_URL"
            printf 'node=%s\n' "$NODE_NAME"
            printf 'workspace=%s\n' "$WORKSPACE"
            java -version 2>&1 | head -n 1
          } | tee out/runtime.txt
        '''
      }
    }

    stage('Retain evidence') {
      steps {
        sh 'sha256sum out/runtime.txt > out/runtime.txt.sha256'
        archiveArtifacts artifacts: 'out/*', fingerprint: true
      }
    }
  }
}

Click Build Now. Before opening the console, notice whether the run briefly appears in the queue. Then inspect the build page, cause, node/executor allocation, console log, workspace path, and archived artifacts. The build should execute on lab-agent, never the built-in node.

Cause

The first run should show a user-initiated cause.

Build

Record full job name, build number, and build URL.

Queue

If visible, record queue/wait reason and allocation transition.

Agent

Record lab-agent, labels, executor, Java/Remoting identity.

Workspace

Record the path, but treat it as transient execution state.

Artifact

Download runtime.txt and its SHA-256 file from the build record.

7. Move the same Pipeline into SCM and create an SCM-triggered run

The second path adds source identity. Create a tiny disposable public repository in a free Git hosting service (GitHub or GitLab is sufficient) containing only a Jenkinsfile and a README.md. Do not use a proprietary repository or credentials. Put the following Pipeline in Jenkinsfile:

pipeline {
  agent { label 'lab' }

  triggers {
    pollSCM('H/2 * * * *')
  }

  stages {
    stage('Observe source') {
      steps {
        sh '''
          set -eu
          mkdir -p out
          {
            printf 'job=%s\n' "$JOB_NAME"
            printf 'build=%s\n' "$BUILD_NUMBER"
            printf 'node=%s\n' "$NODE_NAME"
            printf 'workspace=%s\n' "$WORKSPACE"
            printf 'source_sha=%s\n' "$(git rev-parse HEAD)"
            git status --short
          } | tee out/identity.txt
        '''
      }
    }

    stage('Retain evidence') {
      steps {
        sh 'sha256sum out/identity.txt > out/identity.txt.sha256'
        archiveArtifacts artifacts: 'out/*', fingerprint: true
      }
    }
  }
}

In the job configuration, change Definition to Pipeline script from SCM, choose Git, point at the disposable public repository, use the default branch, and set Script Path to Jenkinsfile. No repository credential is needed for a public lab repository. Save, then run one manual build and record its exact git rev-parse HEAD.

Now commit and push a harmless README change. Because the Jenkinsfile declares pollSCM, Jenkins will poll on its hashed schedule and create a run after it detects a revision change. Inspect the new build cause and verify that the checked-out SHA differs from the previous build. Polling is used here because a localhost controller is normally not reachable by a hosted SCM webhook; webhook design belongs in later lessons.

Free/local alternative: if you do not want to create an external disposable repository, you may use any local Git service already available to your lab network and point the Jenkins Git plugin at it. Do not enable broad “allow local checkout” compatibility switches or disable Git/SSH verification merely to shorten the exercise.

8. Verification checklist: prove each state independently

  • Controller reports Jenkins 2.568.3 and Java 21 for this dated lab baseline.
  • Built-in node has zero executors.
  • lab-agent is online, has one executor, and carries the expected labels.
  • The manual build shows a user cause and ran on lab-agent.
  • The SCM build records a different cause and the exact checked-out commit SHA.
  • Both builds retain evidence artifacts outside the live workspace.
  • No real credential, agent secret, private source, or production URL appears in console output or archived artifacts.

9. Small challenge: diagnose the layer before touching the script

Edit the Pipeline label from lab to lab-missing and schedule one build. Predict the outcome first. The correct prediction is not “the shell fails”: no eligible node exists, so the Pipeline should wait before the agent-executed steps begin. Capture the queue reason, then restore the label and let the run proceed. This separates scheduling failure from script failure.

10. Cleanup: remove only resources created by this lab

Before cleanup, export or screenshot the evidence you want to keep. Then delete the Jenkins item and node from the disposable controller if desired, stop/remove only the named containers, remove the dedicated network, and remove the lab volume only when you are sure you do not need the controller state.

docker rm -f jenkins-ch01-agent jenkins-ch01-controller
docker network rm jenkins-ch01-net

# Destructive: removes the disposable controller's Jenkins home.
# Run only after verifying the exact volume name.
docker volume inspect jenkins-ch01-home
docker volume rm jenkins-ch01-home

If you created an external disposable repository, delete it through that provider only after checking its exact owner/name and confirming it contains no unrelated work.

11. What the lab proved

You now have direct evidence that the controller, queue, agent, executor, workspace, source revision, build record, and artifact store are distinct states. You also observed two different causes for the same logical automation and proved that a source-triggered build must be tied to an immutable SHA rather than a moving branch label.

Next lesson

Configuration, Design Choices, and Tradeoffs

Compare operating-model choices before scaling the Chapter 01 lab into a production Jenkins platform.

Knowledge check

Why did the lab set built-in-node executors to zero before routine builds?

A build with label lab-missing waits in the queue. Which layer is failing?

Why archive runtime.txt instead of relying on the workspace copy?

Why is SCM polling acceptable in this localhost lab even though webhooks are often preferred in production?

What should you record before deleting the controller volume?

Official references and version notes

Version and compatibility note

Rechecked against primary Jenkins sources on 2026-09-13. The executable Chapter 01 baseline is 2.568.3 LTS on Java 21 (Java 25 is also supported by this LTS line), controller image jenkins/jenkins:2.568.3-lts-jdk21, and inbound-agent image jenkins/inbound-agent:3391.va_37fa_a_305d6d-2-jdk21. The current LTS line can change after this date, so future generation and later lab reuse must re-check the LTS changelog, Java support matrix, Docker tags, plugin minimum core versions, and security advisories.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.