How to Automatically Deploy a Spring Boot App to Kubernetes with GitLab CI/CD

If you’re building a Spring Boot application and want to automate your deployment to Kubernetes using GitLab CI/CD, this beginner-friendly tutorial will walk you through every step β€” no advanced DevOps skills required.


βœ… What You’ll Learn

  • How to build and push a Docker image from GitLab
  • How to authenticate Kubernetes with a private container registry
  • How to automatically apply your deployment using GitLab pipelines
  • How to make your image tag dynamic for better version control

πŸ“¦ Prerequisites

Before we dive in, make sure you have:

  • A GitLab repository (self-hosted or GitLab.com)
  • A GitLab Runner connected and working
  • A Kubernetes cluster (like K3s, Minikube, or any hosted K8s)
  • A private container registry (e.g., GitLab Container Registry)
  • A basic Spring Boot project with a Dockerfile

πŸ— Project Structure

Your project should look like this:

my-spring-app/
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ k8s/
β”‚   β”œβ”€β”€ deployment-template.yaml
β”‚   └── service-template.yaml
β”œβ”€β”€ .gitlab-ci.yml

🐳 Sample Dockerfile

FROM eclipse-temurin:17-jdk
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

Make sure your Spring Boot JAR is built and available in target/ before the build step.


🎯 Kubernetes Deployment Template

Your k8s/deployment-template.yaml should include placeholders:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${APP_NAME}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ${APP_NAME}
  template:
    metadata:
      labels:
        app: ${APP_NAME}
    spec:
      containers:
        - name: ${APP_NAME}
          image: registry.example.com/project/${APP_NAME}:${IMAGE_TAG}
          ports:
            - containerPort: 8080
      imagePullSecrets:
        - name: regcred

The ${APP_NAME} and ${IMAGE_TAG} will be dynamically replaced by GitLab.


🧠 Why Use Templates?

By using templates and envsubst, you can generate Kubernetes manifests with real values during CI/CD β€” without hardcoding anything.


βš™οΈ GitLab CI/CD Configuration

Your .gitlab-ci.yml should contain:

stages:
  - build
  - deploy

variables:
  IMAGE_TAG: $CI_COMMIT_SHORT_SHA
  APP_NAME: springboot-app

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG .
    - docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG

deploy:
  stage: deploy
  image: bitnami/kubectl:latest
  before_script:
    - echo "$KUBE_CONFIG_DATA" | base64 -d > kubeconfig.yaml
  script: |
    envsubst < k8s/deployment-template.yaml > k8s/deployment.yaml
    envsubst < k8s/service-template.yaml > k8s/service.yaml

    kubectl --kubeconfig=kubeconfig.yaml delete secret regcred --ignore-not-found
    kubectl --kubeconfig=kubeconfig.yaml create secret docker-registry regcred \
      --docker-server=$CI_REGISTRY \
      --docker-username=gitlab-ci-token \
      --docker-password=$CI_JOB_TOKEN \
      --docker-email=you@example.com

    kubectl --kubeconfig=kubeconfig.yaml apply -f k8s/deployment.yaml
    kubectl --kubeconfig=kubeconfig.yaml apply -f k8s/service.yaml

    kubectl --kubeconfig=kubeconfig.yaml rollout restart deployment/$APP_NAME
    kubectl --kubeconfig=kubeconfig.yaml rollout status deployment/$APP_NAME
  only:
    - main

πŸ” How to Store Secrets

In GitLab β†’ Settings β†’ CI/CD β†’ Variables, add:

  • KUBE_CONFIG_DATA: Your kubeconfig file base64-encoded (cat ~/.kube/config | base64 -w0)
  • CI_REGISTRY_USER and CI_REGISTRY_PASSWORD: From your GitLab Container Registry
  • Keep variables masked and protected.

πŸ§ͺ Testing the Pipeline

Push to the main branch and GitLab will:

  1. Build the Docker image
  2. Push it to the private registry
  3. Authenticate to your Kubernetes cluster
  4. Apply the deployment and service
  5. Restart the deployment and verify rollout

🧹 Common Errors

ErrorCauseSolution
resource name may not be emptyMissing metadata.name after template substitutionEnsure all variables ($APP_NAME, etc.) are exported before envsubst
deployment.yaml does not existWrong path or missing fileCheck your k8s/ folder and use correct relative path
Image not pullingMissing or invalid regcredUse $CI_JOB_TOKEN with gitlab-ci-token when creating the secret

βœ… Final Thoughts

This approach helps you automate Kubernetes deployments directly from GitLab using a lightweight setup. No Helm, no ArgoCD β€” just good old YAML and GitLab CI/CD.

You can extend this setup with:

  • Namespaces per environment
  • Helm support
  • Canary deployments
  • Monitoring rollout status with Slack notifications
This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back