In this tutorial, we will integrate Trivy and SonarQube with GitLab CI/CD to build a DevSecOps pipeline for a Java web application.
The pipeline will automatically:
Build → Trivy Security Scan → SonarQube Code Analysis → Quality Gate → Deploy to Tomcat
Pipeline Flow
Developer → GitLab → GitLab Runner → Maven Build → Trivy Scan → SonarQube Scan → Quality Gate
PASS → Deploy WAR → Tomcat → Java Web Application
FAIL → Pipeline Stops → No Deployment
Prerequisites
Before starting this lab, make sure the following are ready:
- SonarQube is installed and running.
- Java Maven project is available in GitLab.
- GitLab Runner is available.
- Tomcat is installed and running.
- Tomcat Manager is configured for remote deployment.
- SonarQube Quality Gate is configured.
- GitLab Runner can reach both SonarQube and Tomcat.
What We Will Implement
We will perform the following steps:
- Generate a SonarQube authentication token.
- Configure SonarQube and Tomcat credentials as GitLab CI/CD variables.
- Create a
.gitlab-ci.ymlpipeline. - Build the Java application using Maven.
- Scan the project using Trivy.
- Analyze the source code using SonarQube.
- Enforce the SonarQube Quality Gate.
- Deploy the WAR file to Tomcat only when all previous stages succeed.
Step 1 – Create a SonarQube Token
Login to SonarQube.
Go to:
My Account → Security → Tokens
Enter a token name and generate an analysis token.
Copy the generated token.
Save the token securely. You will add it to GitLab as a CI/CD variable.
Step 2 – Configure GitLab CI/CD Variables
Go to your GitLab project:
Settings → CI/CD → Variables
Click Add variable.
Configure the following variables:
| Variable | Purpose |
|---|---|
SONAR_HOST_URL | SonarQube server URL |
SONAR_TOKEN | SonarQube authentication token |
TOMCAT_HOST | Tomcat hostname/IP and port |
TOMCAT_USER | Tomcat Manager username |
TOMCAT_PASSWORD | Tomcat Manager password |
Do not hard-code passwords or tokens inside .gitlab-ci.yml.
Step 3 – Create the GitLab CI/CD Pipeline
Go to the GitLab repository containing your Java project.
Create a new file:
.gitlab-ci.yml
Our pipeline contains four stages:
stages:
- build
- security
- sonar
- deployThe pipeline flow is:
Build → Trivy → SonarQube + Quality Gate → Deploy
Step 4 – Complete .gitlab-ci.yml
stages:
- build
- security
- sonar
- deploy
# -------------------------
# BUILD
# -------------------------
build_war:
stage: build
image: maven:3.8.6-eclipse-temurin-11
script:
- echo "Building WAR file using Maven"
- mvn clean package -f MyWebApp/pom.xml
- echo "Listing target directory"
- ls -la MyWebApp/target
artifacts:
paths:
- MyWebApp/target/*.war
expire_in: 1 hour
# -------------------------
# TRIVY SECURITY SCAN
# -------------------------
trivy_scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- echo "Running Trivy security scan"
- trivy fs --severity HIGH,CRITICAL --exit-code 1 --no-progress .
allow_failure: false
# -------------------------
# SONARQUBE CODE ANALYSIS
# -------------------------
sonarqube_scan:
stage: sonar
image: maven:3.9.6-eclipse-temurin-17
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: "0"
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- echo "Running SonarQube code analysis"
- |
mvn sonar:sonar \
-f MyWebApp/pom.xml \
-Dsonar.projectKey=MyWebApp \
-Dsonar.host.url="${SONAR_HOST_URL}" \
-Dsonar.token="${SONAR_TOKEN}" \
-Dsonar.qualitygate.wait=true
allow_failure: false
# -------------------------
# DEPLOY TO TOMCAT
# -------------------------
deploy_to_tomcat:
stage: deploy
image: curlimages/curl:latest
dependencies:
- build_war
script:
- echo "Deploying WAR file to Tomcat"
- |
curl --fail -v \
-u "${TOMCAT_USER}:${TOMCAT_PASSWORD}" \
-T MyWebApp/target/MyWebApp.war \
"http://${TOMCAT_HOST}/manager/text/deploy?path=/MyWebApp&update=true"Commit the file.
Step 5 – Pipeline Execution
As soon as you commit .gitlab-ci.yml, GitLab creates and starts the pipeline.
Go to:
Build → Pipelines
You should see the pipeline execute in this order:
1. Build
Maven compiles the Java application and generates the WAR file.
↓
2. Trivy Security Scan
Trivy scans the project filesystem for vulnerabilities.
If vulnerabilities matching the configured HIGH or CRITICAL severity threshold are detected, the Trivy command returns a non-zero exit code and the pipeline stops.
↓
3. SonarQube Code Analysis
SonarQube analyzes the application code.
The pipeline waits for the SonarQube Quality Gate result because we configured:
-Dsonar.qualitygate.wait=trueIf the Quality Gate fails, the SonarQube job fails and deployment does not proceed.
↓
4. Deploy to Tomcat
Only when the previous stages succeed does GitLab execute the deployment job.
The WAR artifact generated during the build stage is deployed to Tomcat using the Tomcat Manager API.
Step 6 – Verify the SonarQube Report
Login to SonarQube and open the project.
Review the analysis results, including the applicable:
- Bugs
- Vulnerabilities
- Code Smells
- Security Hotspots
- Duplications
- Coverage
- Quality Gate status
Verify that the Quality Gate shows PASSED.
Step 7 – Verify the Application
After the pipeline completes successfully, open the Java web application running on Tomcat.
The complete DevSecOps workflow is now:
Developer
↓
GitLab Repository
↓
GitLab Runner
↓
Maven Build
↓
Trivy Security Scan
↓
SonarQube Code Analysis
↓
Quality Gate
↓
Tomcat Deployment
↓
Java Web Application
Key Takeaway
GitLab manages and orchestrates the CI/CD pipeline, while GitLab Runner executes the jobs using the Docker images defined in .gitlab-ci.yml.
The application is deployed only after the configured Trivy security check and SonarQube Quality Gate succeed, providing security and quality checks before deployment.


