4. DevOps
DevOps practices aim to shorten the software development lifecycle by automating the build, test, and deployment stages. Two key practices underpin this automation1:
- Continuous Integration (CI) — developers merge changes frequently; each merge triggers an automated build and test cycle.
- Continuous Delivery (CD) — every change that passes CI is automatically packaged and made ready to deploy to any environment.
flowchart LR
dev([Developer]) -->|git push| scm[Source Control]
scm -->|webhook / poll| ci[CI Server\nJenkins]
subgraph pipeline [Pipeline]
direction LR
build[Build] --> test[Test] --> image[Package\nDocker Image] --> push[Push\nRegistry]
end
ci --> pipeline
push -->|deploy| env([Target\nEnvironment])
classDef highlight fill:#FCBE3E
class ci highlight Jenkins is one of the most widely adopted open-source automation servers. It orchestrates CI/CD pipelines through a rich plugin ecosystem and supports Pipeline as Code — pipeline definitions committed to the repository alongside application code in a file called Jenkinsfile2.
1. Setup — Containerized Jenkins
Running Jenkins inside Docker keeps the host machine clean and makes the environment reproducible across machines.
Source
The compose.yaml builds a custom image on top of jenkins/jenkins:jdk25 with extra tooling pre-installed:
| Tool | Purpose |
|---|---|
| Maven | Build Java projects |
| Docker CE | Build and push container images from within pipelines |
| kubectl | Deploy to Kubernetes clusters |
| AWS CLI | Interact with AWS services (ECR, ECS, EKS …) |
The Jenkins process is added to the docker group so that pipelines can invoke docker commands without requiring root. The host Docker socket (/var/run/docker.sock) is bind-mounted into the container for this purpose3.
Docker socket security
Mounting /var/run/docker.sock gives the container full control over the host Docker daemon. This is acceptable for local development, but should be hardened (e.g., rootless Docker or a dedicated build daemon) in shared or production environments.
Start the server:
jenkins/# docker compose up -d --build
[+] Running 2/2
✔ jenkins Created 0.1s
✔ Container jenkins Started 0.2s
Jenkins is now available at http://localhost:9080/.
2. Initial Configuration
Unlock Jenkins
On first start, Jenkins generates a random initial admin password to prevent unauthorised access4. Open the UI and enter it when prompted:
Retrieve the password with either command:
Windows — run as Administrator
Open the terminal as Administrator to avoid permission errors when reading the Jenkins secrets directory.
After entering the password, choose Install suggested plugins and create an admin user.
Number of Executors
An executor is a slot where Jenkins runs one pipeline stage or job. The default is 2; raising it lets multiple jobs run in parallel, which is useful when building several microservices in the same instance.
Manage Jenkins → Nodes → Built-In Node → Configure → Number of executors → 10
3. Credentials
Before creating pipelines that push Docker images, store the Docker Hub secret in Jenkins' credential store. Credentials are injected into pipelines at runtime — they are never written to disk or visible in logs5.
Manage Jenkins → Credentials → System → Global credentials → Add Credentials
| Field | Value |
|---|---|
| Kind | Username with password |
| Username | Your Docker Hub username |
| Password | Docker Hub access token (see tip below) |
| ID | dockerhub-credential |
Use access tokens, not your account password
Create a dedicated token with Read & Write scope at Docker Hub → Account Settings → Security → Access Tokens. If the token is ever compromised you can revoke it without touching your account password6.
4. Pipeline as Code
A Jenkinsfile describes the pipeline in a declarative DSL and lives in the root of the repository. Jenkins checks it out automatically when a build is triggered, so the pipeline evolves alongside the code7.
flowchart LR
repo[Git Repository\nJenkinsfile] -->|SCM checkout| jenkins[Jenkins Job]
jenkins --> s1[Stage: Build]
s1 --> s2[Stage: Test]
s2 --> s3[Stage: Build & Push Image]
s3 --> s4[Stage: Deploy] Jenkins supports two pipeline syntaxes:
| Syntax | Description |
|---|---|
| Declarative | Structured, opinionated DSL — recommended for most projects |
| Scripted | Full Groovy — maximum flexibility, steeper learning curve |
The examples below use the declarative syntax.
5. Examples
auth — Build only
The first pipeline compiles the auth module and produces the artifact:
Source
The single Build stage runs mvn -B -DskipTests clean install. The -B flag enables batch mode (no interactive prompts), which is required in non-interactive CI environments.
Creating a Job
Create a New Item, choose the Pipeline type, and set the Definition to Pipeline script from SCM. Jenkins will clone the repository and read the Jenkinsfile on every build.
auth-service — Build, package, and push
A complete pipeline that builds the service, creates a multi-platform Docker image, and pushes it to Docker Hub:
Source
Walkthrough
environment block
Declares two pipeline-wide variables. NAME is the fully-qualified Docker Hub repository. Referencing env.SERVICE inside NAME avoids duplicating the service name.
Dependencies stage
Triggers upstream Jenkins jobs before proceeding. wait: true blocks until each job completes successfully, guaranteeing that dependency artifacts are up to date before the current service is built.
Build stage
Compiles the project and produces the runnable JAR. Tests are intentionally skipped here — add a separate Test stage with mvn test if you want the pipeline to enforce test results.
Build & Push Image stage
withCredentials([usernamePassword(
credentialsId: 'dockerhub-credential',
usernameVariable: 'USERNAME',
passwordVariable: 'TOKEN')]) {
sh "docker login -u $USERNAME -p $TOKEN"
sh "docker buildx create --use \
--platform=linux/arm64,linux/amd64 \
--node multi-platform-builder-${env.SERVICE} \
--name multi-platform-builder-${env.SERVICE}"
sh "docker buildx build \
--platform=linux/arm64,linux/amd64 \
--push \
--tag ${env.NAME}:latest \
--tag ${env.NAME}:${env.BUILD_ID} \
-f Dockerfile ."
sh "docker buildx rm --force multi-platform-builder-${env.SERVICE}"
}
withCredentials— injects the Docker Hub token stored in step 3 into the build environment. The values are masked in all Jenkins logs5.docker buildx— creates a BuildKit builder that cross-compiles images for bothlinux/arm64(Apple Silicon, AWS Graviton) andlinux/amd64(standard x86-64)8.- Dual tags —
:latestprovides a stable reference;:<BUILD_ID>pins a specific Jenkins build so any image can be traced back to its source commit. - The builder instance is removed after the push to free up system resources.
-
HUMBLE, J.; FARLEY, D. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation. Addison-Wesley, 2010. ↩
-
Jenkins — Pipeline overview. Jenkins documentation. ↩
-
MERKEL, D. Docker: Lightweight Linux containers for consistent development and deployment. Linux Journal, 2014. See also Docker socket documentation. ↩
-
Jenkins — Initial setup. Jenkins documentation. ↩
-
Jenkins — Using credentials. Jenkins documentation. ↩↩
-
Docker Hub — Access tokens. Docker documentation. ↩
-
Jenkins — Pipeline as Code. Jenkins documentation. ↩
-
Docker Buildx — Multi-platform images. Docker documentation. ↩







