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

Atharv KarpeAtharv Karpe
5 min read

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

FeatureGitHubGitLabBitbucket
CI/CDGitHub ActionsBuilt-in GitLab CI/CD โœ…Bitbucket Pipelines
Self-HostedGitHub EnterpriseFree with GitLab CE โœ…Bitbucket Server
DevOps SupportExternal IntegrationsEnd-to-End Built-in โœ…JIRA Integration
Secrets MgmtEncrypted SecretsGroup/Project VariablesEnvironment 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:

  1. I generated SSH keys on the EC2 instance.

  2. Added the public key to GitLab โ†’ User Settings โ†’ SSH Keys.

  3. 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

TypeDescription
SaaS RunnersShared runners provided by GitLab (default)
Self-Hosted RunnersCustom runners you install on your own VM (e.g., EC2) โœ…

Steps I Followed:

  1. Launched a t3.medium EC2 instance.

  2. Installed Docker, Docker Compose, and GitLab Runner.

  3. Registered the runner with my GitLab project:

     sudo gitlab-runner register
    
  4. 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:

TypeWhere to setUse case
Group-level โœ…Group โ†’ Settings โ†’ CI/CD โ†’ VariablesBest for shared secrets across projects
Project-levelProject โ†’ Settings โ†’ CI/CD โ†’ VariablesProject-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 pipeline

  • script: steps to run

  • tags: 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 and ubuntu user added to docker group

  • newgrp docker executed

  • Secrets 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.

0
Subscribe to my newsletter

Read articles from Atharv Karpe directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Atharv Karpe
Atharv Karpe