Deploying a Java Web Application on AWS Using Managed Cloud Services
Posted as part of my DevOps learning journey | Udemy Course: Decoding DevOps – From Basics to Advanced Projects with AI

Deploying a Java web application to the cloud is a milestone moment for any developer or team. But simply moving your app to a cloud server and managing everything manually — databases, caches, message queues, and load balancers — quickly becomes complex, expensive, and hard to scale. AWS offers a smarter path: replacing each self-managed component with a fully managed, cloud-native service.
In this guide, you will learn how to deploy a Java web application on AWS using a re-architecture (refactoring) strategy. Instead of provisioning raw EC2 instances and installing everything by hand, we use AWS-managed PaaS and SaaS services that handle scaling, patching, backups, and availability automatically.
The application stack we'll deploy includes a Tomcat-based Java app, a MySQL relational database, a Memcached caching layer, and a RabbitMQ message broker — all hosted on AWS using the following managed services:
Elastic Beanstalk — hosts and auto-scales the Java/Tomcat application
Amazon RDS — manages the MySQL database
Amazon ElastiCache — manages the Memcached cache
Amazon MQ — manages the RabbitMQ message broker
Amazon Route 53 — handles DNS routing
Amazon CloudFront — serves content globally via a CDN
Amazon S3 — stores deployment artifacts
By the end of this guide, your application will be running behind an HTTPS-enabled load balancer, served through a global CDN, with a fully automated deployment pipeline — all with minimal operational overhead.
Attribution
The application source code used in this guide was developed by a Udemy course instructor and is available on GitHub: Link
This blog post does not claim ownership of the source code. It solely demonstrates the AWS deployment process based on concepts taught in the course, supplemented with general AWS best practices for a broader audience.
Prerequisites
Before you start, make sure you have:
An active AWS account
Java 17+ and Maven 3.9+ installed on your local machine
The application source code cloned from its Git repository (switch to the appropriate branch, i.e.,
awsrefactor)A registered domain name (e.g., via GoDaddy or Amazon Route 53) if you want HTTPS with a custom URL
An SSL/TLS certificate created in AWS Certificate Manager (ACM) for your domain
Step 1: Create the Backend Security Group & Key Pair
All backend services (RDS, ElastiCache, Amazon MQ) will share a single backend security group. This lets them communicate with each other and can be updated later to allow Beanstalk traffic.
In the AWS Console, go to EC2 → Security Groups → Create Security Group
Name it something descriptive (e.g.,
vprofile-backend-sg) and skip adding inbound rules for nowAfter the group is created, copy its Security Group ID
Go to Edit Inbound Rules → Add Rule: select All Traffic and set the source to the security group's own ID. This allows all backend services to talk to each other
Save the rules
Next, create a Key Pair for SSH access to Beanstalk EC2 instances (useful for debugging):
Go to EC2 → Key Pairs → Create Key Pair
Give it a name (e.g.,
vprofile-key) and download the.pemfile safely.
Step 2: Create the Amazon RDS Instance (MySQL)
Your Java app needs a relational database. Amazon RDS manages patching, backups, and scaling for you.
2a. Create a Parameter Group
Go to RDS → Parameter Groups → Create Parameter Group
Engine type: MySQL Community, Family: MySQL 8.4, Type: DB Parameter Group
Give it a recognizable name (e.g.,
vprofile-rds-paragrp)
2b. Create a Subnet Group
Go to RDS → Subnet Groups → Create DB Subnet Group
Select your VPC (use the default if you haven't created a custom one)
Select all Availability Zones and subnets; if you get an error on a specific zone, deselect it and retry
2c. Create the RDS Instance
Go to RDS → Create Database → Standard Create
Engine: MySQL, Version: 8.4.x
Template: Dev/Test (or Sandbox if you want only free-tier options)
Instance identifier: give your RDS a name (e.g.,
vprofile-rds)Credentials: Username
admin, password set to Self Managed → Auto GenerateInstance class: db.t3.micro or db.t2.micro (1 GB RAM)
Storage: GP3, set allocated storage to 20 GB, and uncheck storage auto-scaling for this learning environment
Connectivity: No public access (private, inside VPC), select your backend security group
Additional configuration → Initial database name: set this to
accounts(the name your application expects)Select your custom DB parameter group
Disable automated backups for this exercise, then click Create Database
⚠️ Important: Immediately after clicking Create Database, click View connection details and save the auto-generated password — it will not be shown again.
Step 3: Create Amazon ElastiCache (Memcached)
ElastiCache replaces a self-managed Memcached instance that caches database query results.
3a. Create a Parameter Group for Cache
Go to ElastiCache → Parameter Groups → Create Parameter Group
Family: memcached1.6, give it a name (e.g.,
vprofile-cache-paragrp)
3b. Create a Subnet Group for Cache
Go to ElastiCache → Subnet Groups → Create Subnet Group
Select your default VPC and all available subnets
3c. Create the Memcached Cluster
Go to ElastiCache → Memcached Caches → Create Memcached Cache
Select Node-based cluster → Cluster cache (to see all options)
Cluster name: e.g.,
vprofile-cacheEngine version: 1.6.22 (or any 1.6.x)
Port: 11211 (must match the port in your
application.propertiesfile)Node type: t3.micro (half GB RAM)
Number of nodes: 1
Uncheck "Encryption in transit" — most standard Java app configs for Memcached don't support it by default
Security group: select your backend security group
Click Create
Step 4: Create Amazon MQ (RabbitMQ)
Amazon MQ is a managed message broker service that replaces a self-hosted RabbitMQ instance.
Go to Amazon MQ → Create Broker
Select RabbitMQ as the broker type
Deployment: Single-instance broker (for learning/dev)
Broker name: e.g.,
vprofile-rabbitmqInstance type: minimum available (1 vCPU, 4 GB RAM)
Username:
rabbit, Password: a strong 12+ character password — save this!Under Additional Settings, Broker engine version: 3.13.x (more stable than 4.x)
Access type: Private (your app connects inside the VPC)
VPC/Subnet: default VPC and default subnet
Security group: select your backend security group
Click Create Broker
Note: The connection port for Amazon MQ RabbitMQ is 5671 (TLS), while the default RabbitMQ port is 5672. You'll need to update this in your
application.propertiesfile.
Step 5: Initialize the RDS Database
Since the RDS instance has no public access, you cannot connect to it directly from your laptop. You must launch a temporary EC2 instance in the same VPC to run the initialization script.
Launch a temporary Ubuntu EC2 instance (t2.micro) in the same region (e.g., us-east-1)
Create a new security group for this instance that allows SSH (port 22) from your IP only
Update the backend security group's inbound rules: add a rule for MySQL/Aurora (port 3306) sourced from the temporary EC2 instance's security group
SSH into the EC2 instance:
ssh -i /path/to/key.pem ubuntu@<PUBLIC_IP>
- Install MySQL client and Git:
sudo apt update && sudo apt install mysql-client git -y
Clone your application's source code repository and switch to the correct branch (i.e.,
awsrefactor)Log in to RDS and run the schema SQL file:
mysql -h <RDS_ENDPOINT> -u admin -p accounts < src/main/resources/db_backup.sql
Verify by logging in again and running
SHOW TABLES;— you should see the application tablesTerminate the temporary EC2 instance once initialization is complete
Step 6: Set Up IAM Roles for Beanstalk
Elastic Beanstalk requires specific IAM permissions to manage EC2 instances, load balancers, and other resources.
Go to IAM → Roles → Create Role
Trusted entity: AWS Service → EC2
Add these four policies:
AWSElasticBeanstalkWebTierAWSElasticBeanstalkWorkerTierAWSElasticBeanstalkMulticontainerDockerAWSElasticBeanstalkCustomPlatformforEC2Role
Name the role (e.g.,
vprofile-beanstalk-ec2-role) and create itA service role for Beanstalk itself will either be auto-created during setup, or you can create it by clicking "Create Role" in the Beanstalk configuration wizard.
Step 7: Create the Elastic Beanstalk Environment
Elastic Beanstalk bundles together EC2 instances, a load balancer, an auto-scaling group, CloudWatch monitoring, and S3 artifact storage — all managed for you.
Go to Elastic Beanstalk → Create Application
Environment type: Web Server Environment
Give your application and environment meaningful names; choose a unique domain and check its availability
Platform: Tomcat, Branch: Tomcat 10 with Corretto 21 (Java 21)
Application code: Sample application (we'll deploy the real artifact later)
Preset: Custom configuration
Click Next → Configure Service Access:
Service role: select the auto-created Beanstalk service role (or create one as described above)
EC2 instance profile: select the IAM role you created in Step 6
EC2 key pair: select the key pair from Step 1
VPC: select the default VPC; enable public IP addresses on instances; select all available subnets
Do not attach a database here — we created RDS independently for better decoupling
Instance settings:
Root volume type: GP3 (important — avoids deprecated Launch Configuration errors)
CloudWatch monitoring interval: 5 minutes
Capacity: select Load Balanced with min 2 instances and max 4; instance type: t2.micro or t3.micro
Scaling trigger: use NetworkOut as the metric — when traffic increases, more instances are added
Load Balancer: type Application Load Balancer, visibility Public
Under Processes, edit the default process: enable session stickiness so users stay connected to the same instance (required for apps that store session state locally)
Health reporting: Enhanced — this enables health-aware rolling deployments
Deployment policy: set to Rolling, batch size 50% — one instance is updated at a time
Review all settings and click Submit
Beanstalk will now provision everything. This may take 5–10 minutes.
Step 8: Update the Backend Security Group for Beanstalk
Once Beanstalk finishes creating your environment, it also creates a security group for its EC2 instances. You must allow this security group to talk to your backend services.
Go to EC2 → Instances, select one of the Beanstalk instances, and navigate to the Security tab
Copy the Security Group ID of the instance security group (not the load balancer SG)
Go to your backend security group → Edit Inbound Rules → Add Rule
Rule: All Traffic, Source: the Beanstalk instance security group ID
Save rules
Step 9: Build the Artifact and Update Application Config
Now collect the endpoints from all backend services and update your application.properties file:
Endpoints to collect:
RDS endpoint (from RDS → Databases → your instance → Connectivity)
ElastiCache endpoint (from ElastiCache → your cluster → Configuration Endpoint)
Amazon MQ endpoint (from Amazon MQ → Brokers → your broker → Connections → copy the AMQP URL hostname)
RabbitMQ username and password (the ones you set in Step 4)
Update src/main/resources/application.properties in your source code:
# MySQL RDS
spring.datasource.url=jdbc:mysql://<RDS_ENDPOINT>:3306/accounts
spring.datasource.username=admin
spring.datasource.password=<RDS_PASSWORD>
# Memcached / ElastiCache
memcached.active.host=<ELASTICACHE_ENDPOINT>
memcached.active.port=11211
# RabbitMQ / Amazon MQ
rabbitmq.address=<AMAZON_MQ_ENDPOINT>
rabbitmq.port=5671
rabbitmq.username=rabbit
rabbitmq.password=<RABBITMQ_PASSWORD>
Once updated, build the artifact locally using Maven:
mvn clean install
If the build succeeds, a .war file will be created inside the target/ folder.
Step 10: Deploy the Artifact to Beanstalk
Go to Elastic Beanstalk → your environment
Click Upload and Deploy
Choose the
.warfile from yourtarget/directoryGive it a version label (e.g.,
vprofile-v1.0)Click Deploy
Beanstalk will deploy in rolling fashion — 50% of instances at a time. Monitor progress under the Events tab. Under EC2 → Target Groups, you can watch instances drain and become healthy as each one is updated.
Once deployment is complete, click the Beanstalk URL. You should see your application's login page.
Step 11: Add HTTPS (SSL/TLS) via ACM Certificate
A production app should always use HTTPS.
Go to Beanstalk → Configuration → Instance Traffic and Scaling → Edit
Scroll to Load Balancer Listeners → Add Listener
Protocol: HTTPS, Port: 443
SSL Certificate: select the certificate for your domain from ACM
SSL Policy: select a modern policy (e.g.,
ELBSecurityPolicy-TLS13-1-2)Click Save → Apply
Then create a CNAME DNS record with your domain registrar (GoDaddy, Namecheap, or Route 53):
Name: e.g.,
vproappValue: your Beanstalk environment URL
Wait a few minutes for DNS propagation, then access https://vproapp.yourdomain.com.
Step 12: Set Up CloudFront for Global Content Delivery
If you have a global audience, CloudFront caches content at 600+ edge locations worldwide, reducing latency significantly.
Go to CloudFront → Create Distribution
Distribution type: Single website or app
Origin: select Elastic Load Balancer, then browse and select your Beanstalk load balancer
Allowed HTTP methods: all methods (GET, HEAD, POST, PUT, etc.)
Protocol: HTTP and HTTPS or Redirect HTTP to HTTPS
Cache policy: use default or customize based on your app's caching needs
Skip domain setup for now, then create the distribution
Once created, go to General → Add Domain and add your custom hostname (e.g.,
vproapp.yourdomain.com)Select the matching ACM certificate
Click Add Domain
CloudFront deployment takes ~10 minutes. Afterward, update your DNS CNAME to point to the CloudFront distribution domain name (e.g., xxxx.cloudfront.net) instead of the Beanstalk URL directly.
Cleanup (Avoid Unwanted Charges)
Once done, delete resources in this order to avoid dependency errors:
Disable CloudFront distribution, wait for it to fully disable, then delete it
Remove the DNS CNAME record from your domain registrar
Delete RDS (uncheck final snapshot and automated backups)
Delete ElastiCache Memcached cluster
Delete Amazon MQ broker
Edit the backend security group: remove the rule that allows Beanstalk instance traffic
Terminate the Beanstalk Environment (this also removes the load balancer, EC2 instances, and S3 artifacts)
Optionally, delete the backend security group and the key pair
Conclusion
Deploying a Java web application on AWS becomes much easier when you use managed services instead of handling every server component manually. With Elastic Beanstalk, RDS, ElastiCache, Amazon MQ, Route 53, and CloudFront, you can build a scalable, reliable, and production-ready architecture with far less operational effort.
This blog showed how to take a Java application from source code to a fully deployed cloud setup, including backend initialization, application packaging, Beanstalk deployment, and optional HTTPS and CDN configuration. It also highlighted the importance of using the right AWS service for the right job, especially when moving from a traditional server-based model to a modern cloud-native approach.
Overall, this deployment model is a strong example of how AWS can simplify infrastructure while improving flexibility, performance, and maintainability.



