Skip to content
ExponaBLOG
JAVA, SPRING FRAMEWORK & SPRING BOOT · DAY 085 / 100

Deploying Spring Boot to AWS: what developers should know

Three good options and when to pick each.

Expona Admin8 min read

Introduction

When you've built a solid Spring Boot application locally and it's time to move into production, AWS offers several paths forward—but choosing the wrong one can lock you into operational overhead you didn't anticipate. Deploying Spring Boot to AWS: what developers should know means understanding not just the mechanics of pushing code, but which service matches your team's operational appetite and your application's complexity. This guide walks through three proven options, how to manage data and secrets safely, and the health-check patterns that keep your service stable once it's live.

Three Deployment Options: Choose by Ops Appetite

AWS gives us genuine choice here. Each option trades simplicity for control, and picking the right one early saves weeks of rework later.

ECS Fargate: Containers Without Server Management

ECS Fargate is the middle ground. You package your Spring Boot app in a Docker image, define task memory and CPU in a task definition, and AWS manages the underlying compute. No patching EC2 instances, no cluster orchestration headaches—just your container running on AWS infrastructure.

Real-world scenario: A mid-size e-commerce team with four engineers and a growing order volume. They have some ops experience but prefer to spend cycles on feature work. Fargate handles autoscaling based on CloudWatch metrics, integrates naturally with load balancers, and keeps the operational surface small.

Setup involves creating a task definition that specifies your Docker image URI, memory (typically 512 MB to 2 GB for Spring Boot), CPU, port mappings, and environment variables. Then you define an ECS service that runs N copies of that task and attaches them to an Application Load Balancer. Log aggregation flows automatically to CloudWatch.

Cost is straightforward: you pay per vCPU and GB of memory per hour, plus data transfer. For a small service running 2 tasks at 0.5 vCPU and 1 GB memory, expect around $30–$50 per month (at time of writing).

Elastic Beanstalk: Simple Apps, Quick Setup

If you want deployment to feel almost like git push, Elastic Beanstalk abstracts the container layer away. You upload a JAR or a Docker image, Beanstalk spins up an EC2 instance (or several), attaches a load balancer, and handles rolling deployments automatically.

Real-world scenario: A startup with a single Spring Boot microservice, one part-time ops person, and a Monday morning deadline. They don't need Kubernetes complexity or the cognitive load of ECS task definitions. Beanstalk gets them live in under an hour with minimal configuration.

Elastic Beanstalk sits atop EC2, so you're paying for instances, but the abstraction layer—auto-scaling policies, environment configuration, and deployment hooks—saves operational toil. Rolling deployments are built in: a new instance spins up with your new code, traffic shifts gradually, and old instances drain naturally.

The tradeoff: less fine-grained control. You can't easily customize how the container starts or orchestrate multiple services with deep dependencies. It's a "happy path" tool, and if your happy path stays happy, it's efficient.

EKS: Teams Already on Kubernetes

If your team is already investing in Kubernetes—either in-house or via managed platforms like EKS—then Spring Boot on Kubernetes is a strong choice. You get pod-level autoscaling, canary deployments, and service mesh integration if you're sophisticated enough to want it.

Real-world scenario: A larger platform team running a dozen microservices. They've hired for Kubernetes expertise, they're using Helm for templating, and they want consistent deployment patterns across Go, Python, and Java services. EKS plugs Spring Boot into that ecosystem without friction.

The operational bar is higher: you're responsible for cluster patching, node group management, and understanding the declarative model. But once in place, deploying a new Spring Boot service follows the same pattern as any other containerized workload.

Cost is typically higher than Fargate (EKS charges a flat $0.10 per cluster per hour plus EC2 node costs), but per-service cost can be lower at scale if you're packing workloads densely across nodes.

Option Best For Ops Overhead Typical Cost
ECS Fargate Containers without managing servers; mid-size teams Low–Medium $30–$150/month per service
Elastic Beanstalk Simple single-service apps; rapid deployment Low $20–$100/month
EKS Teams already on Kubernetes; many services High $73+/month (cluster fee) plus node costs

Database on RDS: Managed Postgres with Backups and Failover

Wherever you deploy your Spring Boot app on AWS, your database should almost certainly live on RDS. Running your own database on EC2 is possible but means taking on patches, replication, backup scheduling—operational work that AWS does well and you probably shouldn't.

RDS Postgres is the popular choice for Spring Boot teams. It handles Multi-AZ failover (your primary instance is mirrored synchronously in a second availability zone; if it fails, DNS flips automatically), automated backups retained for up to 35 days, and read replicas for scaling read-heavy workloads.

Configuration sketch: A db.t3.small instance with 20 GB gp3 storage runs around $30–$50 per month (single-AZ) or $60–$80 (Multi-AZ). For most applications, that's a stable, predictable cost. Add read replicas if you're serving analytics queries; they cost about 50% of the primary instance.

Your Spring Boot application connects via JDBC using the RDS endpoint as the hostname. RDS creates a security group; attach your ECS/Beanstalk/EKS security group as an inbound rule, and traffic flows securely across the AWS network.

java
// application.yaml
spring:
  datasource:
    url: jdbc:postgresql://mydb.ch9xxxxxxx.us-east-1.rds.amazonaws.com:5432/appdb
    username: postgres
    password: ${DB_PASSWORD}  // Injected from secrets, covered below
  jpa:
    hibernate:
      ddl-auto: validate

