Full Stack • Java • System Design • Cloud • AI Engineering

Spring Boot with AWS EKS

Learn how to deploy a Spring Boot application on AWS EKS step by step using Docker, Amazon ECR, Kubernetes Deployment, Service, kubectl, eksctl, AWS CLI, Load Balancer, and production best practices.


Introduction

In the previous article, we deployed a Spring Boot application on AWS ECS Fargate.

ECS Fargate is a managed container platform from AWS. But many enterprise companies use Kubernetes for container orchestration.

AWS EKS stands for Elastic Kubernetes Service. It allows us to run Kubernetes workloads on AWS without managing the Kubernetes control plane manually.

In this article, we will deploy a Spring Boot application on AWS EKS step by step.


What You Will Learn

  • What is AWS EKS?
  • What is Kubernetes?
  • ECS vs EKS
  • How to Dockerize Spring Boot
  • How to push Docker image to Amazon ECR
  • How to create EKS Cluster using eksctl
  • How to deploy Spring Boot using Kubernetes YAML
  • How to expose application using LoadBalancer service
  • How to check pods, services, and logs
  • Input and output examples
  • Common errors and fixes
  • Production best practices

What is AWS EKS?

AWS EKS is a managed Kubernetes service.

AWS manages the Kubernetes control plane.

You manage:

  • Worker nodes
  • Pods
  • Deployments
  • Services
  • ConfigMaps
  • Secrets
  • Ingress
  • Application deployments

ECS vs EKS

Feature ECS EKS
Orchestration AWS native Kubernetes
Learning curve Medium High
Portability AWS-focused Cloud portable
YAML complexity Lower Higher
Enterprise usage High Very high
Best for AWS container apps Kubernetes-based platforms

High Level Architecture

flowchart TD
    DEV[Developer Machine]
    APP[Spring Boot App]
    DOCKER[Docker Image]
    ECR[Amazon ECR]
    EKS[AWS EKS Cluster]
    POD[Spring Boot Pod]
    SVC[Kubernetes Service]
    LB[AWS Load Balancer]
    USER[User Browser]

    DEV --> APP
    APP --> DOCKER
    DOCKER --> ECR
    ECR --> EKS
    EKS --> POD
    POD --> SVC
    SVC --> LB
    USER --> LB

Production Architecture

flowchart TD
    U[Users]
    R53[Route 53]
    ALB[Application Load Balancer]
    INGRESS[Kubernetes Ingress]
    EKS[EKS Cluster]

    subgraph NODEGROUP["Managed Node Group"]
        POD1[Spring Boot Pod 1]
        POD2[Spring Boot Pod 2]
        POD3[Spring Boot Pod 3]
    end

    ECR[Amazon ECR]
    RDS[(Amazon RDS)]
    CW[CloudWatch Logs]

    U --> R53
    R53 --> ALB
    ALB --> INGRESS
    INGRESS --> EKS
    EKS --> POD1
    EKS --> POD2
    EKS --> POD3

    ECR --> POD1
    ECR --> POD2
    ECR --> POD3

    POD1 --> RDS
    POD2 --> RDS
    POD3 --> RDS

    POD1 --> CW
    POD2 --> CW
    POD3 --> CW

EKS Important Concepts

Concept Meaning
Cluster Kubernetes cluster managed by AWS
Node Group EC2 worker nodes running pods
Pod Smallest deployable Kubernetes unit
Deployment Manages pod replicas
Service Exposes pods internally or externally
LoadBalancer Creates AWS Load Balancer
Namespace Logical grouping of resources
ConfigMap Non-sensitive configuration
Secret Sensitive configuration
ECR Docker image registry

Prerequisites

You need:

AWS Account
AWS CLI
eksctl
kubectl
Docker Desktop
Java 17 or Java 21
Maven
Spring Boot Application
IAM permissions for EKS, EC2, ECR, CloudFormation, IAM

Check Java:

java -version

Output:

openjdk version "17.0.x"

