AWS CI/CD Pipeline Project: GitHub, Jenkins, Docker and EC2 Deployment

A CI/CD pipeline turns source-code changes into repeatable build, test and deployment actions. For beginners, the most useful way to understand CI/CD is to create a small application, store it in GitHub, package it with Docker, automate the workflow with Jenkins and run the container on an Amazon EC2 server. This AWS CI/CD project explains the architecture, implementation steps, security choices and troubleshooting process in a form that can be presented during an interview.

The goal is not to claim that one small pipeline is production-ready. The goal is to learn the responsibilities of each tool and remove unpredictable manual steps. GitHub stores and versions the code, Jenkins orchestrates the stages, Docker creates a consistent application image, a container registry stores the image, and EC2 provides the runtime environment.

What will the CI/CD project build?

The pipeline follows this logical flow:

  1. A developer updates a simple web application and pushes the change to GitHub.
  2. GitHub notifies Jenkins through a webhook, or Jenkins polls the repository in a learning setup.
  3. Jenkins checks out the selected branch.
  4. The pipeline performs a syntax check or automated test.
  5. Docker builds an immutable image from the reviewed Dockerfile.
  6. The pipeline assigns a traceable image tag and pushes the image to an approved registry.
  7. The EC2 deployment host pulls the new image and starts the container.
  8. A health check verifies that the application responds.

Each stage should fail clearly if its task is unsuccessful. A pipeline that continues after failed tests or a failed image build creates false confidence.

Tools and their responsibilities

GitHub for source control

The Git repository should contain application code, dependency information, a Dockerfile, automated tests and a Jenkinsfile. The Jenkinsfile makes the pipeline part of the codebase, so changes to the delivery process can be reviewed like application changes. Do not commit passwords, private keys, access tokens or cloud credentials.

Jenkins for orchestration

Jenkins reads the pipeline definition and coordinates checkout, test, build, push, deploy and verification stages. A Jenkins controller should not be treated as an unrestricted general-purpose server. Keep it updated, restrict network access, limit plugins and separate build agents when the environment grows.

Docker for repeatable packaging

A Docker image packages the application and its runtime dependencies. The same reviewed image should move through environments rather than being rebuilt differently on every server. Use a small trusted base image, pin important versions where appropriate, run as a non-root user when possible and avoid placing secrets in image layers.

Amazon EC2 for the runtime

An EC2 instance can host Jenkins, run the application container, or serve as a deployment target. For a beginner lab, the architecture may use one Jenkins server and one application server. In a stronger design, the controller, build agents and application workloads are separated so that build activity cannot easily affect the production service.

Plan the repository before creating the pipeline

Create a small application with one health endpoint or predictable home page. Organize the repository so another learner can understand it quickly. A simple structure can contain the application file, a dependency file, a test directory, a Dockerfile, a Jenkinsfile and a README.

The README should describe how to run the application locally, how to build the image, which port is exposed, how the health check works, what environment variables are required and how to clean up resources. Good documentation is part of DevOps work because it reduces hidden knowledge.

Step 1: Prepare the EC2 servers

Launch the required Ubuntu EC2 instances in the selected Region and VPC. Use security groups with narrowly defined inbound rules. Allow SSH only from a trusted source when SSH is required. Expose the application port only to the intended users or load balancer. The Jenkins web interface should not be left open to the entire internet without appropriate protection.

Attach IAM roles to EC2 instances when an AWS service needs access. Do not copy long-term AWS access keys into Jenkins jobs or shell scripts. For administrative access, consider AWS Systems Manager Session Manager when the account and instance are configured for it.

Install the required runtime components using a documented process. If Jenkins runs in a container, understand how it reaches a Docker daemon or build system. Mounting a host Docker socket gives powerful host-level access and must be treated as a security-sensitive design, not as a harmless shortcut.

Step 2: Create a safe Dockerfile

Start from an appropriate official or trusted base image. Copy the dependency file first when that improves build caching, install dependencies, copy the application code and define the startup command. Add a .dockerignore file to exclude the Git directory, local environments, build output, secrets and unnecessary files.

Use a specific working directory and expose the application port as documentation. Where the application supports it, create a non-root user and run the process with that identity. Keep the image focused on one application responsibility. After building locally or on the lab server, run the container and test the endpoint before automating it.

Step 3: Configure Jenkins access

Install the minimum Jenkins plugins needed for the chosen pipeline. Connect the repository using a narrowly scoped credential or GitHub App where available. Store registry credentials in the Jenkins credentials store, reference them by credential ID and mask sensitive output. Do not print tokens in the console log.

If Jenkins must deploy to another instance, choose a controlled access method. A common lab uses SSH credentials, but a stronger AWS pattern can use Systems Manager and an instance IAM role. Whichever method you choose, document who can invoke it, which host it can reach and which commands it can run.

Step 4: Define Jenkinsfile stages

A clear declarative pipeline might contain the following stages:

  • Checkout: retrieve the expected repository and branch.
  • Validate: inspect required files and perform a syntax check.
  • Test: run unit or integration tests and stop on failure.
  • Build: build the Docker image from the committed Dockerfile.
  • Scan: run an available image or dependency security check.
  • Push: authenticate to the registry and push a unique tag.
  • Deploy: pull that exact tag on the target and start the container.
  • Verify: request the health endpoint and fail if it is unhealthy.

