๐ฆ Deploying a Spring Boot Banking App Using GitLab CI/CD with Self-Hosted Runners

Table of contents
- ๐ Why GitLab for CI/CD?
- ๐ง GitHub vs GitLab vs Bitbucket: Quick Comparison
- ๐๏ธ GitLab Structure: Groups & Projects
- ๐ Secure Access via SSH
- ๐ค Setting Up GitLab Runners
- ๐ GitLab CI/CD Pipeline Structure
- ๐ Secrets & Variables
- ๐ฆ Using Artifacts to Store Logs
- ๐งฝ Cleanup Job (Docker Prune)
- ๐งช Sample Job Setup
- ๐ Pre-Deployment Checklist
- ๐ Final Thoughts: My Experience
- โ๏ธ Challenges Faced
- โ Conclusion

In this article, I share my experience deploying a Spring Boot-based Banking Application using GitLab CI/CD pipelines, leveraging self-hosted runners, secure secrets, artifacts, and robust pipeline structures. This setup is fully automated, scalable, and production-ready ideal for DevOps, DevSecOps, and modern application deployments.
๐ Why GitLab for CI/CD?
GitLab is more than just a Git repository platform โ itโs a complete DevOps solution that offers:
Built-in CI/CD capabilities
Self-hosted or SaaS deployment options
Secure handling of secrets and variables
Runner flexibility (cloud and custom)
Artifact management and group-level collaboration
Unlike GitHub (which requires GitHub Actions or Jenkins for automation), GitLab CI/CD is integrated by default and works out of the box.
๐ง GitHub vs GitLab vs Bitbucket: Quick Comparison
Feature | GitHub | GitLab | Bitbucket |
CI/CD | GitHub Actions | Built-in GitLab CI/CD โ | Bitbucket Pipelines |
Self-Hosted | GitHub Enterprise | Free with GitLab CE โ | Bitbucket Server |
DevOps Support | External Integrations | End-to-End Built-in โ | JIRA Integration |
Secrets Mgmt | Encrypted Secrets | Group/Project Variables | Environment Variables |
๐๏ธ GitLab Structure: Groups & Projects
In GitLab, resources are organized into:
Groups: Like teams or departments. I created a group called
banking-devops
.Projects: Individual repositories inside a group, e.g.,
banking-app
.
This structure makes it easy to manage access, shared variables, and pipelines across multiple services or microservices.
๐ GitHub uses โrepositories under users or organizationsโ, while GitLab uses โprojects inside groupsโ โ more aligned with enterprise/team structure.
๐ Secure Access via SSH
To securely push/pull code between my EC2 runner and GitLab:
I generated SSH keys on the EC2 instance.
Added the public key to GitLab โ
User Settings โ SSH Keys
.Kept the private key safely on the server.
Now, I can perform Git operations (clone, push, pull) without password prompts.
๐ค Setting Up GitLab Runners
SaaS vs Self-Hosted Runners
Type | Description |
SaaS Runners | Shared runners provided by GitLab (default) |
Self-Hosted Runners | Custom runners you install on your own VM (e.g., EC2) โ |
Steps I Followed:
Launched a t3.medium EC2 instance.
Installed Docker, Docker Compose, and GitLab Runner.
Registered the runner with my GitLab project:
sudo gitlab-runner register
Chose
shell
executor and added a tag (bank
) to bind this runner in the.gitlab-ci.yml
.
๐ GitLab CI/CD Pipeline Structure
Hereโs the structure of my .gitlab-ci.yml
file used for the Banking App (Spring Boot):
stages:
- build
- test
- push
- deploy
- cleanup
Each job is assigned to a stage, and the stages run sequentially, while jobs within the same stage can run in parallel.
๐ Secrets & Variables
GitLab supports two types of variables:
Type | Where to set | Use case |
Group-level โ | Group โ Settings โ CI/CD โ Variables | Best for shared secrets across projects |
Project-level | Project โ Settings โ CI/CD โ Variables | Project-specific secrets |
My Use:
I added DOCKERHUB_USER
and DOCKERHUB_PASS
at the group level (masked + protected). This allowed me to securely authenticate with Docker Hub from any project under the group.
๐ Masked variables are hidden in logs, even if they fail. Always use this for credentials.
๐ฆ Using Artifacts to Store Logs
Artifacts allow you to persist logs or files generated during pipeline execution. I used this to store:
build.log
deploy.log
Example:
artifacts:
paths:
- logs/
expire_in: 1 day
These logs are downloadable from the GitLab UI after the pipeline finishes โ great for debugging and compliance.
๐งฝ Cleanup Job (Docker Prune)
After deployment, I added a cleanup stage to remove unused Docker objects:
docker system prune -af
docker system prune -a --volumes --force
โ ๏ธ This wipes all unused containers, images, volumes โ use with caution. If you're running containers that arenโt tagged or are in exited state, they'll be deleted too.
๐งช Sample Job Setup
Each job includes:
stage
: defines where the job runs in the pipelinescript
: steps to runtags
: ensures it runs on the correct self-hosted runner
Example: Build Stage
build_job:
stage: build
tags:
- bank
script:
- mkdir logs
- echo "Build started at $(date)" > logs/build.log
- docker build -t bank-app1:latest . >> logs/build.log
- echo "Build ended at $(date)" >> logs/build.log
๐ Pre-Deployment Checklist
Before triggering your first pipeline, ensure:
Docker & Docker Compose installed on runner
GitLab runner registered
gitlab-runner
andubuntu
user added todocker
groupnewgrp docker
executedSecrets configured at group level
Firewall/inbound rules open on EC2
๐ Final Thoughts: My Experience
Setting up a CI/CD pipeline for a Spring Boot Banking App taught me how to:
Use GitLabโs built-in CI/CD features effectively
Manage secure deployments with group-level secrets
Set up and maintain self-hosted runners
Automate build, test, deployment, and cleanup
Debug pipeline issues using artifacts and logs
โ๏ธ Challenges Faced
Docker permission issues (resolved by managing user groups)
DockerHub login failures from CI (fixed using
--password-stdin
)Runner stopping unexpectedly after jobs (solved by editing
.bash_logout
)
โ Conclusion
Using GitLab CI/CD for deploying my Spring Boot Banking App helped me automate everything from code commit to production deployment. With self-hosted runners, secure secrets, stage-wise pipelines, and artifact management, I now have a scalable, reliable deployment flow that fits enterprise-level requirements.
Subscribe to my newsletter
Read articles from Atharv Karpe directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
