Once you’ve built a Docker container image for your Spring Boot app, the next challenge is figuring out how to write Kubernetes manifests — especially around health checks and handling sensitive configuration, which have their own K8s-specific patterns.

This article walks through the manifests for Deployment, Service, ConfigMap, and Secret step by step. For building and pushing Docker images, see Running a Spring Boot App in a Docker Container.

Prerequisites

This article assumes the following are already in place:

  • Your Spring Boot 3.x app’s Docker image has been pushed to a registry
  • You can access a K8s cluster via kubectl

You will create four resources: Deployment, Service, ConfigMap, and Secret.

Preparing Your Spring Boot App

Make sure spring-boot-starter-actuator is in your dependencies so your app can respond to K8s probes.

<!-- pom.xml -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'

For an introduction to Actuator basics, see Getting Started with Spring Boot Actuator.

Next, enable the Kubernetes probe endpoints in application.yml:

management:
  endpoint:
    health:
      probes:
        enabled: true
  endpoints:
    web:
      exposure:
        include: health

Setting management.endpoint.health.probes.enabled=true enables /actuator/health/liveness and /actuator/health/readiness. Note that in Spring Boot 3.x, probes are automatically enabled when the KUBERNETES_SERVICE_HOST environment variable is set (i.e., when running on a K8s cluster), so whether to set this explicitly depends on your environment.

Creating the Deployment Manifest

Create a deployment.yaml file. Include both livenessProbe and readinessProbe in the same step.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      # imagePullSecrets: [{name: regcred}]  # Add this for private registries
      containers:
        - name: myapp
          image: your-registry/myapp:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "500m"
              memory: "1Gi"
          envFrom:
            - configMapRef:
                name: myapp-config
            - secretRef:
                name: myapp-secret
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 5

livenessProbe checks whether the container is “alive” — if it fails, the Pod is automatically restarted. readinessProbe checks whether the container is ready to accept traffic — if it fails, the Pod is removed from the Service’s routing targets.

Set initialDelaySeconds based on your Spring Boot startup time. If it’s too short, the probe will fail before the app finishes starting, resulting in a CrashLoopBackOff. If startup takes 30 seconds, setting it to around 60 seconds provides a safe buffer. If you’re using a private registry, don’t forget to uncomment imagePullSecrets and configure credentials — otherwise you’ll get an ImagePullBackOff error.

Creating the Service Manifest

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

type: ClusterIP makes the service accessible only within the cluster and is the recommended default. If you want to expose the service directly to the internet in a cloud environment, change it to LoadBalancer and a cloud provider load balancer will be provisioned automatically.

Injecting Environment-Specific Properties with ConfigMap

Non-sensitive configuration values should be managed via ConfigMap.

apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  SPRING_PROFILES_ACTIVE: "production"
  APP_EXTERNAL_API_URL: "https://api.example.com"

Passing SPRING_PROFILES_ACTIVE as an environment variable causes application-production.yml to be loaded automatically. For more on profiles and external properties, see the Spring Boot Properties Configuration Guide and Using Spring Boot Profiles to Switch Environment-Specific Configuration Safely.

Passing Database Credentials with Secret

Sensitive values such as passwords should be managed via Secret.

apiVersion: v1
kind: Secret
metadata:
  name: myapp-secret
type: Opaque
stringData:
  SPRING_DATASOURCE_URL: "jdbc:postgresql://db:5432/mydb"
  SPRING_DATASOURCE_USERNAME: "appuser"
  SPRING_DATASOURCE_PASSWORD: "your-secret-password"

Using stringData allows you to write values in plain text. If you use the data field instead, values must be Base64-encoded.

One important caveat: Kubernetes Secrets are only Base64-encoded by default — they are not encrypted. Since they are stored as-is in etcd, consider enabling etcd encryption or adopting an external secret management tool such as External Secrets Operator for production environments. The rule of thumb for choosing between ConfigMap and Secret is simple: use ConfigMap for settings that are safe to expose, and Secret for passwords and API keys.

Applying and Verifying with kubectl apply

# Apply
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# Check status
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>

# Verify access from local machine
kubectl port-forward svc/myapp 8080:80

If kubectl get pods shows a STATUS of Running and READY of 2/2 (when replicas is 2), the deployment is successful.

After updating a ConfigMap or Secret, a Pod restart is required. Environment variables loaded via envFrom are only read at startup, so run kubectl rollout restart deployment/myapp to apply the changes.

Summary

Key points for deploying a Spring Boot app to Kubernetes:

  • Set management.endpoint.health.probes.enabled=true (may be auto-enabled in K8s environments via KUBERNETES_SERVICE_HOST)
  • Wire /actuator/health/liveness to livenessProbe and /actuator/health/readiness to readinessProbe
  • Set initialDelaySeconds with enough buffer beyond your app’s startup time
  • Manage non-sensitive config in ConfigMap and passwords in Secret
  • Secrets are only Base64-encoded, not encrypted — additional safeguards are needed in production
  • Pod restarts are required after updating ConfigMap or Secret

Integration with Helm charts and CI/CD pipelines will be covered in a future article.