Hands-on Jenkins CD Project (Intermediate) β Deploying Docker Containers on AWS EC2
Building on Jenkins CD to Implement Continuous Deployment Using Docker
π Introduction
In the previous project, a Jenkins-based Continuous Integration (CI) pipeline was implemented from scratch using Jenkins, Docker, and GitHub webhooks. The focus of that project was on build automation, early failure detection, and debugging in a production-like environment.
This project builds on that foundation and focuses exclusively on Continuous Deployment (CD).
The objective here is to automatically deploy the application after a successful CI build, by running a Docker container on the same AWS EC2 instance where Jenkins is hosted.
This project demonstrates a practical, beginner-to-intermediate Continuous Deployment workflow that is commonly used for internal services and small-scale applications.
π― Project Objective
The goals of this project are to:
Extend an existing Jenkins CI pipeline to support Continuous Deployment
Automatically deploy the latest Docker image after a successful build
Replace any previously running application instance
Verify deployment through browser access
Maintain a clean, repeatable, and predictable deployment process
π§© Project Scope & Assumptions
This project is based on the following assumptions:
Jenkins is already installed and running on an AWS EC2 instance
Docker is already installed on the EC2 instance
Jenkins has permission to execute Docker commands
A working Jenkins CI pipeline already exists
GitHub webhook integration is already configured
β οΈ Installation and CI setup steps are intentionally omitted, as they were covered in detail in Project 1.
ποΈ Continuous Deployment Flow
The Continuous Deployment flow implemented in this project is as follows:
Code push to GitHub
β
Jenkins CI pipeline executes successfully
β
Docker image is built
β
Deployment stage runs automatically
β
Existing container is stopped (if running)
β
New container is deployed
β
Application is accessible via browser
The automatic deployment stage is what differentiates Continuous Deployment (CD) from Continuous Integration (CI).
πΉ Phase 1 β Deployment Strategy
Deployment Target
Host: Same AWS EC2 instance running Jenkins
Deployment Method: Docker container
Application Port: Separate from Jenkins (e.g., 8081)
Why This Approach?
Deploying the application on the same EC2 instance:
Keeps the infrastructure simple
Reduces operational complexity
Makes debugging easier
Is ideal for learning and demonstration purposes
This approach reflects how many internal tools and small services are deployed in real-world environments.
πΉ Phase 2 β Deployment Design & Failure Planning
Before implementing Continuous Deployment in Jenkins, the deployment process must be designed carefully, with attention to potential failure scenarios.
Deployment failures are common when applications are redeployed repeatedly on the same host. This phase focuses on planning first, rather than immediately writing pipeline code.
π§ Deployment Design Principles
The following principles guided the deployment design:
Deployment should be fully automated
The process should be repeatable across multiple runs
Existing application instances should not cause conflicts
Deployment should execute only after a successful CI build
The logic should be simple and easy to debug
These principles help avoid unstable deployments and unnecessary manual intervention.
π§ Container Deployment Strategy
The application is deployed using Docker with the following strategy:
A fixed container name is used
The application runs inside a Docker container
The container exposes the application on a dedicated port
Each deployment replaces the previously running container
Using a fixed container name simplifies container lifecycle management and ensures that only one instance of the application runs at a time.
β οΈ Identifying Potential Deployment Failures
Before implementing deployment, the following common failure scenarios were identified:
1οΈβ£ Container Already Running
Docker will fail if a container with the same name already exists.
2οΈβ£ Port Conflict
Deployment will fail if the application port is already in use.
3οΈβ£ Partial Cleanup
An improperly stopped container can interfere with subsequent deployments.
These issues are common in real production-like environments.
π― Planned Failure Scenario (Intentional)
To maintain the failure-driven learning approach used in Project 1, the initial deployment is intentionally designed to fail due to an existing running container.
This helps demonstrate:
How Jenkins reports deployment failures
How failures appear in pipeline logs
Why cleanup logic is required
π οΈ Planned Fix Strategy
To resolve the expected failure, the deployment logic is designed to:
Detect existing containers
Stop them gracefully
Remove them before redeployment
Start a fresh container cleanly
This makes the deployment idempotent, meaning it can be executed multiple times without causing conflicts.
π Why Failure Planning Matters
Failure planning:
Reduces trial-and-error debugging
Improves confidence in pipeline behavior
Encourages better design decisions
Reflects real-world DevOps practices
Rather than reacting to failures randomly, this approach treats failures as a design consideration.
πΉ Phase 3 β Implementing Continuous Deployment in Jenkins
With the deployment strategy finalized, the Jenkins pipeline is extended to include a deployment stage.
The pipeline is defined using a Jenkinsfile stored in the GitHub repository, following the Pipeline as Code approach.

Jenkinsfile β Deployment Logic
The deployment stage performs the following steps:
Stops the existing container (if running)
Removes the container
Starts a new container using the latest Docker image
pipeline {
agent any
stages {
stage('Build Docker Image') {
steps {
sh 'docker build -t jenkins-cd-image .'
}
}
stage('Deploy Application') {
steps {
sh '''
docker stop jenkins-cd-app || true
docker rm jenkins-cd-app || true
docker run -d -p 8081:80 --name jenkins-cd-app jenkins-cd-image
'''
}
}
}
}
The use of || true ensures that cleanup commands do not fail the pipeline when no existing container is present.
πΉ Phase 4 β Executing the Deployment Pipeline
After committing the Jenkinsfile, the pipeline is executed from Jenkins.
During execution, Jenkins:
Pulls the Jenkinsfile from GitHub
Builds the Docker image
Executes the deployment stage
Starts the application container

πΉ Phase 5 β Deployment Verification
After successful pipeline execution, the deployment is verified by accessing the application via browser:
http://<EC2-PUBLIC-IP>:8081
If the page loads successfully, it confirms that:
Jenkins executed the deployment stage
The Docker container is running
The latest application version is live

πΉ Observations from the Deployment Process
During repeated pipeline executions, the following observations were made:
Deployments remain consistent across runs
Existing containers do not cause conflicts
Jenkins logs clearly show deployment actions
Failures, when introduced intentionally, are easy to diagnose
This confirms that the deployment logic is stable and predictable.
π Conclusion
This project demonstrates a practical implementation of Continuous Deployment using Jenkins and Docker on a single AWS EC2 instance.
By extending an existing CI pipeline with a carefully designed deployment stage, the project shows how validated code can be deployed automatically in a controlled and repeatable manner. The emphasis on deployment planning, failure awareness, and idempotent execution closely reflects real-world Jenkins CI/CD practices used for internal services and small-scale applications.