CoursesJenkins foundations, done rightInstall & your first pipeline

Install & your first pipeline

From zero to a green build.

Beginner12 min · lesson 3 of 16

In about five minutes you can turn an empty machine into a working continuous integration server (CI: a machine that builds and tests every code change on its own, so nobody has to remember to do it) and watch it print Finished: SUCCESS. The quickest and safest route in is the official container image. A container image is a vacuum-sealed meal kit: the application and every ingredient it needs, frozen together, so it comes out the same on any kitchen counter and you can bin it and open a fresh one whenever you like. We pin the lts-jdk21 tag. LTS stands for Long-Term Support, the hardened release line that gets refreshed every twelve weeks. That is the line you want in production, not the weekly bleeding-edge build.

terminal
$ docker run -d --name jenkins \
-p 8080:8080 -p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts-jdk21
$ docker ps --filter name=jenkins
output
a1b2c3d4e5f6c9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4
CONTAINER ID IMAGE STATUS PORTS NAMES
a1b2c3d4e5f6 jenkins/jenkins:lts-jdk21 Up 15 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins

Why a container instead of installing Jenkins from a package? Three reasons. The image pins one exact version. Jenkins and the Java runtime it ships with stay walled off from whatever else lives on the host. And upgrading means pulling a newer image and pointing it at the same volume, so you never end up with a half-upgraded machine or a Java version some other tool quietly changed underneath you. The trade-off is that you now think in Docker terms. Everything durable lives in the volume, and "reinstall" means "throw the container away and make a new one". For a learning box, and for most real installs too, that isolation is worth the shift.

Two published ports and one volume do the real work here. -p 8080:8080 opens the web interface. -p 50000:50000 is the inbound agent port, often called JNLP (Java Network Launch Protocol), which build machines dial into over an ordinary TCP network connection in later lessons. -v jenkins_home:/var/jenkins_home mounts a named Docker volume at JENKINS_HOME, the one directory where Jenkins keeps every job, build record, plugin and secret. Treat it as the filing cabinet: the container is the office, offices are replaceable, and losing the cabinet loses everything. Mount it on the very first run. Skip it, and the day you upgrade by recreating the container, the whole instance quietly disappears. On first boot Jenkins also writes a one-time initial admin password to disk and bolts the setup wizard shut behind it, so nobody else on the network can claim your fresh, unconfigured server before you do.

Read that secret straight out of the running container, then paste it into the wizard's Unlock screen.

terminal
$ docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
output
2f3a9c1e8b7d4f60a1c2e3b4d5f60718

The setup wizard

The wizard is a short run of screens. First it checks the initial admin password you read out of the container. Then it offers to install plugins. A plugin is a bundle of extra code that teaches Jenkins a new trick, like talking to Git or Docker, and Install suggested plugins is a sane starting set. Next it makes you create a first admin user, and finally it confirms the Jenkins URL. Do create a real named account with a strong password rather than living on the throwaway initial one, and do not put the instance on the public internet until you have. Everything the wizard does by clicking can also be written down as a file. That is Configuration as Code, JCasC for short, plus job seeding, both covered later, and it is how teams rebuild an identical Jenkins on demand.

Your first pipeline

Now the payoff. Choose New Item, name it hello-pipeline, and pick Pipeline. A Pipeline job is one whose entire definition is a script instead of a page of checkboxes in the web interface. That script is a Jenkinsfile, written in declarative pipeline syntax: an opinionated format with a fixed skeleton that reads top to bottom, closer to filling in a form than writing a program. Paste the script below into the job's Pipeline box. pipeline { } wraps everything. agent any means run this on any free executor, and an executor is one slot that runs a single build at a time, like a checkout lane at a supermarket. stages holds the ordered phases. Each stage('Name') is one phase, and it shows up as a labelled box on the job page. Inside a stage, steps lists the actual work: echo prints a line, and sh runs a shell command on the agent.

Jenkinsfile (inline, first run)
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello from Jenkins'
}
}
stage('Build') {
steps {
sh 'echo building...'
}
}
}
}

Click Build Now. Jenkins grabs an executor, creates a workspace (a scratch directory belonging to that job, under JENKINS_HOME/workspace), then walks the stages in order and streams everything it does to the Console Output. Open build #1, then Console Output. You are looking at the full anatomy of a run: who started it, which node it landed on, each stage opening and closing, the output of your echo and sh, and a verdict at the bottom. Read it top to bottom. This log is where you will spend your time whenever a build misbehaves.

Console Output (build #1)
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/hello-pipeline
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Hello)
[Pipeline] echo
Hello from Jenkins
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Build)
[Pipeline] sh
+ echo building...
building...
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

