End-to-end automated deployment from source code to production on AWS
Code pushed to GitHub triggers a Jenkins pipeline that builds a Docker image, pushes it to Amazon ECR, and deploys it to a Kubernetes cluster running on AWS EC2. Every layer of the stack — from the EC2 instances themselves to the running pods — is provisioned or configured through code.
GitHub → Jenkins → Docker Build → Amazon ECR → Kubernetes (Minikube) → Running App
↓
Terraform → AWS Infrastructure (EC2, VPC, Security Groups)
↓
Ansible → Server Configuration (Java, Jenkins, Docker, Minikube)
┌───────────────────────────────────────────────────────────────────┐
│ DEVELOPER MACHINE │
│ git push → GitHub Repository │
└──────────────────────────────┬────────────────────────────────────┘
│ Webhook trigger
▼
┌───────────────────────────────────────────────────────────────────┐
│ AWS EC2 — JENKINS SERVER │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ Checkout │───▶│ Docker Build │───▶│ Push to Amazon ECR │ │
│ │ (GitHub) │ │ :v${BUILD} │ │ + kubectl deploy │ │
│ └──────────┘ └──────────────┘ └────────────────────────┘ │
└───────────────────────────────────────────────┬───────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ AWS EC2 — APP SERVER │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Kubernetes (Minikube) │ │
│ │ Deployment → ReplicaSet → Pod → Docker Container │ │
│ │ (NGINX · Static App) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ 🌐 NodePort Service → http://<EC2-PUBLIC-IP>:<PORT> │
└───────────────────────────────────────────────────────────────────┘
| Layer | Tool | Purpose |
|---|---|---|
| ☁️ Cloud | AWS EC2 | Compute infrastructure |
| 🏗️ IaC | Terraform | Provision EC2, VPC, security groups |
| ⚙️ Config | Ansible | Install and configure Jenkins, Docker, Minikube |
| 🔄 CI/CD | Jenkins | Pipeline orchestration (build → push → deploy) |
| 📦 Containers | Docker | Application packaging and versioning |
| 🗃️ Registry | Amazon ECR | Private Docker image storage |
| ☸️ Orchestration | Kubernetes | Pod scheduling, scaling, service exposure |
| 🌐 Web Server | NGINX | Serve static app inside the container |
| 🗂️ SCM | GitHub | Source control and pipeline trigger |
multi-ec2-cicd-devops-project/
├── terraform/
│ ├── main.tf # EC2 instances, VPC, security groups
│ ├── variables.tf # Input variables
│ └── outputs.tf # Public IPs, resource names
├── ansible/
│ ├── inventory.ini # EC2 host addresses
│ └── playbooks/ # Jenkins, Docker, Minikube setup
├── docker/
│ ├── Dockerfile # NGINX image definition
│ └── index.html # Static web application
├── k8s/
│ ├── deployment.yaml # Kubernetes Deployment
│ ├── service.yaml # NodePort Service
│ └── kubernetes-beginner-guide.html # K8s conceptual learning guide
├── jenkins/
├── Jenkinsfile # Declarative pipeline definition
├── ecr.tf # Amazon ECR repository
└── .gitignore
- AWS account with IAM credentials configured (
aws configure) - Terraform
>= 1.0 - Ansible
>= 2.9 - An SSH key pair for EC2 access
cd terraform/
terraform init
terraform plan
terraform applyCreates EC2 instances, VPC, subnets, and security groups on AWS.
cd ansible/
ansible-playbook -i inventory.ini playbooks/setup.ymlInstalls Java, Jenkins, Docker, and Minikube on the target instances.
Open Jenkins at http://<JENKINS-EC2-IP>:8080, configure the pipeline to point at this repository, then push a commit. Jenkins picks it up automatically.
minikube service webapp-service --urlOpen the returned URL in your browser.
pipeline {
agent any
stages {
stage('Checkout') {
// Pull latest code from GitHub
}
stage('Build Docker Image') {
// docker build -t $ECR_REPO:v${BUILD_NUMBER} .
}
stage('Push to Amazon ECR') {
// aws ecr get-login-password | docker login
// docker push $ECR_REPO:v${BUILD_NUMBER}
}
stage('Deploy to Kubernetes') {
// kubectl apply -f k8s/deployment.yaml
// kubectl apply -f k8s/service.yaml
}
}
}Each stage is independently logged. Failures are easy to isolate and retry.
This project goes hands-on with the full Kubernetes workload model on Minikube:
- Pods — smallest deployable unit, wrapping the Docker container
- Deployments — declarative updates, rollback support, desired-state management
- ReplicaSets — maintain the specified number of pod replicas automatically
- Services (NodePort) — expose pods to external traffic outside the cluster
- Minikube — single-node cluster for zero-cost development on EC2
A companion interactive guide (k8s/kubernetes-beginner-guide.html) covers cluster architecture, the control plane, worker nodes, and the full request lifecycle from DNS to pod.
Why Minikube instead of EKS?
Keeps costs at zero while covering the same Kubernetes API surface. The deployment.yaml and service.yaml manifests are portable to any standard cluster — upgrading to EKS later requires no changes to the K8s config.
Why Amazon ECR instead of Docker Hub? ECR integrates natively with AWS IAM. The EC2 instance role grants pull access automatically — no credentials need to be stored in Jenkins.
Why Ansible instead of EC2 user data scripts? Ansible playbooks are idempotent and rerunnable. If server configuration drifts, re-running the playbook restores the desired state without re-provisioning the instance.
Why a declarative Jenkinsfile? The pipeline definition lives in version control alongside the application code. Every change is tracked, reviewable, and rollback-able like any other file in the repo.
- Migrate from Minikube to EKS for production-grade scaling
- Add automated rollback on deployment failure
- Set up Prometheus + Grafana monitoring dashboards
- Implement multi-environment pipelines — dev / staging / prod
- Move secrets to Jenkins Credentials Manager (no plain-text keys)
- Enable Slack / email notifications on pipeline success or failure
Anshu Sharma — Cloud & DevOps Engineer
Building production-grade infrastructure and automation on AWS.