Skip to main content

Command Palette

Search for a command to run...

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

Updated
13 min read
Deploying a Java Web Application on AWS Using Managed Cloud Services

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.

  1. In the AWS Console, go to EC2 → Security Groups → Create Security Group

  2. Name it something descriptive (e.g., vprofile-backend-sg) and skip adding inbound rules for now

  3. After the group is created, copy its Security Group ID

  4. 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

  5. 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 .pem file 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

  1. Go to RDS → Create Database → Standard Create

  2. Engine: MySQL, Version: 8.4.x

  3. Template: Dev/Test (or Sandbox if you want only free-tier options)

  4. Instance identifier: give your RDS a name (e.g., vprofile-rds)

  5. Credentials: Username admin, password set to Self Managed → Auto Generate

  6. Instance class: db.t3.micro or db.t2.micro (1 GB RAM)

  7. Storage: GP3, set allocated storage to 20 GB, and uncheck storage auto-scaling for this learning environment

  8. Connectivity: No public access (private, inside VPC), select your backend security group

  9. Additional configuration → Initial database name: set this to accounts (the name your application expects)

  10. Select your custom DB parameter group

  11. 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

  1. Go to ElastiCache → Memcached Caches → Create Memcached Cache

  2. Select Node-based clusterCluster cache (to see all options)

  3. Cluster name: e.g., vprofile-cache

  4. Engine version: 1.6.22 (or any 1.6.x)

  5. Port: 11211 (must match the port in your application.properties file)

  6. Node type: t3.micro (half GB RAM)

  7. Number of nodes: 1

  8. Uncheck "Encryption in transit" — most standard Java app configs for Memcached don't support it by default

  9. Security group: select your backend security group

  10. Click Create

Step 4: Create Amazon MQ (RabbitMQ)

Amazon MQ is a managed message broker service that replaces a self-hosted RabbitMQ instance.

  1. Go to Amazon MQ → Create Broker

  2. Select RabbitMQ as the broker type

  3. Deployment: Single-instance broker (for learning/dev)

  4. Broker name: e.g., vprofile-rabbitmq

  5. Instance type: minimum available (1 vCPU, 4 GB RAM)

  6. Username: rabbit, Password: a strong 12+ character password — save this!

  7. Under Additional Settings, Broker engine version: 3.13.x (more stable than 4.x)

  8. Access type: Private (your app connects inside the VPC)

  9. VPC/Subnet: default VPC and default subnet

  10. Security group: select your backend security group

  11. 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.properties file.

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.

  1. Launch a temporary Ubuntu EC2 instance (t2.micro) in the same region (e.g., us-east-1)

  2. Create a new security group for this instance that allows SSH (port 22) from your IP only

  3. 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

  4. SSH into the EC2 instance:

ssh -i /path/to/key.pem ubuntu@<PUBLIC_IP>
  1. Install MySQL client and Git:
sudo apt update && sudo apt install mysql-client git -y
  1. Clone your application's source code repository and switch to the correct branch (i.e., awsrefactor)

  2. Log in to RDS and run the schema SQL file:

mysql -h <RDS_ENDPOINT> -u admin -p accounts < src/main/resources/db_backup.sql
  1. Verify by logging in again and running SHOW TABLES; — you should see the application tables

  2. Terminate 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.

  1. Go to IAM → Roles → Create Role

  2. Trusted entity: AWS Service → EC2

  3. Add these four policies:

    • AWSElasticBeanstalkWebTier

    • AWSElasticBeanstalkWorkerTier

    • AWSElasticBeanstalkMulticontainerDocker

    • AWSElasticBeanstalkCustomPlatformforEC2Role

  4. Name the role (e.g., vprofile-beanstalk-ec2-role) and create it

    A 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.

    1. Go to Elastic Beanstalk → Create Application

    2. Environment type: Web Server Environment

    3. Give your application and environment meaningful names; choose a unique domain and check its availability

    4. Platform: Tomcat, Branch: Tomcat 10 with Corretto 21 (Java 21)

    5. Application code: Sample application (we'll deploy the real artifact later)

    6. Preset: Custom configuration

    7. 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

    8. VPC: select the default VPC; enable public IP addresses on instances; select all available subnets

    9. Do not attach a database here — we created RDS independently for better decoupling

    10. Instance settings:

      • Root volume type: GP3 (important — avoids deprecated Launch Configuration errors)

      • CloudWatch monitoring interval: 5 minutes

    11. Capacity: select Load Balanced with min 2 instances and max 4; instance type: t2.micro or t3.micro

    12. Scaling trigger: use NetworkOut as the metric — when traffic increases, more instances are added

    13. Load Balancer: type Application Load Balancer, visibility Public

    14. 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)

    15. Health reporting: Enhanced — this enables health-aware rolling deployments

    16. Deployment policy: set to Rolling, batch size 50% — one instance is updated at a time

    17. 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.

  1. Go to EC2 → Instances, select one of the Beanstalk instances, and navigate to the Security tab

  2. Copy the Security Group ID of the instance security group (not the load balancer SG)

  3. Go to your backend security group → Edit Inbound Rules → Add Rule

  4. Rule: All Traffic, Source: the Beanstalk instance security group ID

  5. 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

  1. Go to Elastic Beanstalk → your environment

  2. Click Upload and Deploy

  3. Choose the .war file from your target/ directory

  4. Give it a version label (e.g., vprofile-v1.0)

  5. 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.

  1. Go to Beanstalk → Configuration → Instance Traffic and Scaling → Edit

  2. Scroll to Load Balancer Listeners → Add Listener

  3. Protocol: HTTPS, Port: 443

  4. SSL Certificate: select the certificate for your domain from ACM

  5. SSL Policy: select a modern policy (e.g., ELBSecurityPolicy-TLS13-1-2)

  6. Click Save → Apply

Then create a CNAME DNS record with your domain registrar (GoDaddy, Namecheap, or Route 53):

  • Name: e.g., vproapp

  • Value: 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.

  1. Go to CloudFront → Create Distribution

  2. Distribution type: Single website or app

  3. Origin: select Elastic Load Balancer, then browse and select your Beanstalk load balancer

  4. Allowed HTTP methods: all methods (GET, HEAD, POST, PUT, etc.)

  5. Protocol: HTTP and HTTPS or Redirect HTTP to HTTPS

  6. Cache policy: use default or customize based on your app's caching needs

  7. Skip domain setup for now, then create the distribution

  8. Once created, go to General → Add Domain and add your custom hostname (e.g., vproapp.yourdomain.com)

  9. Select the matching ACM certificate

  10. 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:

  1. Disable CloudFront distribution, wait for it to fully disable, then delete it

  2. Remove the DNS CNAME record from your domain registrar

  3. Delete RDS (uncheck final snapshot and automated backups)

  4. Delete ElastiCache Memcached cluster

  5. Delete Amazon MQ broker

  6. Edit the backend security group: remove the rule that allows Beanstalk instance traffic

  7. Terminate the Beanstalk Environment (this also removes the load balancer, EC2 instances, and S3 artifacts)

  8. 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.