Not just a project — 6+ months of real hands-on DevOps practice covering containerization, orchestration, networking, CI/CD, and production-grade patterns.
Each service lives in its own isolated repository. There's also a dedicated repo for reusable CD workflows and one for Terraform.
GitHub Organization
│
├── auth-service/ # github.com/Yousefa7medmaher/auth-service
│ ├── .github/workflows/
│ │ └── CI.yaml # Build & push to ECR + triggers CD
│ ├── k8s/ # K8s manifests for this service
│ ├── src/
│ ├── config/
│ ├── controllers/
│ ├── models/
│ ├── utils/
│ ├── Dockerfile
│ └── docker-compose.yaml
│
├── products-service/ # github.com/Yousefa7medmaher/products-service
│ ├── .github/workflows/
│ │ └── CI.yaml
│ ├── k8s/
│ ├── src/ controllers/ models/ routes/ services/ test/
│ ├── Dockerfile
│ └── docker-compose.yml
│
├── cart-service/ # github.com/Yousefa7medmaher/cart-service
│ ├── .github/workflows/
│ │ └── CI.yaml
│ ├── k8s/
│ ├── src/
│ └── Dockerfile
│
├── order-service/ # github.com/Yousefa7medmaher/order-service
│ ├── .github/workflows/
│ │ └── CI.yaml
│ ├── k8s/
│ ├── src/
│ └── Dockerfile
│
├── reusable-workflows/ # Shared CD logic — called by all services
│ └── .github/workflows/
│ └── cd-deploy-eks.yml
│
└── terraform/ # EKS cluster provisioning
Each service uses a multistage Dockerfile to keep production images lean and secure.
| Practice | Reason |
|---|---|
node:20-alpine base image |
~5MB vs ~900MB — faster push/pull |
Copy package.json first |
Docker layer cache skips npm install if deps unchanged |
| Multistage build | Final image has no dev dependencies or source code |
Non-root user appuser |
Limits blast radius if container is compromised |
npm ci over npm install |
Deterministic installs via package-lock.json |
Every push to main in any service repo triggers:
Lint & Test → Build → Push to Amazon ECR
- Docker layer caching via GitHub Actions cache (
type=gha) - Images tagged with the commit SHA
- Pushes to a private ECR repository for that service
- After a successful push, the CI calls the shared CD workflow
All services share a single reusable deploy workflow via workflow_call. Each service's CI calls it like this:
# Inside each service's CI.yaml — after the build/push job
uses: Yousefa7medmaher/reusable-workflows/.github/workflows/cd-deploy-eks.yml@main
with:
image_tag: ${{ github.sha }}
environment: production
cluster_name: my-eks-cluster
namespace: micro-app
service_name: auth-service
repo_name: auth-service
secrets: inheritWhat the CD workflow does:
- Configures AWS credentials
- Installs
kubectland updates kubeconfig viaaws eks update-kubeconfig - Replaces
IMAGE_PLACEHOLDERin k8s manifests with the real ECR image URI - Applies manifests with
kubectl apply -f k8s/ -n $NAMESPACE - Verifies rollout with
kubectl rollout status
The EKS cluster is provisioned with Terraform. Kept minimal for free-tier usage:
- Minimal node count and instance sizes to stay within free-tier limits
- Resource requests and limits set on all pods to avoid over-scheduling
- EBS CSI driver enabled for persistent volumes (MongoDB)
- Metrics server deployed for resource visibility
Allocatable resources per node:
Pod CPU & memory usage:
| Topic | Details |
|---|---|
| Deployments | Replicas, rolling updates, resource limits |
| Health Probes | /health (liveness) + /ready (readiness) on every service |
| ConfigMaps & Secrets | Externalized config, base64-encoded secrets |
| Services | ClusterIP (internal) · LoadBalancer (external/production) |
| StatefulSet | MongoDB with persistent volume claims |
| Ingress | Manifest ready, not yet applied in current deployment |
Every service exposes /health and /ready. Verified via port-forward locally and via the LoadBalancer externally.
Port-forward test (local):
LoadBalancer test (production):
Using type: LoadBalancer instead of Ingress for now (Ingress manifest is ready but not yet applied). The auth-svc LoadBalancer is provisioned on AWS ELB automatically.
Service + endpoints verification:
External Traffic
│
▼
┌──────────────────┐
│ AWS LoadBalancer │ ← ELB provisioned per service (production)
└────────┬─────────┘
│
▼
┌────────────────────────────────────────┐
│ EKS Cluster (micro-app ns) │
│ auth-service (x4 pods) │
│ └──ClusterIP──► mongo-auth │
└────────────────────────────────────────┘
Ingress + path-based routing is the next step — manifest already exists.
docker compose up --build| Service | Local Port |
|---|---|
| auth-service | 3001 |
| cart-service | 3002 |
| order-service | 3003 |
| products-service | 3004 |
| MongoDB | 27017 |
| Service | Port | ECR Repo |
|---|---|---|
auth-service |
5000 | <account>.dkr.ecr.us-east-1.amazonaws.com/auth-service |
cart-service |
3002 | <account>.dkr.ecr.us-east-1.amazonaws.com/cart-service |
order-service |
3003 | <account>.dkr.ecr.us-east-1.amazonaws.com/order-service |
products-service |
3004 | <account>.dkr.ecr.us-east-1.amazonaws.com/products-service |
- Deploy to AWS EKS
- Provision cluster with Terraform
- Ingress controller with path-based routing
- Multi-environment support (staging / prod)
- GitHub Actions — CI push to ECR
- GitHub Actions — CD deploy to EKS (reusable workflow)
- Migrate to Jenkins self-hosted
- HashiCorp Vault integration
- Replace K8s Secrets with Vault-backed secrets
- Prometheus + Grafana
- Horizontal Pod Autoscaler (HPA)
Joe Ahmed — DevOps enthusiast documenting a real 6-month learning journey.
"This repo isn't just a project — it's a living record of daily practice, real mistakes, and genuine growth in the DevOps field."







