AWS Builder Center
Deploying a Dockerized Spring Boot Application on AWS EC2

Deploying a Dockerized Spring Boot Application on AWS EC2

Learn how to deploy a Dockerized Spring Boot application on Amazon EC2 from development to production. This article covers Docker image creation, EC2 setup, security groups, container deployment, environment variables, port configuration, HTTPS considerations, and CloudWatch monitoring. It provides a practical workflow for developers who want to understand how containerized Java applications can be deployed on AWS.

Deploying a Dockerized Spring Boot Application on AWS EC2

Developing a Spring Boot application locally is straightforward. The next challenge is making that application available to users through the internet.
One practical approach is to package the application using Docker and deploy the container on an Amazon EC2 instance.
This approach is useful for developers who want to understand the fundamentals of cloud deployment while still having direct control over the server environment.
In this article, we will take a Spring Boot application, create a Docker image, run it locally, launch an EC2 instance, configure networking, and deploy the application.

1. Deployment Architecture

The basic architecture looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Internet
|
v
┌─────────────────┐
│ Amazon EC2 │
│ │
│ Linux Server │
│ | │
│ Docker │
│ | │
│ Spring Boot │
│ | │
│ :8080 │
└─────────────────┘
The application is packaged inside a Docker container, while EC2 provides the virtual server on which the container runs.
A more complete setup can include:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Users
|
v
Internet
|
v
Amazon EC2
|
Docker
|
Spring Boot
|
REST API
|
Application
|
CloudWatch

2. Why Docker?

Without Docker, deploying a Java application often requires manually installing and configuring:
  • Java
  • Application dependencies
  • Environment variables
  • System packages
  • Application configuration
This can lead to differences between development and production environments.
Docker packages the application and its runtime requirements into a container image.
The workflow becomes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Spring Boot Source Code
|
v
Maven Build
|
v
JAR File
|
v
Docker Image
|
v
EC2 Server
|
v
Docker Container
This makes the deployment process more consistent.

3. Build the Spring Boot Application

Suppose we have a simple Spring Boot REST API.
A basic controller could look like:
1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping("/api")
public class HelloController {

@GetMapping("/hello")
public String hello() {
return "Hello from Spring Boot on AWS!";
}
}
When the application runs locally, the endpoint could be accessed through:
1
http://localhost:8080/api/hello
The next goal is to make the same application accessible from an EC2 instance.

4. Create the Production JAR

For a Maven-based Spring Boot project, we can build the application using:
1
./mvnw clean package
On Windows, the Maven wrapper can be run with:
1
mvnw.cmd clean package
After a successful build, Maven creates a JAR file inside the target directory.
For example:
1
2
target/
└── application.jar
The JAR contains the packaged Spring Boot application.

5. Create a Dockerfile

Now we can create a Dockerfile in the project root.
A simple example is:
1
2
3
4
5
6
7
8
9
FROM eclipse-temurin:21-jre

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
Let's understand each instruction.

FROM

1
FROM eclipse-temurin:21-jre
This provides a Java runtime environment for the application.

WORKDIR

1
WORKDIR /app
This sets the working directory inside the container.

COPY