Check Docker:

docker --version

Output:

Docker version 25.x.x

Check AWS CLI:

aws --version

Output:

aws-cli/2.x.x

Check kubectl:

kubectl version --client

Output:

Client Version: v1.x.x

Check eksctl:

eksctl version

Output:

0.x.x

Step 1: Create Spring Boot Application

Create project:

springboot-eks-demo
 ┣ src/main/java/com/codewithvenu/eks
 ┃ ┣ EksDemoApplication.java
 ┃ ┗ controller
 ┃   ┗ HelloController.java
 ┣ src/main/resources
 ┃ ┗ application.yml
 ┣ k8s
 ┃ ┣ deployment.yml
 ┃ ┗ service.yml
 ┣ Dockerfile
 ┗ pom.xml

Step 2: Create Main Class

package com.codewithvenu.eks;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EksDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(EksDemoApplication.class, args);
    }
}

Step 3: Create Controller

package com.codewithvenu.eks.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Map;

@RestController
public class HelloController {

    @Value("${spring.application.name}")
    private String appName;

    @Value("${app.environment:local}")
    private String environment;

    @GetMapping("/")
    public Map<String, Object> home() {
        return Map.of(
                "message", "Spring Boot application is running on AWS EKS",
                "application", appName,
                "environment", environment,
                "timestamp", LocalDateTime.now().toString()
        );
    }

    @GetMapping("/health")
    public Map<String, String> health() {
        return Map.of("status", "UP");
    }

    @GetMapping("/version")
    public Map<String, String> version() {
        return Map.of("version", "1.0.0");
    }
}

Step 4: Configure application.yml

server:
  port: 8080

spring:
  application:
    name: springboot-eks-demo

app:
  environment: ${APP_ENVIRONMENT:local}

management:
  endpoints:
    web:
      exposure:
        include: health,info

Step 5: Build JAR

Input:

mvn clean package

Output:

BUILD SUCCESS

Generated JAR:

target/springboot-eks-demo-0.0.1-SNAPSHOT.jar

Run locally:

java -jar target/springboot-eks-demo-0.0.1-SNAPSHOT.jar

Test:

curl http://localhost:8080/

Output:

{
  "application": "springboot-eks-demo",
  "environment": "local",
  "message": "Spring Boot application is running on AWS EKS",
  "timestamp": "2026-06-25T10:30:00"
}

Step 6: Create Dockerfile

Create Dockerfile:

FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

COPY target/springboot-eks-demo-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

Step 7: Build Docker Image

Input:

docker build -t springboot-eks-demo .

Output:

Successfully built image
Successfully tagged springboot-eks-demo:latest

Run locally:

docker run -p 8080:8080 -e APP_ENVIRONMENT=docker-local springboot-eks-demo

Test:

curl http://localhost:8080/

Output:

{
  "application": "springboot-eks-demo",
  "environment": "docker-local",
  "message": "Spring Boot application is running on AWS EKS",
  "timestamp": "2026-06-25T10:40:00"
}

Step 8: Configure AWS CLI

Input:

aws configure

Example input:

AWS Access Key ID: your-access-key
AWS Secret Access Key: your-secret-key
Default region name: us-east-1
Default output format: json

Validate:

aws sts get-caller-identity

Output:

{
  "UserId": "AIDAEXAMPLE",
  "Account": "123456789012",
  "Arn": "arn:aws:iam::123456789012:user/codewithvenu"
}

Step 9: Create Amazon ECR Repository

Input:

aws ecr create-repository \
  --repository-name springboot-eks-demo \
  --region us-east-1

Output:

{
  "repository": {
    "repositoryName": "springboot-eks-demo",
    "repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo"
  }
}

Repository URI:

123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo

Step 10: Login Docker to ECR

Input:

aws ecr get-login-password --region us-east-1 \
| docker login --username AWS \
--password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com

Output:

Login Succeeded

Step 11: Tag Docker Image

