Skip to main content

Command Palette

Search for a command to run...

Hands-on Jenkins CD Project (Intermediate) – Deploying Docker Containers on AWS EC2

Building on Jenkins CD to Implement Continuous Deployment Using Docker

Published
β€’6 min read

πŸ“˜ 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.

More from this blog

codeops-labs.hashnode.dev

16 posts