1
COPY target/*.jar app.jar
This copies the Spring Boot JAR into the container.

EXPOSE

1
EXPOSE 8080
This documents the port used by the application.

ENTRYPOINT

1
ENTRYPOINT ["java", "-jar", "app.jar"]
This starts the Spring Boot application when the container runs.

6. Build the Docker Image

After creating the Dockerfile, build the image:
1
docker build -t springboot-app .
Docker reads the instructions from the Dockerfile and creates an image.
We can verify it using:
1
docker images
The result will contain an image similar to:
1
2
REPOSITORY TAG IMAGE ID
springboot-app latest abc123

7. Test the Container Locally

Before deploying to AWS, it is good practice to test the container locally.
Run:
1
docker run -d -p 8080:8080 --name springboot-app springboot-app
The mapping:
1
8080:8080
means:
1
Host Port : Container Port
We can then test:
1
http://localhost:8080/api/hello
If the API responds correctly, the container is ready for deployment.

8. Create an Amazon EC2 Instance

The next step is to create an EC2 instance.
When launching an instance, we need to select:
  • An appropriate AWS Region
  • An Amazon Machine Image
  • An instance type
  • A key pair
  • Network settings
  • Storage configuration
For learning purposes, a small instance can be sufficient, depending on the application's resource requirements.
The architecture becomes:
1
2
3
4
5
6
7
8
9
AWS
|
+-- EC2 Instance
|
+-- Linux
|
+-- Docker
|
+-- Spring Boot Container

9. Configure the Security Group

A security group acts as a virtual firewall for the EC2 instance.
For example, the application may need:
1
2
3
4
5
Inbound Rules

SSH → Port 22
HTTP → Port 80
Custom → Port 8080
During development, port 8080 can be opened for testing.
However, exposing application ports directly to the internet is not always the best production design.
A more production-oriented architecture can place a reverse proxy or load balancer in front of the application.
The important principle is:
Only expose the ports that are actually required.

10. Connect to the EC2 Instance

After launching the instance, we can connect to it using SSH.
For example:
1
ssh -i my-key.pem ec2-user@<EC2-PUBLIC-IP>
The exact username depends on the operating system image being used.
Once connected, we are working inside the cloud server.
1
2
3
4
5
Local Machine
|
| SSH
v
Amazon EC2

11. Install Docker on EC2

The Docker installation commands depend on the Linux distribution used by the EC2 instance.
After installation, verify Docker:
1
docker --version
Then verify that the Docker service is running.
The goal is to reach this state:
1
2
3
4
5
EC2
|
+-- Docker Engine
|
+-- Containers

12. Getting the Docker Image onto EC2

There are multiple ways to provide the image to the EC2 instance.
For a simple learning workflow, the image can be stored in a container registry such as Amazon Elastic Container Registry (ECR).
The workflow becomes:
1
2
3
4
5
6
7
8
9
10
11
12
13
Developer
|
v
Docker Build
|
v
Amazon ECR
|
v
Amazon EC2
|
v
Docker Container
ECR provides a centralized place to store container images.
This is more scalable than manually transferring image files as the project grows.

13. Push the Image to Amazon ECR

First, create an ECR repository for the application.
The Docker image then needs to be tagged with the ECR repository address.
For example:
1
2
docker tag springboot-app:latest \
<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/springboot-app:latest
The image can then be pushed to ECR after authenticating Docker with the registry.
Conceptually:
1
2
3
4
5
6
7
Local Docker Image
|
v
Amazon ECR
|
v
Container Registry

14. Pull the Image on EC2

Once the image is available in ECR, the EC2 instance can authenticate with ECR and pull the image.
The workflow becomes:
1
docker pull <ECR-IMAGE>
Then verify the image:
1
docker images
We now have the same application image that was built during development.

15. Run the Spring Boot Container

The container can be started with:
1
2
3
4
docker run -d \
--name springboot-app \
-p 8080:8080 \
springboot-app:latest
The flow is now:
1
2
3
4
5
6
7
8
9
10
11
12
13
Internet
|
v
EC2 Public IP
|
v
Port 8080
|
v
Docker
|
v
Spring Boot
If the security group allows the required traffic, the API can be accessed through the EC2 public address.
For example:
1
http://<EC2-PUBLIC-IP>:8080/api/hello

16. Environment Variables

Real applications usually need configuration values such as:
1
2
3
4
5
DATABASE_URL
DATABASE_USERNAME
DATABASE_PASSWORD
JWT_SECRET
API_KEY
These values should not be hard-coded into the application.
Instead, they can be supplied through environment variables or a dedicated AWS secret-management solution.
For example:
1
2
3
4
5
6
7
docker run -d \
--name springboot-app \
-p 8080:8080 \
-e DB_URL="$DB_URL" \
-e DB_USERNAME="$DB_USERNAME" \
-e DB_PASSWORD="$DB_PASSWORD" \
springboot-app:latest
This separates application code from environment-specific configuration.

17. Connecting a Database

A typical Spring Boot application may require a database.
Instead of installing the database on the same EC2 instance, a managed AWS database service such as Amazon RDS can be used.
The architecture becomes:
1
2
3
4
5
6
7
8
9
10
11
12
13
Internet
|
v
EC2
|
Docker
|
Spring Boot
|
v
RDS
|
Database
This separates the application server from the database layer.
It also allows database infrastructure to be managed independently from the application container.

18. Securing Database Access

The database should not normally be publicly accessible just because the application needs access to it.
A better architecture is:
1
2
3
4
5
6
7
8
Internet
|
v
EC2
|
| Private Network
v
RDS
Security groups can be configured so that database traffic is allowed only from the appropriate application resources.
For example:
1
2
3
4
5
EC2 Security Group
|
| Database Port
v
RDS Security Group
This reduces unnecessary exposure of the database.

19. Monitoring with Amazon CloudWatch

A deployed application needs monitoring.
Amazon CloudWatch can be used to collect logs and metrics and to create alarms.
A simplified architecture is:
1
2
3
4
5
6
7
8
9
10
11
12
Spring Boot
|
v
Docker
|
v
EC2
|
v
CloudWatch
/ \
Logs Metrics
Monitoring can help identify:
  • Application errors
  • High CPU usage
  • Memory-related problems
  • Unexpected traffic
  • Container failures
  • Infrastructure issues
This becomes especially important when an application is serving real users.

20. Handling Application Updates

Suppose a developer changes the Spring Boot application.
Without automation, the deployment might look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Change Code
↓
Build JAR
↓
Build Docker Image
↓
Push Image
↓
SSH into EC2
↓
Pull New Image
↓
Stop Old Container
↓
Start New Container
This works for learning and small projects, but it becomes repetitive as the application grows.

21. Automating the Deployment

The process can later be integrated with GitHub Actions.
A CI/CD workflow could look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Developer
|
v
GitHub
|
v
GitHub Actions
|
+---- Build
|
+---- Test
|
+---- Docker Build
|
v
Amazon ECR
|
v
Amazon EC2
|
v
New Container
Now a code push can automatically trigger the deployment pipeline.
This reduces manual work and makes deployments more consistent.

22. Improving the Production Architecture

A simple EC2 deployment is useful for learning, but a production architecture can evolve.
Instead of:
1
2
3
4
5
6
7
Internet
|
v
EC2
|
v
Container
we can introduce additional components:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Internet
|
v
Application Load
Balancer
|
┌──────────┴──────────┐
| |
v v
EC2 + EC2 +
Container Container
| |
└──────────┬──────────┘
|
v
RDS
This architecture can provide better availability and allow traffic to be distributed across multiple application instances.

23. EC2 vs Containers vs Managed Container Services

AWS provides multiple ways to run applications.

EC2

1
2
You manage:
OS + Docker + Application
This provides significant control.

ECS

1
2
AWS manages more of:
Container orchestration
This can reduce some operational overhead.

Serverless

1
2
AWS manages:
Underlying infrastructure
This can be useful for event-driven workloads.
There is no single solution that is best for every application.
The choice depends on:
  • Application architecture
  • Traffic
  • Operational requirements
  • Cost
  • Scaling requirements
  • Team expertise

24. Common Deployment Mistakes

Opening Too Many Ports

Only required ports should be accessible.

Hard-Coding Credentials

Database passwords, API keys, and secrets should never be committed to source control.

Using the Root AWS Account

IAM users and roles should be used according to the required permissions.

Ignoring Logs

An application can be running while still experiencing errors.

Deploying Without Testing

A successful Docker build does not guarantee that the application itself works correctly.

Using latest Everywhere

Versioned image tags can make deployments easier to trace and roll back.
For example:
1
2
3
springboot-app:v1.0.0
springboot-app:v1.1.0
springboot-app:v1.2.0
This provides clearer version history.

25. A Complete Deployment Flow

Putting everything together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Developer
|
v
Spring Boot Code
|
v
Maven
|
v
JAR File
|
v
Docker Build
|
v
Docker Image
|
v
Amazon ECR
|
v
Amazon EC2
|
v
Docker Container
|
v
Spring Boot API
|
┌────┴────┐
| |
v v
RDS CloudWatch
Database Monitoring
This represents the journey from source code to a running cloud application.

26. What I Learned

Deploying a Spring Boot application on EC2 demonstrates that cloud deployment is more than simply uploading a JAR file.
There are several layers involved:
1
2
3
4
5
6
7
8
9
10
11
12
13
Application
↓
Docker
↓
Operating System
↓
EC2
↓
Networking
↓
Security
↓
Monitoring
Each layer has a role in making the application accessible and maintainable.
Docker makes the application portable, EC2 provides computing infrastructure, ECR stores container images, and CloudWatch provides visibility into the running system.
As the application grows, additional AWS services can be introduced according to the requirements.

Conclusion

Deploying a Dockerized Spring Boot application on Amazon EC2 is a practical way to understand the fundamentals of cloud deployment.
The complete journey can be summarized as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Code
↓
Maven Build
↓
Docker Image
↓
Amazon ECR
↓
Amazon EC2
↓
Docker Container
↓
Spring Boot API
↓
Users
From here, the architecture can evolve toward load balancing, automated CI/CD, managed databases, centralized secrets, monitoring, and container orchestration.
The most important lesson is to build the deployment in stages.
Start with a working application, containerize it, deploy it to EC2, secure the network, add monitoring, and then automate the process.
This approach provides a strong foundation for moving from local development toward real-world AWS and DevOps practices.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article