That last line is Jenkins handing down its verdict, and the vocabulary is fixed. SUCCESS is green: every step passed. FAILURE is red: a step returned a non-zero exit code. UNSTABLE is yellow: the build ran to the end, but a post step raised a hand about something, usually failing tests. ABORTED is grey: somebody cancelled it, or it timed out. A stage goes red the instant an sh step exits non-zero, and the pipeline stops right there unless you handle it. That blunt, immediate signal is the whole reason CI exists.

Make the build reproducible

Clicking Build Now is fine once. CI has to run with nobody watching. The same job is drivable from a command line through the Jenkins CLI (command-line interface), a small Java program file that the controller hands out at /jnlpJars/jenkins-cli.jar. Authenticate with an API token. API stands for application programming interface, and an API token is a string that lets a program log in as you, generated under your user, Security, API Token. It carries the same power as your password, so guard it like one, but you can burn it and issue a new one without touching your login. The command below fires hello-pipeline, waits for it, streams the same console log, and hands back a real exit code that a deploy script can branch on. You built a Pipeline job here. The next lesson puts it head to head with the older Freestyle style.

terminal
$ curl -sO http://localhost:8080/jnlpJars/jenkins-cli.jar
$ java -jar jenkins-cli.jar -s http://localhost:8080/ \
-auth admin:$JENKINS_TOKEN \
build hello-pipeline -s -v
Console Output (via CLI)
Started hello-pipeline #2
[Pipeline] Start of Pipeline
Running on Jenkins in /var/jenkins_home/workspace/hello-pipeline
Hello from Jenkins
+ echo building...
building...
[Pipeline] End of Pipeline
Finished: SUCCESS
Completed hello-pipeline #2 : SUCCESS
$ echo $?
0

Three first-run snags catch nearly everyone. If docker run fails with port is already allocated, something else on the machine already owns 8080, so remap the host side, for example -p 8081:8080. If the initialAdminPassword file is not there, the instance was already set up on an earlier boot, so log in with the admin user you created instead. And if a build sits there as a grey, pulsing bar, no executor is free: this single container has a small, fixed number of slots, so one stuck build blocks the queue until it finishes or you abort it.

Your first build is running on the controller
On a fresh single-container Jenkins, agent any schedules the build on the built-in node, and that node is the controller process itself, the machine holding every credential and every job definition. So a buggy or hostile build step runs with the controller's full access. It is the equivalent of handing a first-day contractor the master key to the whole building. Fine while you are learning. On a real instance, the first thing you do is set Manage Jenkins, Nodes, Built-In Node, executors to 0 and run builds on separate agents, so a poisoned build stays trapped on a disposable worker instead of owning your entire delivery pipeline.
From empty machine to a green build
1docker run lts-jdk21
container boots, JENKINS_HOME initialises
2cat initialAdminPassword
paste the one-time secret into the wizard
3wizard: plugins + admin
hands control to a real account
4Pipeline + inline Jenkinsfile
pipeline / agent / stages / steps
5Build Now
an executor runs the stages
6Finished: SUCCESS
your first green build
Every later lesson reuses this same loop. Only the Jenkinsfile gets richer.
Quick check
01Your Jenkinsfile says agent any, and this is a fresh single-container setup. Where does the first build actually run?
Correct — With no separate agents attached, agent any lands on the built-in node, and that node lives inside the controller container. Fine for learning. Production sets the built-in node to 0 executors so builds never touch the controller.
Incorrect — No. Disposable per-build agents are a later setup you configure on purpose. A fresh single container has no agents to spin up.
Incorrect — No. sh steps run in the Jenkins container's workspace through an executor, not directly against the host daemon.
Incorrect — No. agent any is happy to use the built-in node, which is exactly why your first build succeeds with no extra setup.
02A build's final line reads 'Finished: UNSTABLE' (yellow). Going by the result vocabulary in this lesson, what most likely happened?
Incorrect — A clean pass reports SUCCESS (green), and UNSTABLE is not a warming-up state.
Incorrect — A non-zero step gives you FAILURE (red), which is a different verdict.
Correct — UNSTABLE (yellow) usually comes from a post step marking failing tests while the build itself ran to the end.
Incorrect — A cancelled or timed-out build reports ABORTED (grey).
03You want a deploy script to run hello-pipeline on its own, with nobody clicking Build Now, and to carry on only if the build passed. Using the Jenkins CLI shown in this lesson, what gets the script authenticated safely and tells it the outcome?
Correct — An API token is a revocable stand-in for your password, and build … -s -v waits for the run and returns a real exit code you can gate on.
Incorrect — The lesson uses an API token precisely so scripts never carry your real password, and scraping HTML breaks the moment the page changes.
Incorrect — The CLI's build command triggers the same job from the command line.
Incorrect — That tells you whether the container started, not whether the pipeline passed.

Try this

Run docker ps --filter name=jenkins on a scratch host or disposable cluster and read the output against what this lesson described. Then change one input so it fails, and re-run: the error you get is the one you will meet in production.

Takeaway

The trap worth remembering here: your first build is running on the controller. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.

Related