Installing Jenkins LTS¶
Overview¶
A Jenkins controller you cannot rebuild is a liability. Labs and production both need a reproducible Long-Term Support (LTS) install: pinned image, persistent JENKINS_HOME, and a documented first-boot path through the setup wizard.
This tutorial standardises on Docker Compose with the official jenkins/jenkins LTS image so every learner shares the same baseline. You will also know when package or Web Application Archive (WAR) installs make sense on bare metal. After the wizard you have an admin user, a Jenkins URL, suggested plugins, and a volume you must not delete casually.
This is Tutorial 2 in Module 2: Installing Jenkins LTS of the REBASH Academy Jenkins for Cloud & DevOps Engineers series — written for Cloud, DevOps, Platform, and Site Reliability Engineering (SRE) engineers. Official reference: Installing Jenkins with Docker.
Prerequisites¶
- Introduction to Jenkins and CI/CD
- Docker Engine with Compose v2 (
docker compose) - Free host ports 8080 (UI) and 50000 (inbound agents) — or willingness to remap
- Browser for the setup wizard
- Optional: artefacts from
~/rebash-jenkins/module-01
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Bring up Jenkins LTS with Docker Compose and a named volume for
JENKINS_HOME - Retrieve the initial admin password and complete the setup wizard safely
- Explain suggested plugins, admin user creation, and Jenkins URL
- Contrast Compose, package (deb/rpm), and WAR install paths
- Stop or destroy a lab controller without losing the mental model of what was persisted
Architecture¶
Compose publishes the UI and agent port; a Docker volume mounts at /var/jenkins_home inside the container.
Theory¶
What it is¶
Jenkins LTS is the production support line of Jenkins core. The official container image jenkins/jenkins publishes LTS tags such as lts and lts-jdk17. Inside the container, JENKINS_HOME defaults to /var/jenkins_home and holds jobs, plugins, secrets, users, and build history.
Docker Compose describes the controller as code: image tag, ports, and volume. That is the lab default for this course. Alternatives:
| Path | When to use |
|---|---|
| Docker / Compose | Labs, many self-hosted starts, reproducible demos |
| Package (deb/rpm) | Dedicated VMs owned by Linux lifecycle tooling |
| WAR + supported JDK | Custom servlet hosting (advanced; follow Java requirements on jenkins.io) |
First boot runs the setup wizard: unlock with secrets/initialAdminPassword, install suggested plugins (or select plugins), create the first admin user, and set the Jenkins URL.
Why it matters¶
Controllers die from “I ran a random docker run and lost the volume,” public 8080 without Transport Layer Security (TLS), and latest tags that jump majors under you. Platform teams pin LTS, back up JENKINS_HOME, and treat first-boot decisions (plugins, URL, admin identity) as part of the platform product.
Suggested plugins pull in Pipeline, Git, and credentials helpers so you are not stuck with an empty controller. Skipping them then wondering why Pipeline is missing is a common lab failure.
How it works¶
- Pull
jenkins/jenkins:lts-jdk17(or a pinned LTS minor tag). - Start the container with a named volume on
/var/jenkins_home. - Wait until Jenkins listens on HTTP port 8080 inside the container.
- Read
/var/jenkins_home/secrets/initialAdminPasswordonce. - Complete the wizard in the browser; admin credentials replace the unlock password.
- Later modules reuse the same volume so jobs and plugins persist across
compose down(without-v).
Agent port 50000 is for inbound agents using the Jenkins remoting protocol. You can remap host ports if 8080 is busy.
Official Docker docs also show a Docker-in-Docker sidecar for building images from Jenkins; that pattern belongs in Module 8. This module keeps the controller install minimal.
Key concepts and comparisons¶
| Path / item | Role |
|---|---|
/var/jenkins_home | JENKINS_HOME in the official image |
secrets/initialAdminPassword | One-time unlock for the wizard |
| Plugin Manager | Adds features after (or during) the wizard |
| Jenkins URL | Root URL used in emails, webhooks, and redirects |
| Volume vs bind mount | Named volumes are simpler for labs; bind mounts need UID awareness (jenkins user in the image) |
| Tag style | Advice |
|---|---|
lts-jdk17 | Good lab/production default for this course |
lts | Tracks current LTS JDK line — still prefer pinning for prod |
latest / weekly | Feature preview only — not this course’s production path |
Common pitfalls¶
- Publishing
0.0.0.0:8080on a public host without a reverse proxy and authentication hardening. - Using
docker compose down -vand deleting months of jobs by accident. - Bind-mounting a host directory owned by root so the container user cannot write
JENKINS_HOME. - Skipping suggested plugins then blocking on missing Pipeline/Git.
- Treating the initial admin password as a long-lived secret after the wizard completes.
Hands-on Lab¶
Objective¶
Start Jenkins LTS with Compose, capture the initial admin password, complete the setup wizard, and prove the controller answers HTTP — with cleanup that preserves or destroys the volume deliberately.
Prerequisites¶
- Docker Engine + Compose v2
- Browser
- Ports 8080/50000 free, or edit the Compose ports
Lab environment¶
Workspace: ~/rebash-jenkins/module-02
mkdir -p ~/rebash-jenkins/module-02 && cd ~/rebash-jenkins/module-02
set -euo pipefail
docker version | tee docker-version.txt
docker compose version | tee compose-version.txt
Expected output
Client/server version lines; Compose version printed.
Real-world scenario¶
Your team needs a disposable but realistic Jenkins LTS for Pipeline labs. You must be able to rebuild the controller from Compose, keep JENKINS_HOME across restarts, and document the unlock path so a teammate can finish the wizard without pinging you.
Step-by-step tasks¶
Task 1 – Write and start Compose for Jenkins LTS¶
Run:
Create compose.yaml:
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=true
volumes:
jenkins_home:
Start and verify:
docker compose pull
docker compose up -d
docker compose ps | tee compose-ps.txt
docker compose logs --tail=40 jenkins | tee boot.log
Expected output
Service running (or healthy); logs show Jenkins starting. If port 8080 is busy, change the left-hand port mapping and re-up.
Task 2 – Wait for Jenkins and read the initial admin password¶
cd ~/rebash-jenkins/module-02
set -euo pipefail
# Wait up to ~2 minutes for the unlock file
for i in $(seq 1 24); do
if docker compose exec -T jenkins test -f /var/jenkins_home/secrets/initialAdminPassword; then
echo "ready on attempt $i" | tee ready.txt
break
fi
echo "waiting ($i)..."
sleep 5
done
docker compose exec -T jenkins cat /var/jenkins_home/secrets/initialAdminPassword \
| tr -d '\r' | tee initialAdminPassword.txt
test -s initialAdminPassword.txt
wc -c initialAdminPassword.txt | tee password-bytes.txt
Expected output
Non-empty initialAdminPassword.txt. Open http://127.0.0.1:8080/ and paste that password into Unlock Jenkins.
Task 3 – Complete the wizard and capture non-secret evidence¶
In the browser (do not automate credentials into Git):
- Choose Install suggested plugins (recommended for this course).
- Create the first admin user; store the password in your password manager — not in the lab directory.
- Confirm the Jenkins URL (for local labs:
http://127.0.0.1:8080/). - Finish and land on the dashboard.
Then capture non-secret evidence from the shell:
cd ~/rebash-jenkins/module-02
set -euo pipefail
# HTTP smoke (may be 403/200/503 depending on auth and readiness — prove TCP/HTTP responds)
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/login | tee http-login-code.txt \
|| curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/ | tee http-root-code.txt
docker compose exec -T jenkins bash -lc 'ls /var/jenkins_home | head' | tee jenkins-home-listing.txt
docker compose exec -T jenkins bash -lc 'test -d /var/jenkins_home/plugins && echo plugins_dir_ok' | tee wizard-plugins-check.txt
grep -qE '^(200|403|503)$' http-login-code.txt 2>/dev/null || grep -qE '^(200|403|503)$' http-root-code.txt
Expected output
HTTP code file shows a response; listing includes paths such as secrets, plugins, or users after the wizard.
Task 4 – Prove persistence across restart (keep volume)¶
cd ~/rebash-jenkins/module-02
set -euo pipefail
docker compose restart jenkins
sleep 20
docker compose ps | tee compose-ps-after-restart.txt
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/login | tee http-after-restart.txt
# Volume still present
docker volume ls | grep jenkins | tee volumes.txt
test -s volumes.txt
Expected output
Controller comes back; volume name containing jenkins still listed. You should still log in with the admin user, not the initial unlock password.
Validation steps¶
-
compose.yamlpinsjenkins/jenkins:lts-jdk17 - Initial admin password was read once and wizard completed
-
http-login-code.txtandjenkins-home-listing.txtprove the controller responds - Restart kept the named volume; login still works with the admin user
Common errors and fixes¶
| Error | Cause | Fix |
|---|---|---|
Bind for 0.0.0.0:8080 failed | Port in use | Map "18080:8080" (or free the port) |
| Unlock file missing | Jenkins still warming | Retry the wait loop; check docker compose logs jenkins |
| Permission errors on bind mount | Host dir ownership | Prefer the named volume in this lab |
| Wizard already completed | Reusing volume | Continue with admin login, or down -v to reset lab data only |
Challenge exercise¶
Pin a specific LTS image digest or minor tag (for example check Docker Hub tags and replace lts-jdk17 with a dated tag in compose.yaml). Recreate the stack with docker compose up -d --pull always and prove the tag with:
cd ~/rebash-jenkins/module-02
docker compose images | tee images.txt
grep jenkins images.txt
Learning outcomes¶
- Installed Jenkins LTS reproducibly with Compose
- Completed unlock → plugins → admin → URL
- Distinguished volume persistence from container lifecycle
- Captured HTTP and volume evidence without committing secrets
Cleanup¶
Keep the controller for Modules 3+ (recommended):
Full lab reset (destroys JENKINS_HOME):
Remove initialAdminPassword.txt from shared machines after unlock:
Validation¶
- Lab completed under
~/rebash-jenkins/module-02/ - You can explain what lives in
JENKINS_HOMEversus the container image - You know when to use Compose vs package vs WAR
- You can describe the failure mode of
compose down -von a shared lab volume
Code Walkthrough¶
- Pin the image —
lts-jdk17or a finer tag beats floatinglatest. - Persist deliberately — named volume for labs; backup strategy for production.
- Evidence without secrets — HTTP codes, listings, and compose output — never commit admin passwords.
- Wizard once — suggested plugins unblock Pipeline/Git for the rest of the course.
- Least exposure — bind UI to localhost for labs; put TLS and access control in front for any shared host.
Security Considerations¶
- The initial admin password is a bootstrap secret — delete local copies after unlock.
- Do not expose port 8080 to the public internet without TLS and hardened auth.
- Admin accounts are break-glass: use personal accounts and least privilege later (Module 11).
- Treat the Docker volume as sensitive as a VM disk — it contains credentials ciphertext and job configs.
- Prefer stopping Compose over leaving an unlocked wizard open on a shared network.
Common Mistakes¶
Running docker compose down -v on a shared controller
The -v flag deletes the jenkins_home volume. Fix: use stop or down without -v unless you intend a full reset.
Publishing Jenkins on a public IP for “just a lab”
Scanners find open Jenkins UIs quickly. Fix: listen on 127.0.0.1 or use a VPN/SSH tunnel; add a reverse proxy with TLS for shared environments.
Skipping suggested plugins
Pipeline and Git arrive via plugins. Fix: install suggested plugins for this course, then govern extras via Plugin Manager later.
Bind-mounting /var/jenkins_home as root
The process inside the image runs as the jenkins user. Fix: use a named volume (this lab) or fix ownership to match the container UID.
Best Practices¶
- Encode the controller in Compose (or later Jenkins Configuration as Code) from day one.
- Pin LTS image tags in production; document upgrade tickets.
- Back up
JENKINS_HOMEbefore plugin or core upgrades. - Keep lab passwords out of Git; use a password manager.
- Separate test controllers from production controllers for plugin trials.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Page never loads | Container crash / still starting | docker compose logs -f jenkins |
| Unlock password rejected | Wrong container / stale volume | Confirm exec target; reset volume only if disposable |
live UI but empty plugins | Custom plugin choice | Plugin Manager → install Pipeline and Git |
| Disk pressure | Large build history in volume | Trim old builds; expand disk |
| Agent port confusion | 50000 not published | Add host mapping or use SSH/JNLP patterns later |
Summary¶
Jenkins LTS on Docker Compose gives a reproducible controller with persistent JENKINS_HOME. Complete the wizard once, protect the volume, and keep admin secrets out of Git. Next: Using Jenkins — Jobs, Views, and Folders.
Interview Questions¶
1. What is stored in JENKINS_HOME, and why must it be persisted?
Reveal answer
Jobs, plugins, users, credentials ciphertext, secrets, and build history live under JENKINS_HOME ( /var/jenkins_home in the official image). If the volume is deleted, the controller configuration and history are gone even if you recreate the container from the same image.
2. Why prefer Jenkins LTS container tags over latest for production?
Reveal answer
LTS is the supported production line with a predictable upgrade cadence. latest or weekly tags can introduce breaking core/plugin combinations without a controlled change window.
3. What is the initial admin password, and when does it stop mattering?
Reveal answer
It unlocks the setup wizard from secrets/initialAdminPassword on first boot. After you create an admin user, you authenticate with that account; the unlock file should not be treated as the long-term password.
4. When would you install Jenkins from OS packages instead of Docker?
Reveal answer
When the organisation standardises on VM images, configuration management, and OS patching for the controller host, or when container networking/storage policies make Docker a poor fit. The trade-off is you own the JVM and OS lifecycle explicitly.
5. What risk does publishing port 8080 on 0.0.0.0 create?
Reveal answer
The UI (and setup wizard if unfinished) may be reachable from untrusted networks. Attackers scan for open Jenkins instances. Labs should bind to localhost; shared hosts need TLS termination and access control.
6. How do you reset a disposable lab controller without affecting a volume you want to keep?
Reveal answer
docker compose down removes containers/networks but keeps named volumes. Add -v only when you intentionally destroy JENKINS_HOME. Prefer stop between modules when you want the same controller state.
7. Why install suggested plugins on a training controller?
Reveal answer
They provide the common Pipeline, SCM, and credentials stack so learners are not blocked on missing plugins. Production still needs a governed plugin list — suggested plugins are a bootstrap, not an infinite allow-list.
8. What is port 50000 used for in the Compose file?
Reveal answer
It is the default inbound agent (remoting) port so agents can connect to the controller. You still must configure agents securely; opening 50000 widely without authentication controls is unsafe.
Related Tutorials¶
- Introduction to Jenkins and CI/CD
- Using Jenkins — Jobs, Views, and Folders
- Docker Compose fundamentals