Input:

docker tag springboot-eks-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:latest

Validate:

docker images

Output:

REPOSITORY                                                       TAG
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo latest

Step 12: Push Image to ECR

Input:

docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:latest

Output:

latest: digest: sha256:abc123 size: 1572

Image Push Flow

flowchart LR
    JAR[Spring Boot JAR]
    IMG[Docker Image]
    ECR[Amazon ECR]
    EKS[AWS EKS]

    JAR --> IMG
    IMG --> ECR
    ECR --> EKS

Step 13: Create EKS Cluster

Use eksctl to create the cluster.

Input:

eksctl create cluster \
  --name codewithvenu-eks-cluster \
  --region us-east-1 \
  --nodegroup-name codewithvenu-workers \
  --node-type t3.small \
  --nodes 2 \
  --nodes-min 1 \
  --nodes-max 3 \
  --managed

Output:

creating EKS cluster "codewithvenu-eks-cluster"
creating managed nodegroup "codewithvenu-workers"
waiting for cluster to become ready
EKS cluster "codewithvenu-eks-cluster" is ready

This can take several minutes.


Step 14: Verify EKS Cluster

Input:

kubectl get nodes

Output:

NAME                                           STATUS   ROLES    AGE   VERSION
ip-192-168-10-10.ec2.internal                 Ready    <none>   5m    v1.x
ip-192-168-20-20.ec2.internal                 Ready    <none>   5m    v1.x

Check current context:

kubectl config current-context

Output:

codewithvenu-eks-cluster.us-east-1.eksctl.io

Step 15: Create Namespace

Input:

kubectl create namespace codewithvenu

Output:

namespace/codewithvenu created

Validate:

kubectl get namespaces

Output:

NAME            STATUS   AGE
codewithvenu    Active   10s

Step 16: Create Kubernetes Deployment YAML

Create file:

k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: springboot-eks-demo
  namespace: codewithvenu
spec:
  replicas: 2
  selector:
    matchLabels:
      app: springboot-eks-demo
  template:
    metadata:
      labels:
        app: springboot-eks-demo
    spec:
      containers:
        - name: springboot-eks-demo
          image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          env:
            - name: APP_ENVIRONMENT
              value: "eks-dev"
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 40
            periodSeconds: 20
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "500m"
              memory: "1024Mi"

Important:

Replace this image URI:

123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:latest

with your real ECR image URI.


Step 17: Create Kubernetes Service YAML

Create file:

k8s/service.yml
apiVersion: v1
kind: Service
metadata:
  name: springboot-eks-service
  namespace: codewithvenu
spec:
  type: LoadBalancer
  selector:
    app: springboot-eks-demo
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

This will create an AWS Load Balancer automatically.


Step 18: Apply Kubernetes YAML

Input:

kubectl apply -f k8s/deployment.yml
kubectl apply -f k8s/service.yml

Output:

deployment.apps/springboot-eks-demo created
service/springboot-eks-service created

Step 19: Check Pods

Input:

kubectl get pods -n codewithvenu

Output:

NAME                                   READY   STATUS    RESTARTS   AGE
springboot-eks-demo-7f9d8c6d88-a1b2c   1/1     Running   0          60s
springboot-eks-demo-7f9d8c6d88-d4e5f   1/1     Running   0          60s

If pods are pending or crashing:

kubectl describe pod <pod-name> -n codewithvenu

Step 20: Check Service

Input:

kubectl get service -n codewithvenu

Output:

NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP                                                              PORT
springboot-eks-service   LoadBalancer   10.100.10.10    abc123.us-east-1.elb.amazonaws.com                                      80

Wait until EXTERNAL-IP is available.


Step 21: Test Application

Input:

curl http://abc123.us-east-1.elb.amazonaws.com/

Output:

{
  "application": "springboot-eks-demo",
  "environment": "eks-dev",
  "message": "Spring Boot application is running on AWS EKS",
  "timestamp": "2026-06-25T11:10:00"
}

