TL;DR
Complete guide to deploying a Spring Boot application on AWS EC2 with Amazon Corretto, systemd, nginx, and CloudWatch.
Key facts
- Topology
- Spring Boot on AWS EC2
TL;DR
This guide covers deploying a Spring Boot application on an AWS EC2 instance with Amazon Corretto JDK, systemd process management, nginx reverse proxy, SSL via Certbot, and CloudWatch monitoring for JVM metrics.
Step 1 — Launch and configure the EC2 instance
Launch an EC2 instance with Ubuntu 24.04 LTS (t3.medium or larger for production). Configure the security group to allow only the ports you need:
- Port 22 — SSH (restrict to your IP or VPN CIDR)
- Port 80 — HTTP (for Certbot verification and redirect)
- Port 443 — HTTPS (public traffic)
- Port 8080 — do NOT open publicly (Spring Boot listens here, nginx proxies internally)
SSH into the instance:
ssh -i your-key.pem ubuntu@ec2-xx-xx-xx-xx.compute-1.amazonaws.com
Step 2 — Install Amazon Corretto JDK
Amazon Corretto is a no-cost, production-ready OpenJDK distribution maintained by AWS:
sudo apt update && sudo apt upgrade -y
wget -O - https://apt.corretto.aws/corretto.key | sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | \
sudo tee /etc/apt/sources.list.d/corretto.list
sudo apt update
sudo apt install -y java-21-amazon-corretto-jdk
java -version
Step 3 — Deploy the JAR
Create a service user and deploy directory:
sudo useradd -r -s /bin/false springapp
sudo mkdir -p /opt/springapp
sudo chown springapp:springapp /opt/springapp
Upload your JAR via SCP or pull from S3:
# From S3
aws s3 cp s3://your-bucket/releases/myapp-1.0.0.jar /opt/springapp/app.jar
sudo chown springapp:springapp /opt/springapp/app.jar
Step 4 — Systemd service
Create /etc/systemd/system/springapp.service:
[Unit]
Description=Spring Boot Application
After=network.target
[Service]
User=springapp
Group=springapp
WorkingDirectory=/opt/springapp
ExecStart=/usr/bin/java \
-Xms1g -Xmx1g \
-XX:+UseG1GC \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/opt/springapp/dumps/ \
-Dspring.profiles.active=production \
-jar app.jar
Restart=always
RestartSec=10
SuccessExitStatus=143
StandardOutput=journal
StandardError=journal
PrivateTmp=true
NoNewPrivileges=true
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
sudo mkdir -p /opt/springapp/dumps
sudo chown springapp:springapp /opt/springapp/dumps
sudo systemctl daemon-reload
sudo systemctl start springapp
sudo systemctl enable springapp
Step 5 — nginx reverse proxy
sudo apt install -y nginx
Create /etc/nginx/sites-available/springapp:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
}
sudo ln -s /etc/nginx/sites-available/springapp /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Step 6 — SSL with Certbot
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
Step 7 — CloudWatch agent for JVM metrics
Install the CloudWatch agent to ship system and JVM metrics:
wget https://amazoncloudwatch-agent.s3.amazonaws.com/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb
Configure the agent to collect memory, disk, and CPU metrics. Combine this with Spring Boot Actuator's /actuator/metrics endpoint pushed via Micrometer's CloudWatch registry for JVM-level metrics (heap usage, GC pauses, thread count).
Add the Micrometer CloudWatch dependency to your Spring Boot project:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-cloudwatch2</artifactId>
</dependency>
With Reflex
Reflex provides an alternative to complex CloudWatch configuration by monitoring your EC2 instance and Spring Boot application from a single agent. It tracks JVM heap, GC pauses, HTTP error rates, and system resources — and can execute automated recovery playbooks when issues arise, without requiring IAM role configuration or CloudWatch dashboards. See How it works.