Initializing a Jenkins Controller

Beginner

Introduction

Jenkins normally starts as an uninitialized controller. The first administrator unlocks the controller with the initial password, creates a permanent admin account, and confirms that Jenkins is ready for work.

In this lab, you will practice that first-run workflow safely. The main Jenkins service in the VM is already configured for other labs, so you will start a second temporary Jenkins controller on port 18080. You will initialize only that temporary controller, verify the result, and then remove it.

Start an Isolated Fresh Jenkins Controller

In this step, you will start a new Jenkins controller without changing the main Jenkins service. A Jenkins controller stores its configuration under /var/jenkins_home, so this lab uses a separate Docker volume named jenkins-init-lab-home.

The docker run command below starts the temporary controller in the background. The -d option means detached mode, --name gives the container a stable name, -p 18080:8080 publishes Jenkins on host port 18080, and -v attaches the isolated Jenkins home volume.

docker run -d \
  --name jenkins-init-lab \
  -p 18080:8080 \
  -v jenkins-init-lab-home:/var/jenkins_home \
  jenkins/jenkins:latest

Docker prints the new container ID. The exact ID is different every time, so any long hexadecimal value is acceptable.

Next, wait until Jenkins starts answering HTTP requests. The loop below asks the local controller for /login until it returns an HTTP status code. A 200 response means the login page is available.

until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:18080/login)" = "200" ]; do
  echo "Waiting for the temporary Jenkins controller..."
  sleep 5
done

You should see the waiting message a few times, then the prompt returns. Confirm the container and port mapping:

docker ps --filter name=jenkins-init-lab --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

The output should include the container name and port 18080:

NAMES              STATUS          PORTS
jenkins-init-lab   Up ...          0.0.0.0:18080->8080/tcp, ...

Retrieve the Initial Administrator Password

In this step, you will retrieve the one-time password that Jenkins creates for the first administrator. Jenkins stores this password inside the controller home directory at /var/jenkins_home/secrets/initialAdminPassword.

Use docker exec to run cat inside the temporary Jenkins container, then save the password into your project directory:

docker exec jenkins-init-lab cat /var/jenkins_home/secrets/initialAdminPassword > /home/labex/project/jenkins-init-password.txt

The command uses > redirection to write the command output into a file. That keeps the password available for the next steps without relying on a shell variable.

Display only the first few characters so you can confirm the file is not empty without exposing the full value in your terminal history:

cut -c1-8 /home/labex/project/jenkins-init-password.txt

You should see eight characters from the password, similar to this:

1a2b3c4d

Create a Permanent Administrator Account

In this step, you will use the initial password to create a real Jenkins administrator named lab-admin. The first password is only meant for setup. A normal administrator account is what teams use after the controller is initialized.

Jenkins protects form submissions with a crumb token. A crumb is tied to the same browser or HTTP session that requested it, so the command below also saves Jenkins cookies in a cookie jar file.

INIT_PASSWORD=$(tr -d '\r\n' < /home/labex/project/jenkins-init-password.txt)
COOKIE_JAR=/home/labex/project/jenkins-init-cookies.txt

curl -fsS -c "${COOKIE_JAR}" -u "admin:${INIT_PASSWORD}" \
  http://localhost:18080/crumbIssuer/api/json \
  > /home/labex/project/jenkins-init-crumb.json

The next command sends a small Groovy script to Jenkins. The script creates a private security realm, adds the lab-admin user, grants full control to logged-in users, and marks the setup wizard as completed.

CRUMB_FIELD=$(sed -n 's/.*"crumbRequestField":"\([^"]*\)".*/\1/p' /home/labex/project/jenkins-init-crumb.json)
CRUMB_VALUE=$(sed -n 's/.*"crumb":"\([^"]*\)".*/\1/p' /home/labex/project/jenkins-init-crumb.json)

cat > /home/labex/project/initialize-controller.groovy <<'GROOVY'
import jenkins.model.Jenkins
import jenkins.install.InstallState
import hudson.security.HudsonPrivateSecurityRealm
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy

def jenkins = Jenkins.get()
def realm = new HudsonPrivateSecurityRealm(false)
realm.createAccount('lab-admin', 'LabEx-Admin-123')
jenkins.setSecurityRealm(realm)

def strategy = new FullControlOnceLoggedInAuthorizationStrategy()
strategy.setAllowAnonymousRead(false)
jenkins.setAuthorizationStrategy(strategy)

jenkins.setInstallState(InstallState.INITIAL_SETUP_COMPLETED)
jenkins.save()
println('created lab-admin and completed setup')
GROOVY

curl -fsS -b "${COOKIE_JAR}" -u "admin:${INIT_PASSWORD}" \
  -H "${CRUMB_FIELD}: ${CRUMB_VALUE}" \
  --data-urlencode "script@/home/labex/project/initialize-controller.groovy" \
  http://localhost:18080/scriptText \
  > /home/labex/project/jenkins-init-result.txt

Read the result file:

cat /home/labex/project/jenkins-init-result.txt

The expected message is:

created lab-admin and completed setup

Confirm the Initialized Controller Dashboard

In this step, you will confirm that the temporary controller is no longer only an unlock screen. A healthy initialized controller should authenticate the lab-admin user and return the main Jenkins page.

Use curl with -u to authenticate. The -o option writes the HTML to a file, and -w '%{http_code}' prints only the HTTP status code.

curl -s -u 'lab-admin:LabEx-Admin-123' \
  -o /home/labex/project/jenkins-init-dashboard.html \
  -w '%{http_code}\n' \
  http://localhost:18080/

The status code should be:

200

Now search the saved dashboard page for stable Jenkins text:

grep -o -E 'Dashboard|Welcome to Jenkins|Jenkins' /home/labex/project/jenkins-init-dashboard.html | head

You should see at least one matching line. The exact HTML can change between Jenkins versions, but it should identify the page as Jenkins.

Stop and Remove the Disposable Controller

In this step, you will clean up the temporary controller. Cleaning up matters because the main Jenkins service is the one used by the rest of the Jenkins labs, and the temporary controller was only for practicing first-run initialization.

First stop and remove the temporary container:

docker rm -f jenkins-init-lab

Then remove its isolated Jenkins home volume:

docker volume rm jenkins-init-lab-home

Finally, prove the temporary controller is gone:

docker ps -a --filter name=jenkins-init-lab --format '{{.Names}}'

This command should print nothing. An empty result means no container with that name remains.

Summary

You started an isolated Jenkins controller, retrieved its one-time initial administrator password, created a permanent administrator account, confirmed that the initialized dashboard was reachable, and cleaned up the temporary controller. This is the core first-run initialization workflow behind a usable Jenkins controller.