Health check:

curl http://abc123.us-east-1.elb.amazonaws.com/health

Output:

{
  "status": "UP"
}

Version endpoint:

curl http://abc123.us-east-1.elb.amazonaws.com/version

Output:

{
  "version": "1.0.0"
}

Step 22: View Pod Logs

Input:

kubectl logs -n codewithvenu deployment/springboot-eks-demo

Output:

Started EksDemoApplication
Tomcat started on port 8080

Stream logs:

kubectl logs -f -n codewithvenu deployment/springboot-eks-demo

Step 23: Scale Application

Scale to 3 replicas:

kubectl scale deployment springboot-eks-demo \
  --replicas=3 \
  -n codewithvenu

Output:

deployment.apps/springboot-eks-demo scaled

Validate:

kubectl get pods -n codewithvenu

Output:

NAME                                   READY   STATUS    RESTARTS   AGE
springboot-eks-demo-7f9d8c6d88-a1b2c   1/1     Running   0          5m
springboot-eks-demo-7f9d8c6d88-d4e5f   1/1     Running   0          5m
springboot-eks-demo-7f9d8c6d88-g7h8i   1/1     Running   0          10s

Step 24: Deploy New Version

Update controller:

@GetMapping("/version")
public Map<String, String> version() {
    return Map.of("version", "1.0.1");
}

Build JAR:

mvn clean package

Build Docker image:

docker build -t springboot-eks-demo .

Tag image with version:

docker tag springboot-eks-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:1.0.1

Push image:

docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:1.0.1

Update deployment image:

kubectl set image deployment/springboot-eks-demo \
springboot-eks-demo=123456789012.dkr.ecr.us-east-1.amazonaws.com/springboot-eks-demo:1.0.1 \
-n codewithvenu

Output:

deployment.apps/springboot-eks-demo image updated

Check rollout:

kubectl rollout status deployment/springboot-eks-demo -n codewithvenu

Output:

deployment "springboot-eks-demo" successfully rolled out

Test:

curl http://abc123.us-east-1.elb.amazonaws.com/version

Output:

{
  "version": "1.0.1"
}

Kubernetes Deployment Flow

flowchart LR
    CODE[Code Change]
    BUILD[Build JAR]
    IMAGE[Build Docker Image]
    PUSH[Push to ECR]
    UPDATE[Update Kubernetes Deployment]
    ROLLOUT[Rolling Deployment]
    TEST[Test Application]

    CODE --> BUILD
    BUILD --> IMAGE
    IMAGE --> PUSH
    PUSH --> UPDATE
    UPDATE --> ROLLOUT
    ROLLOUT --> TEST

Step 25: Rollback Deployment

If new version has issues:

kubectl rollout undo deployment/springboot-eks-demo -n codewithvenu

Output:

deployment.apps/springboot-eks-demo rolled back

Check history:

kubectl rollout history deployment/springboot-eks-demo -n codewithvenu

Output:

REVISION  CHANGE-CAUSE
1         <none>
2         <none>

Step 26: Use ConfigMap

Create file:

k8s/configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: springboot-eks-config
  namespace: codewithvenu
data:
  APP_ENVIRONMENT: "eks-configmap-dev"
  SPRING_PROFILES_ACTIVE: "dev"

Apply:

kubectl apply -f k8s/configmap.yml

Update deployment env section:

envFrom:
  - configMapRef:
      name: springboot-eks-config

Step 27: Use Kubernetes Secret

Create secret:

kubectl create secret generic springboot-db-secret \
  --from-literal=DB_USERNAME=appuser \
  --from-literal=DB_PASSWORD=secret123 \
  -n codewithvenu

Output:

secret/springboot-db-secret created

Use in deployment:

envFrom:
  - secretRef:
      name: springboot-db-secret

For production, prefer AWS Secrets Manager with external secrets integration.


Step 28: Cleanup Resources

Delete Kubernetes resources:

kubectl delete -f k8s/service.yml
kubectl delete -f k8s/deployment.yml
kubectl delete namespace codewithvenu

Delete EKS cluster:

eksctl delete cluster \
  --name codewithvenu-eks-cluster \
  --region us-east-1

Delete ECR repository:

aws ecr delete-repository \
  --repository-name springboot-eks-demo \
  --force \
  --region us-east-1

Important:

Delete the cluster when learning is complete to avoid AWS charges.


Common Errors and Fixes

Error 1: ImagePullBackOff

Reason:

Wrong ECR image URI
Image not pushed
Worker node cannot access ECR

Fix:

kubectl describe pod <pod-name> -n codewithvenu

Check image URI and IAM permissions.


Error 2: Pods Running But URL Not Working

Check service:

kubectl get svc -n codewithvenu

Possible reasons:

Load Balancer still provisioning
Security group issue
Wrong targetPort
Application not listening on 8080

Error 3: CrashLoopBackOff

Check logs:

kubectl logs <pod-name> -n codewithvenu

Possible reasons:

Spring Boot startup error
Missing environment variable
Database connection failure
Memory limit too low

Error 4: kubectl Cannot Connect

Update kubeconfig:

aws eks update-kubeconfig \
  --name codewithvenu-eks-cluster \
  --region us-east-1

Validate:

kubectl get nodes

Error 5: LoadBalancer External IP Pending

Wait a few minutes.

Check:

kubectl describe svc springboot-eks-service -n codewithvenu

Make sure subnets are properly tagged for load balancer creation.


EKS Best Practices

  • Use managed node groups
  • Use private subnets for worker nodes
  • Use ALB Ingress Controller for production
  • Use Horizontal Pod Autoscaler
  • Use Cluster Autoscaler or Karpenter
  • Use ConfigMaps for non-sensitive config
  • Use Secrets Manager for secrets
  • Use CloudWatch Container Insights
  • Use resource requests and limits
  • Use readiness and liveness probes
  • Use versioned Docker image tags
  • Avoid using latest in production
  • Use CI/CD pipeline
  • Enable RBAC
  • Use namespaces for environment separation
  • Use Helm for repeatable deployments

EKS vs ECS Fargate

Feature ECS Fargate EKS
Container orchestration ECS Kubernetes
Complexity Medium High
Portability AWS-focused Multi-cloud friendly
YAML Lower Higher
Enterprise Kubernetes No Yes
Best for AWS-native containers Kubernetes platforms

Interview Questions

What is EKS?

Amazon EKS is a managed Kubernetes service that runs Kubernetes control plane components for you.

What is a Pod?

A Pod is the smallest deployable unit in Kubernetes. It contains one or more containers.

What is a Deployment?

A Deployment manages replicas of pods and supports rolling updates and rollbacks.

What is a Service?

A Service exposes pods using a stable network endpoint.

What is the difference between ClusterIP, NodePort, and LoadBalancer?

ClusterIP exposes service internally. NodePort exposes service on node ports. LoadBalancer creates an external cloud load balancer.

What is ImagePullBackOff?

It means Kubernetes cannot pull the Docker image from the container registry.

Why use readiness and liveness probes?

Readiness checks whether the app is ready to receive traffic. Liveness checks whether the app is still healthy.


Summary

In this article, we deployed a Spring Boot application on AWS EKS.

We covered:

  • Spring Boot REST API creation
  • Dockerfile creation
  • Local Docker testing
  • Amazon ECR image push
  • EKS cluster creation
  • Kubernetes namespace
  • Deployment YAML
  • Service YAML
  • LoadBalancer access
  • Logs
  • Scaling
  • Rollout
  • Rollback
  • ConfigMap
  • Secret
  • Cleanup
  • Best practices

EKS is powerful for enterprise Kubernetes-based platforms, microservices, and cloud-native deployments.


Loading likes...

Comments

Share a question, correction, or practical insight about this article.

Loading approved comments...