One production habit: always set ddl-auto: validate (or none) in production. Let your migration tool (Flyway or Liquibase) own schema changes; never let Hibernate auto-generate DDL in production.

RDS also supports encrypted storage at rest and encrypted connections. Enable both: encryption is free and removes the "we should do this eventually" conversation.

Secrets: AWS Secrets Manager or Parameter Store via Spring Cloud AWS

Hardcoding database passwords or API keys in your Docker image or deployment configuration is a guarantee that you'll regret it. AWS offers two managed secret stores.

Secrets Manager vs. Parameter Store

Secrets Manager is purpose-built for secrets: database credentials, API tokens, and encryption keys. It integrates with RDS for automatic credential rotation (your password can change daily without application restarts). Cost is $0.40 per secret per month plus API calls.

Parameter Store (part of Systems Manager) is a key-value config store. It's fine for non-critical values like feature flags or API endpoints, but lacks rotation. It's also cheaper ($0.04 per advanced parameter per month).

Most teams use Secrets Manager for database credentials and API keys, Parameter Store for configuration.

Integration with Spring Boot

Spring Cloud AWS integrates both seamlessly. Add the dependency:

java
// pom.xml
<dependency>
  <groupId>io.awspring.cloud</groupId>
  <artifactId>spring-cloud-aws-secrets-manager-config</artifactId>
  <version>3.0.0</version>
</dependency>

Then bootstrap your secrets in application.yaml:

java
// bootstrap.yaml
spring:
  config:
    import: aws-secretsmanager:
  cloud:
    aws:
      region:
        static: us-east-1

Create your secret in Secrets Manager (AWS console or CLI):

java
aws secretsmanager create-secret \
  --name myapp/db-password \
  --secret-string "super-secure-password-here" \
  --region us-east-1

Spring Cloud AWS will fetch that secret during startup and inject it as ${myapp/db-password}. Reference it in your datasource config or directly in Java:

java
@Value("${myapp/db-password}")
private String dbPassword;

IAM permissions: your ECS task, Beanstalk environment, or EKS pod needs an IAM role allowing secretsmanager:GetSecretValue on the secret ARN. Never use AWS access keys for this; always use IAM roles so credentials rotate transparently.

Health Checks: Point the Load Balancer at /actuator/health/readiness

Once your service is running behind a load balancer (whether ECS with ALB, Beanstalk, or EKS Ingress), the load balancer needs to know if your instance is healthy. Misconfigured health checks lead to traffic flowing to dying instances.

Spring Boot Actuator exposes /actuator/health by default. It returns 200 OK if the app is running; if a database connection fails or a critical component is down, it returns 503.

For readiness checks, use /actuator/health/readiness. This endpoint returns 200 when your app is ready to serve traffic (database is up, caches are warm, startup jobs are complete). During rolling deployments, the load balancer removes instances returning non-200 before traffic stops flowing to them.

Configure your load balancer (ALB health check settings in ECS, or health check settings in Beanstalk):

  • Path: /actuator/health/readiness
  • Port: 8080 (or whatever your app listens on)
  • Protocol: HTTP
  • Healthy threshold: 2 (require two consecutive 200 responses)
  • Unhealthy threshold: 3 (mark unhealthy after three consecutive non-200 responses)
  • Interval: 30 seconds (check every 30 seconds)
  • Timeout: 5 seconds (wait 5 seconds for a response)

If your Actuator endpoints are on a non-standard port or behind authentication, configure it explicitly:

java
// application.yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: when-authorized

Better: exclude sensitive endpoints from requiring authentication by setting up a public health check endpoint that doesn't need credentials. The load balancer doesn't carry API tokens; keeping health checks public and simple avoids configuration friction.

Common Mistakes

  • Not setting readiness probes: Deploying with the default health endpoint, which doesn't wait for database connections or startup hooks. Your load balancer starts routing traffic immediately, before your app is truly ready.
  • Storing secrets in environment variables in the Docker image: Docker images are artifacts; secrets leak if the image is shared or inspected. Always inject secrets at runtime via Secrets Manager or Parameter Store.
  • Choosing EKS before the team knows Kubernetes: Bringing in a powerful but unfamiliar tool adds cognitive load. Pick Fargate or Beanstalk first; migrate to EKS only if you genuinely need its features and have the expertise.
  • Using Hibernate DDL auto in production: Letting Hibernate manage schema migrations in production is a foot gun. Use versioned migrations (Flyway, Liquibase) so you can audit and rollback if needed.
  • Not monitoring database connections: Spring Boot connection pools are finite. Without visibility into pool exhaustion, your app can appear to hang. Enable CloudWatch metrics for database connection count.

Key Takeaways

  • Choose ECS Fargate for simplicity with some ops comfort, Elastic Beanstalk for minimum ops overhead on a single service, and EKS only if your team already knows Kubernetes.
  • Always use RDS for your database; it handles backups, failover, and patching, removing operational toil you shouldn't take on.
  • Use AWS Secrets Manager for credentials and API keys; integrate via Spring Cloud AWS so secrets are injected at startup without hardcoding.
  • Configure load balancer health checks to hit /actuator/health/readiness and give your app time to start up before traffic arrives.
  • Never let Hibernate manage schema migrations in production. Use Flyway or Liquibase and keep control of your database changes.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles