A complete, step-by-step guide to implement a GitLab CI/CD pipeline to:
✅ Deploy a Spring Boot Java application
✅ Use a specific GitLab Runner on an EC2 t2.medium instance with Docker executor
✅ Use Maven Docker image for builds
✅ Host a SonarQube dashboard on the same EC2 instance
🔧 PREREQUISITES
GitLab account and repository (your Spring Boot app should be in GitLab)
AWS EC2 instance (Ubuntu 22.04 LTS, t2.medium, security group open to ports 22, 8080, 9000, and 22)
A domain/subdomain (optional) if you want to expose SonarQube externally.
STEP 1: Launch & Prepare EC2 Instance
First, you need to fork the source code of this project from my GitLab repo. You can download it into your local machine by running the command git clone https://gitlab.com/uwadon1/Jenkins-Zero-To-Hero.git, and then cd into the directory.
a. Launch an Ubuntu EC2 (t2.medium)
Navigate to the AWS console and launch an instance. We will launch an Ubuntu instance that will serve as the runner, and give it the name "Gitlab-server" and select the AMI server to be Ubuntu Server 22.04 LTS
We will select the instance type t2.medium and make use of an existing keypair named "devopsapp" and leave the network settings as default.
We will leave the other options as default and click launch instance (we will leave the Security group setting for now and come back to make necessary adjustments to our S.G)
We will navigate to the SG overview page to make the manual adjustments to our security group settings to allow necessary traffic.
We will open the following traffic and allow access to IPv4 anywhere:
22 (SSH)
80 (Http)
443 (Https)
9000 (SonarQube)
b. SSH into the instance
Now we will SSH into the instance we just created, using the command below:
ssh -i ~/.ssh/devopsapp.pem ubuntu@35.91.67.242
(Ensure to replace with your instance IP address)
c. Update and install dependencies
We will first need to update the instance and install Docker using the command sudo apt update
We will also install Docker using the command
sudo apt install docker.io
Next, we will install JDK, which is a prerequisite for installing Jenkins, using the command
sudo apt install openjdk-17-jre
Sudo apt update
sudo apt install openjdk-11-jre
STEP 2: Set up and Configure SonarQube on EC2
To install SonarQube, we need to ensure Java is already installed.
The next thing we will have to do is move to the root directory, so that we can have admin privileges, using the commands below:
sudo su -
apt install unzip
adduser sonarqube
sudo su - sonarqube
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.4.0.54424.zip
unzip *
chmod -R 755 /home/sonarqube/sonarqube-9.4.0.54424
chown -R sonarqube:sonarqube /home/sonarqube/sonarqube-9.4.0.54424
cd sonarqube-9.4.0.54424/bin/linux-x86–64/
./sonar.sh start
We can verify the status of SonarQube via this command: ./sonar.sh status
Wait for SonarQube to start (2–3 minutes), then access your SonarQube on your browser via http://54.245.159.36:9000/ {ensure to input your EC2 IP address)
The Default login details will be: admin/admin. You will need to change the default password to a new one in the next tab.
Next, we will generate a Sonar Token. To do that, navigate to 'my account' at the top right corner, then go to the settings tab, and click on generate token to generate a token to allow GitLab CI/CD to communicate with your SonarQube server.
STEP 3: Secure Your CI/CD
We will input the SonarQube token into our GitLab variables. To do this, we will go to the settings tab on GitLab, select CICD and click on variables. Go to GitLab → Settings → CI/CD → Variables
Add the token API key to the GitLab CICD variables:
Key = SONAR_LOGIN
Value = cd14b6f8bb12f8a7a94127f3ce1044516d33a5fa
Configure GitLab CI/CD Variables
Add these variables (mask sensitive ones):
-
SONAR_HOST_URL:http://<your-ec2-public-ip>:9000 -
SONAR_LOGIN: The token you generated in SonarQube -
DOCKER_USENAME: (if pushing to Docker registry) -
DOCKER_PASSWORD: (if pushing to Docker registry)
STEP 4: Register GitLab Runner on EC2
We will have to create a runner, so we can always have backend access to it and be in full control of the runner. We will install the runner on our instance. To set up the runner, we will navigate to the settings, then CICD, then runners: Settings → CI/CD → Variables
a. Install GitLab Runner
# Download the binary for your system
sudo curl -L - output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# Give it permission to execute
sudo chmod +x /usr/local/bin/gitlab-runner
# Create a GitLab Runner user
sudo useradd - comment 'GitLab Runner' - create-home gitlab-runner - shell /bin/bash
# Install and run as a service
sudo gitlab-runner install - user=gitlab-runner - working-directory=/home/gitlab-runner
sudo gitlab-runner start
Command to register runner
sudo gitlab-runner register - url https://gitlab.com - token glrt-92fA16tAk07hUeItqxgyj286MQpwOjE2Y2QwYwp0OjMKdTpneHBzNhg.01.1j1fxtc8g
Follow prompts:
GitLab URL: https://gitlab.com/
Registration token: (get from GitLab project → Settings → CI/CD → Runners → Expand)
Description: my-runner
Tags: Docker
Executor: docker
Docker image: uwadon01/maven-uwadon1-docker-agent:v1
An equivalent Dockerfile sample that replicates the uwadon01/maven-uwadon1-docker-agent:v1 image with modern best practices:
Reconstructed Dockerfile
dockerfile
# Start with the same base as the original (Ubuntu-based)
FROM ubuntu:20.04
# Set environment variables to match original
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
JAVA_VERSION=jdk-11.0.11+9 \
JAVA_HOME=/opt/java/openjdk \
MAVEN_VERSION=3.8.1 \
MAVEN_HOME=/usr/share/maven \
MAVEN_CONFIG=/root/.m2
# Install base dependencies (matches original apt commands)
RUN apt-get update && \
apt-get install -y - no-install-recommends \
curl \
ca-certificates \
gnupg \
git \
bash \
docker.io \
&& rm -rf /var/lib/apt/lists/*
# Install AdoptOpenJDK 11 (matches original version)
RUN mkdir -p /opt/java/openjdk && \
curl -L https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11+9/OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz | \
tar -xz -C /opt/java/openjdk - strip-components=1
# Install Maven 3.8.1 (matches original)
RUN mkdir -p /usr/share/maven /usr/share/maven/ref && \
curl -L https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz | \
tar -xz -C /usr/share/maven - strip-components=1 && \
ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
# Copy the original maven entrypoint script (recreated from inspection)
RUN echo $'#!/bin/sh\n\
# Licensed to the Apache Software Foundation (ASF) under one\n\
# or more contributor license agreements. See the NOTICE file\n\
# distributed with this work for additional information\n\
# regarding copyright ownership. The ASF licenses this file\n\
# to you under the Apache License, Version 2.0 (the\n\
# "License"); you may not use this file except in compliance\n\
# with the License. You may obtain a copy of the License at\n\
#\n\
# http://www.apache.org/licenses/LICENSE-2.0\n\
#\n\
# Unless required by applicable law or agreed to in writing,\n\
# software distributed under the License is distributed on an\n\
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n\
# KIND, either express or implied. See the License for the\n\
# specific language governing permissions and limitations\n\
# under the License.\n\
\n\
# For backwards compatibility with older versions of Docker\n\
if [ "$1" = "mvn" ]; then\n\
shift\n\
fi\n\
exec "$@"' > /usr/local/bin/mvn-entrypoint.sh && \
chmod +x /usr/local/bin/mvn-entrypoint.sh
# Set working directory and entrypoint
WORKDIR /workspace
ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"]
CMD ["mvn"]
# Verify installations
RUN mvn - version && \
java - version && \
docker - version
How to Build and Use
- Build the image:
docker build -t maven-uwadon1-docker-agent:v1 . - Verify it matches the original:
docker run -it - rm maven-uwadon1-docker-agent:v1 bash -c "mvn - version; docker - version; java - version" - Push to your registry:
docker tag maven-uwadon1-docker-agent:v1 yourusername/maven-yourname-docker-agent:v1
docker push yourusername/maven-yourname-docker-agent:v1
For GitLab CI/CD Usage
In your .gitlab-ci.yml:
yaml
image: uwadon01/maven-uwadon1-docker-agent:v1
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
build:
script:
- mvn clean install
- docker build -t myapp .
This Dockerfile image sample provides the same functionality as the original but with a more maintainable and transparent construction.
PS: You can decide to use my Docker image, but you have to retag it; here's how to retag an existing image for use:
Pull the original image:
docker pull uwadon01/maven-uwadon1-docker-agent:v1
- Retag the image (to your own registry or for local use):
docker tag uwadon01/maven-uwadon1-docker-agent:v1 yourusername/maven-yourname-docker-agent:v1 - Push to your registry (if needed)
docker push yourusername/maven-yourname-docker-agent:v1You can view the runner that was just created. You will see a green dot beside it, which shows it is now active.
STEP 5: Create/Edit the .gitlab-ci.yml in your Spring Boot repo
image: 'uwadon01/maven-uwadon1-docker-agent:v1'
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ''
# SONAR_HOST_URL: http://54.237.226.245:9000
# SONAR_LOGIN: ${SONAR_LOGIN}
# Define the default working directory for the pipeline
before_script:
- cd java-maven-sonar-argocd-helm-k8s/spring-boot-app
stages:
- build
- test
- sonarqube
# - dockerization
build-job:
stage: build
tags:
- docker
script:
- echo "Compiling the code…"
- mvn clean package
unit-test-job:
stage: test
tags:
- docker
script:
- echo "Running unit tests… This will take about 60 seconds."
- mvn test
code-quality-job:
stage: sonarqube
tags:
- docker
script:
- echo "scanning code"
- mvn sonar:sonar -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.login=${SONAR_LOGIN}
#docker:
# stage: dockerization
# image: docker:19.03
# services:
# - name: docker:dind
# script:
# - whoami
# - docker build -t gitlab-cicd-demo:latest .
# - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
# - docker tag gitlab-cicd-demo:latest $DOCKER_USERNAME/gitlab-cicd-demo:latest
# - docker push $DOCKER_USERNAME/gitlab-cicd-demo:latest
STEP 6: Push and Trigger Pipeline
git add .gitlab-ci.yml
git commit -m "Add GitLab CI/CD pipeline"
git push origin main
The pipeline will now run the various commands listed in your .gitlab.yml file
You can view the progress in the pipeline dashboard
This command will build the Spring Boot app using our Docker image, run the unit tests and send code analysis to SonarQube. You can view the result by logging in to your SonarQube project dashboard
✅ TROUBLESHOOTING
Issue & Solution
Runner says "no tags match"
Make sure your job has the correct tags: matching your runnerSonarQube is not accessible
Ensure port 9000 is open and the container is runningApp not reachable
Make sure your app binds to 0.0.0.0:8080, not localhostDocker permission issues
Ensure the user is in the Docker groupSonarQube Issues
Check logs:docker logs sonarqube, Ensure enough memory (t2.medium should be sufficient)Maven Build Issues
Clear cache:mvn dependency:purge-local-repository, Check network connectivity from EC2
NB: To edit the runner configuration:
sudo nano /etc/gitlab-runner/config.toml
Afterwards, you can restart the runner:
sudo gitlab-runner restart
Ensure your Spring Boot project has:
-
pom.xmlwith required dependencies and.gitlab-ci.ymlat the root
This comprehensive guide provides a complete walkthrough to set up a GitLab CI/CD pipeline for a Spring Boot Java application using a specific GitLab Runner on an EC2 t2.medium instance with Docker executor, Maven image, and hosting SonarQube on the same instance.



























Top comments (0)