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
: Yourkubeconfig
file base64-encoded (cat ~/.kube/config | base64 -w0
)CI_REGISTRY_USER
andCI_REGISTRY_PASSWORD
: From your GitLab Container Registry- Keep variables masked and protected.
๐งช Testing the Pipeline
Push to the main
branch and GitLab will:
- Build the Docker image
- Push it to the private registry
- Authenticate to your Kubernetes cluster
- Apply the deployment and service
- Restart the deployment and verify rollout
๐งน Common Errors
Error | Cause | Solution |
---|---|---|
resource name may not be empty | Missing metadata.name after template substitution | Ensure all variables ($APP_NAME , etc.) are exported before envsubst |
deployment.yaml does not exist | Wrong path or missing file | Check your k8s/ folder and use correct relative path |
Image not pulling | Missing or invalid regcred | Use $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