Deploy your code on a Docker Container using Jenkins on AWS

Description: We are going to deploy a Java Web app on a Docker Container built on an EC2 Instance using Jenkins.
Agenda:
Setup Jenkins (Jenkins-EC2 instance)
Setup & Configure Maven and Git (Jenkins-EC2 instance)
Integrating GitHub and Maven with Jenkins (Jenkins-EC2 instance)
Setup Docker Host (Docker EC2 instance)
Integrate Docker with Jenkins
Automate the Build and Deploy process using Jenkins
Test the deployment
Step 1: Setup Jenkins Server on AWS EC2 Instance
Setup a Linux EC2 Instance
Install Java
Install Jenkins
Start Jenkins
Access Web UI on port 8080
Step 1.1: Setup a Linux EC2 instance with mentioned configurations
Step 1.2.3.4: For installation of Java, Jenkins use below user data scrip
#!/bin/bash
set -e
# Update the OS
sudo yum update -y
# Add Jenkins repository and import the key
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
# Upgrade all packages
sudo yum upgrade -y
# Install Java 17 (required for Jenkins 2.404+)
sudo yum install -y java-17-amazon-corretto
# Install Jenkins
sudo yum install -y jenkins
# Enable and start Jenkins service
sudo systemctl enable jenkins
sudo systemctl start jenkins
# Install additional tools: Git, Maven
sudo yum install -y git maven
# Install Amazon SSM Agent (recommended yum/rpm method for Amazon Linux)
sudo yum install -y https://s3.amazonaws.com/amazon-ssm-${AWS_REGION:-us-east-1}/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
# Set environment variables for Java and Maven for all users
JAVA_HOME_PATH="/usr/lib/jvm/java-17-amazon-corretto.x86_64"
M2_HOME_PATH="/usr/share/maven"
M2_BIN_PATH="/usr/share/maven/bin"
cat <<EOF | sudo tee /etc/profile.d/dev_env.sh
export JAVA_HOME=$JAVA_HOME_PATH
export M2_HOME=$M2_HOME_PATH
export M2=$M2_BIN_PATH
export PATH=\$PATH:\$JAVA_HOME/bin:\$M2
EOF
source /etc/profile.d/dev_env.sh
# Optionally check Jenkins service status and disk usage
sudo systemctl status jenkins
df -h
echo "Jenkins, Java 17, Git, Maven, and SSM Agent installed and configured successfully!"
Note: Run this script to check everything is installed or not:
echo "==== Jenkins ===="
sudo systemctl status jenkins | grep Active
echo "==== Java ===="
java -version
echo "==== Git ===="
git --version
echo "==== Maven ===="
mvn -version
echo "==== SSM Agent ===="
sudo systemctl status amazon-ssm-agent | grep Active
echo "==== Environment Variables ===="
echo "JAVA_HOME: $JAVA_HOME"
echo "M2_HOME: $M2_HOME"
echo "M2: $M2"
Step: 1.5 Access Jenkins UI on http://publicip:8080
Step 2: Integrate GitHub with Jenkins
Install Git on Jenkins Instance
Install Github Plugin on Jenkins GUI
Configure Git on Jenkins GUI
Step 2.1: Git already installed using user-data scrip
Step 2.2: Install Github plugin on Jenkins GUs
Step 3: Integrate Maven with Jenkins
Setup Maven on Jenkins Server (Done through User data script)
Setup Environment Variables JAVA_HOME,M2,M2_HOME ((Done through User data script)
Install Maven Plugin (GUI)
Configure Maven and Java (GUI)
Step 3.3: Now we need to update the paths where Java and Maven have been installed in the Jenkins UI. We will first install the Maven Integration Plugin as shown below
Step 3.4: After clicking on Install without restart, go again to manage Jenkins and select Global Tool configuration to set the paths for Java and Maven
Step 4: Setup Docker Host
Setup a Linux EC2 Instance
Install Docker
Start Docker Services
Run Basic Docker Commands
Step 4.1: Let's first launch an EC2 Instance. We will skip the steps here as we have already shown earlier how to create an EC2 Instance.
Use user data script to run Docker:
#!/bin/bash
# This script installs Docker, starts Docker service, and runs basic Docker commands on Amazon Linux 2
# Update the system
echo "Updating system packages..."
sudo yum update -y
# Install SSM Agent (for SSM login capability)
# Install Amazon SSM Agent (recommended yum/rpm method for Amazon Linux)
sudo yum install -y https://s3.amazonaws.com/amazon-ssm-${AWS_REGION:-us-east-1}/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
# Install Docker
echo "Installing Docker..."
sudo amazon-linux-extras install docker -y
# Start Docker service
echo "Starting Docker service..."
sudo systemctl enable docker
sudo systemctl start docker
# Add ec2-user to docker group to run docker without sudo
sudo usermod -a -G docker ec2-user
# Run basic Docker commands
echo "Running basic Docker commands..."
# Display Docker version
echo "Docker version:"
docker version
# Pull hello-world image
echo "Pulling hello-world image..."
sudo docker pull hello-world
# Run hello-world container
echo "Running hello-world container..."
sudo docker run hello-world
echo "Docker installation and basic setup completed!"
Step 4.4: Create Tomcat Docker Container
docker pull tomcat
docker images
docker run -d --name tomcat-container -p 8081:8080 tomcat
docker ps
Note: Before accessing our container from the browser we need to allow port 8081 in the Security Group of our EC2 docker-host machine.
Create security group rule, select Custom TCP as type, and in port range put 8081–9000
Now let’s take the public IP of our Docker-host EC2 machine and with port 8081 access it from our browser
From the above screenshot, you can see that although there is a 404 error, it also displays Apache Tomcat at the bottom which means the installation is successful however this is a known issue with the Tomcat docker image that we will fix in the next steps.
The above issue occurs because whenever we try to access the Tomcat server from the browser it will look for the files in /webapps directory which is empty and the actual files are being stored in /webapps.dist.
So in order to fix this issue we will copy all the content from webapps.dist to webapps directory and that will resolve the issue.
Let's access our tomcat container and perform the steps as shown below:
docker exec -it tomcat-container /bin/bash
cd webapps.dist/
cp -R * ../webapps/
cd ../webapps/
ls -lrth
After that we should be able to access our tomcat docker container
Note: Somehow if we stop this container and start another container with the same Image on a different port we will face the same issue of 404 error. This happens because every time we launch a new container we are using a new Image as the previous container gets deleted.
This issue can be solved by creating our own Docker Image with the appropriate changes required to run our container. This can be achieved by creating a Dockerfile on which we can mention the steps required to build the docker image and from that Image, we can run the container.
Create a Customized Dockerfile for Tomcat:
vim Dockerfile
FROM tomcat:9.0-jdk11
RUN cp -R /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps
# Expose Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]
docker build -t tomcat-latest .
docker run -d -p 8085:8080 --name tomcat-latest tomcat-latest
Now you can access the tomcat page on port 8085
Step 5: Integrate Docker with Jenkins
Create a dockeradmin user
Install the “Publish Over SSH” plugin
Add Dockerhost to Jenkins “configure systems”
Step 5.1: Let's first create a dockeradmin user and create a password for it as well.
# sudo adduser dockeradmin
# sudo passwd dockeradmin
sudo mkdir /home/dockeradmin/.ssh
sudo chown dockeradmin:dockeradmin /home/dockeradmin/.ssh
sudo chmod 700 /home/dockeradmin/.ssh
# Now let’s add this user to the Docker group with the below command:
# usermod -aG docker dockeradmin
#Now login to Jenkins server and create a ssh key public and private file
sh-keygen -t rsa
#Now copy files to Jenkins user and test connectivity:
# Copy the private key to jenkins user
sudo cp /home/ec2-user/.ssh/id_rsa /var/lib/jenkins/.ssh/
sudo chown jenkins:jenkins /var/lib/jenkins/.ssh/id_rsa
sudo chmod 600 /var/lib/jenkins/.ssh/id_rsa
# Also copy the public key
sudo cp /home/ec2-user/.ssh/id_rsa.pub /var/lib/jenkins/.ssh/
sudo chown jenkins:jenkins /var/lib/jenkins/.ssh/id_rsa.pub
sudo chmod 644 /var/lib/jenkins/.ssh/id_rsa.pub
# Switch to jenkins user and test
sudo su - jenkins
ssh dockeradmin@172.31.86.2 "whoami"
Now in order to access the server using the newly created User we need to allow password-based authentication. For that, we need to do some changes in the /etc/ssh/sshd_config file
As you can see above we need to uncomment PasswordAuthentication yes and comment out PasswordAuthentication no.
Once we have updated the config file we need to restart the services with the command:
service sshd reload
systemctl reload sshd
Redirecting to /bin/systemctl reload sshd.service
Now we would be able to log in using the dockeradmin user credentials
Step 5.2: Now next step is to integrate Docker with Jenkins, for that we need to install the “Publish Over SSH” plugin:
Go to Manage Jenkins > Manage Plugins > Available plugins and search for publish over ssh plugin
Now we need to configure our dockerhost in Jenkins. For that go to Manage Jenkins > Configure System:
On the next page after scrolling down you would be able to see Publish over SSH section where you need to add a new SSH server with the info as shown below
Also copy the .pem file of dockerhost vm:
Note: It should be noted that it's best practice to use ssh keys however for this demo we are using password-based authentication. In the above screenshot, we have provided details of our Docker host which we created on EC2 Instance. Also, note the use of private IP under Hostname as both our Jenkins Server and Dockerhost are on the same subnet. We can also use Public IP here.
Click on Apply and Save to proceed. With this, our Docker integration with Jenkins is successfully accomplished.
Now have to add global configuration of ssh
Now run the pipeline to check the server connectivity from Jenkins server to dockerhost server.
pipeline {
agent any
stages {
stage('Test connectivity') {
steps {
sh 'whoami'
sh 'ssh -o StrictHostKeyChecking=no dockeradmin@172.31.86.2 "whoami && pwd"'
}
}
}
}
Step 6: Now run the application using Jenkins pipeline
Create deployment script file
Create Dockerfile
Create Pipeline
Step 6.1: Create deployment script to run tomcat applications:
#cd /home/dockeradmin/tomcatproject
#vim deploy.sh
#!/bin/bash
echo "Starting deployment..."
# Stop and remove existing container
docker stop my-tomcat-app || true
docker rm my-tomcat-app || true
# Remove old image
docker rmi my-tomcat-app:latest || true
# Build new image
docker build -t my-tomcat-app:latest .
# Run new container
docker run -d --name my-tomcat-app -p 8081:8080 my-tomcat-app:latest
echo "Deployment completed!"
echo "Application available at: http://localhost:8081"
Step 6.2: Create Dockerfile to run application
# cd /home/dockeradmin/tomcatproject
# vim Dockerfile
FROM tomcat:latest
RUN cp -R /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps
EXPOSE 8080
CMD ["catalina.sh", "run"]
Step 6.3: Create pipeline:
pipeline {
agent any
stages {
stage('Checking file present or not'){
steps{
echo 'Cheking all files'
script {
sh '''
ssh dockeradmin@172.31.86.2 "
cd /home/dockeradmin/tomcat-project
ls -lrth
"
'''
}
}
}
stage('Deploying tomcat-application'){
steps{
echo 'Deploying Docker host'
script {
sh '''
ssh dockeradmin@172.31.86.2 "
cd /home/dockeradmin/tomcat-project
chmod +x deploy.sh
./deploy.sh
"
'''
}
}
}
stage('Veryfying deployemnt'){
steps{
echo 'Check docker images and docker container'
script{
sh '''
ssh dockeradmin@172.31.86.2 "
docker images
docker ps -a
"
'''
}
}
}
}
post {
success {
echo 'Deployment successful! Access your app at: http://172.31.86.2:8081'
}
failure {
echo 'Deployment failed!'
}
}
}
Note: Row check in the browser using http:publicip:8081
Conclusion
In conclusion, this tutorial demonstrated how to set up a seamless CI/CD pipeline using Jenkins and Tomcat for Java applications. By following the steps outlined, you can automate the build and deployment process, allowing developers to focus on writing code and improving application features. Integrating GitHub webhooks ensures that the pipeline is triggered automatically upon changes in the repository, further streamlining the development workflow.
Subscribe to my newsletter
Read articles from Ashutosh ojha directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ashutosh ojha
Ashutosh ojha
DevOps Engineer with 4+ years of experience in automating, optimizing, and scaling AWS and on-prem infrastructures. I specialize in CI/CD pipeline design, Kubernetes (on-prem & EKS), and infrastructure as code with Ansible and Terraform. Successfully migrated 200+ servers to AWS with zero downtime and implemented robust observability stacks (Prometheus, Grafana). Driven by automation, I focus on building secure, scalable, and cost-efficient solutions, with a strong track record in monitoring, incident response, and reducing deployment times. Always open to connecting about DevOps, Cloud, and Automation!