Tagging only with latest makes rollback and auditing difficult. Add a unique identifier such as the Jenkins build number or a shortened Git commit hash. You may also update a convenience tag, but deployment records should identify the exact immutable version.

Step 5: Connect GitHub to Jenkins

Configure the Jenkins job to use the Jenkinsfile from source control. Add a webhook in GitHub that targets the Jenkins webhook endpoint, then restrict and protect the endpoint appropriately. Push a harmless README change and confirm that the expected job starts once.

If the webhook does not work, review GitHub delivery history, the response status, Jenkins logs, job configuration, network rules and the selected branch. Avoid solving the problem by opening every port to every address. Fix the specific connectivity or authentication issue.

Step 6: Deploy the Docker container to EC2

The deployment stage should pull the exact image tag, stop and remove the old application container in a controlled way, start the new container with the required configuration and verify the health endpoint. Keep secrets outside the image and out of console output. If environment variables contain sensitive data, obtain them from an approved secret-management system.

A simple lab may have a short interruption while replacing the container. A production design needs a safer strategy such as a load balancer, multiple targets, rolling or blue-green deployment, health checks and a defined rollback process. State this limitation honestly in your portfolio.

Step 7: Add rollback and deployment evidence

Keep the previous known-good image tag. If the health check fails, the pipeline can stop and report the failure. A later version can automate rollback to the previous tag, but test that behavior carefully. Automated rollback is only valuable when it returns the service to a verified state.

Capture the commit identifier, Jenkins build number, test result, image tag, registry digest, deployment time and health-check result. These details create traceability from source code to the running container. They also make troubleshooting much faster than guessing which version is live.

Security practices for the project

  • Protect the main branch and require review for pipeline changes.
  • Use least-privilege GitHub, registry and AWS permissions.
  • Keep credentials in approved credential stores and rotate them when necessary.
  • Restrict Jenkins and SSH access through security groups and network design.
  • Do not run untrusted pull-request code on an agent that holds production credentials.
  • Scan dependencies and images, then define how serious findings block a release.
  • Patch Jenkins, plugins, operating systems and container base images.
  • Send logs and metrics to a monitoring system instead of depending only on the terminal.

Monitoring the deployed application

A deployment is not complete when the container starts. Monitor the EC2 instance, application endpoint and container logs. Create alarms for meaningful infrastructure or service conditions and notify the correct person. The companion AWS EC2 monitoring and automatic alerts project shows how CloudWatch and SNS can provide this operational feedback.

For stronger isolation, keep public entry points and private workloads in the correct network layers. The AWS VPC project using public and private subnets explains route tables, internet access and security-group relationships for a web-and-database architecture.

Common CI/CD problems and fixes

  • Repository checkout fails: verify the repository URL, branch, credential scope and GitHub access.
  • Docker permission denied: review how the Jenkins agent accesses the build engine; do not apply broad permissions without understanding the risk.
  • Image push fails: check registry authentication, repository name, tag and network access.
  • Container exits immediately: inspect container logs, startup command, application binding address and required environment variables.
  • Application is unreachable: confirm port mapping, security groups, route tables, local firewall and service health.
  • Webhook does not trigger: inspect delivery history and verify that Jenkins can receive and validate the request.
  • Wrong version deployed: use unique immutable tags and record the tag used by the deployment stage.

How to present the project in an interview

Explain the problem first: manual deployments were slow and inconsistent. Then describe the event flow from a GitHub change to Jenkins, testing, Docker image creation, registry push, EC2 deployment and health verification. Explain how a failed test prevents deployment and how a unique image tag supports rollback.

Add one challenge you solved. For example, a container may have worked locally but failed on EC2 because the application listened only on localhost. Explain how logs and network checks identified the problem. Interviewers usually learn more from a clear troubleshooting story than from a long list of tools.

Who can learn this project?

This project is suitable for AWS beginners, Linux learners, developers moving into deployment work, system administrators and DevOps freshers. Basic Git, Linux command-line, networking and Docker knowledge helps. If those foundations are new, learn them in small steps before trying to automate the complete workflow.

Students seeking a guided path across cloud services, deployment, monitoring and interview projects can review AWS training in Vizag from Softenant Technologies.

Frequently asked questions

Can Jenkins and the application run on one EC2 instance?

They can for a temporary learning lab, but the services compete for resources and share risk. Separate the controller, build agents and application workloads for a more realistic design.

Should the pipeline use the latest Docker tag?

Use a unique build or commit-based tag for traceability and rollback. A convenience latest tag can exist, but it should not be the only way to identify a release.

Should AWS keys be stored in GitHub?

No. Do not commit cloud keys to a repository. Prefer IAM roles and short-lived credentials, and use approved secret stores for sensitive values.

What proves that CI/CD is working?

A successful project shows a traceable commit, passing tests, a built and pushed image, a controlled deployment, a health-check result and logs that make failures understandable.

Conclusion

This AWS CI/CD pipeline project connects development and cloud operations in one repeatable workflow. By combining GitHub, Jenkins, Docker and EC2, a beginner learns source control, pipeline-as-code, image versioning, credential handling, deployment checks and rollback thinking. The strongest result is not merely an automated demo; it is a documented process that is secure enough for its purpose, fails clearly and can be explained confidently.

Leave a Comment

Your email address will not be published. Required fields are marked *