Author: Pooja Bhavani

Managing employee data efficiently is a challenge for growing organizations. This project is built on a Django-based Employee Management System (EMS) and deployed it on AWS using Terraform for Infrastructure as Code (IaC) and Jenkins for CI/CD automation.

Why This Matters

  • Faster, more reliable software delivery
  • Scalable infrastructure that grows with your needs
  • Reduced manual errors through automation

Overview

The EMS manages employee records, leave requests, and reports. It is packaged for containerized deployment using Docker and Docker Compose, served behind Nginx, and configured with a Jenkins CI/CD pipeline for automation. By default, it uses SQLite as the database, but this can be swapped with PostgreSQL for production environments. Additionally, Terraform is used to provision the AWS infrastructure, including VPC, subnets, EC2 instances, and security controls.

Prerequisites

  • Backend Framework: Django (Python)
  • Web Server: Nginx
  • Database: SQLite (local) can be swapped with PostgreSQL/RDS
  • Containerization: Docker & Docker Compose
  • Infrastructure: AWS (VPC, Subnets, EC2, Security Groups, IAM) via Terraform
  • CI/CD: Jenkins server with Docker installed on the agent(s) that build and push images and run Terraform.

Step 1: To run the app locally

  • Clone repo
# Clone repo
git clone https://github.com/On-cloud7/ems.git
cd ems

# Create virtual environment
python -m venv myenv
source myenv/bin/activate   # Linux/Mac
myenv\Scripts\activate      # Windows

# Install dependencies
pip install -r requirements.txt

# Run migrations
python manage.py migrate

# Start development server
python manage.py runserver

Step 2: Containerize (Docker & Docker Compose)

Write a Dockerfile and docker-compose.yml file (Try it your self)

Build image locally

#Base image
FROM python:3.13-alpine 

# Set working directory
WORKDIR /app

# Copy project files
COPY  . . 

# Copy requirements 
COPY requirements.txt .

#install dependencies
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
RUN python3 manage.py makemigrations
RUN python3 manage.py migrate


#Expose Django port
EXPOSE 8000  

# Run Django server
ENTRYPOINT [ "python3" ]

CMD [ "manage.py" , "runserver" ,"0.0.0.0:8000" ]

Allow port 8000 in security group of the Instance

Step 3 — Create your AWS infrastructure with Terraform

Create a secure VPC, public & private subnets, IGW, NAT (for private outbound), security groups, and an EC2 to host your containers (or a fleet behind an ELB).

Create this infra using terraform write terraform files and run the below commands (Try it your self to make your terraform skills stronger)

terraform init
terraform plan
terraform apply

Step 4: Deploy on EC2

On EC2, install Docker & Docker Compose (via user data or Ansible), then run:

EC2 with Docker Compose

  • Use EC2 user-data script that installs Docker & Docker Compose, and pulls your image from a registry (Docker Hub / ECR), and runs docker-compose up -d.
  • Ensure the EC2’s security group allows SSH (for admin) and app ports (e.g., 80/443).

B) Use managed services (recommended for production)

  • ECR for images + ECS Fargate or EKS for containers + ALB for load balancing and auto-scaling. These require more Terraform code but reduce maintenance.

Step 5: Automate with Jenkins CI/CD

To make CI/CD workflow smooth, we add Jenkins.

Jenkins Pipeline (Jenkinsfile):

  1. Checkout Code from GitHub
  2. Build Docker Image
  3. Run Tests
  4. Push to Docker Hub / ECR
  5. Terraform Apply to provision infra
  6. Deploy by restarting Docker containers on EC2

Jenkinsfile

@Library("book") _
pipeline{
    agent{label "slave2"}
    stages{
        stage("code clone"){
            steps{
                echo "cloning the code "
                git url: "https://github.com/On-cloud7/ems.git", branch: "main"
                echo "code cloned"
            }
        }

        stage("installing tools "){
            steps{
                echo "installing docker "
                sh """ 
                curl -LO https://raw.githubusercontent.com/addico786/tools_download_scripts/master/docker.sh
                sudo chmod +x docker.sh
                ./docker.sh

                """
                sh """ 
                curl -LO https://raw.githubusercontent.com/addico786/tools_download_scripts/master/trivy.sh
                sudo chmod +x trivy.sh
                ./trivy.sh
                """
                echo "trivy installed"
                echo "docker installed "
            }
        }
        stage("building images"){
            steps{
                echo "building docker images"
                sh " docker rmi -f app:latest || true "
                docker_build("app","latest",".")
                echo "docker built image"
            }
        }
        stage("image scan"){
            steps{
                echo "scanning image"
                sh "trivy image app:latest"
                echo "scan complete "
            }
        }
        stage("network_create"){
            steps{
                echo "network create"
                sh "docker network disconnect django app_cont || true "
                sh "docker network disconnect django nginx_cont || true"
                sh "docker network rm django || true "
                docker_network("django")
            }
        }
        stage("container create"){
            steps{
                echo "creating the container"
                sh "docker rm -f app_cont || true "
                docker_run("8000:8000","app_cont","django","app:latest")
            }
        }

        stage("nginx config"){
            steps{
                echo "building the code "
                sh "docker rm -f nginx_cont || true"
                docker_build("nginx","latest","./nginx")
                docker_run("80:80","nginx_cont","django","nginx:latest")
                echo "running the container "
            }
        }
    }
}

Secrets and security

  • Use Jenkins Credentials plugin to store AWS keys, Docker registry creds, SSH private keys.

Step 6 — Application concerns & production hardening

  • DB: Switch SQLite to PostgreSQL (RDS or managed DB) for multi-instance reliability.
  • Static files: Serve via S3 + CloudFront or ensure Nginx correctly serves collectstatic output.
  • Secrets: Do not store secrets in Git. Use AWS Secrets Manager / Parameter Store or Jenkins credentials instead of code.
  • Scaling & HA: Add ELB + Auto Scaling across multiple AZs for high availability.
  • Monitoring & logging: Use CloudWatch (or Prometheus/Grafana) and centralize logs (CloudWatch Logs or ELK) for monitoring and log aggregation.
  • Backup & DR: Automate DB snapshots and S3 backups.

Test Your Understanding

Let’s verify your grasp of the concepts we’ve covered. Take your time with these questions!

  1. Which Terraform command refreshes the state to match real resources?
  • A) terraform validate
  • B) terraform refresh
  • C) terraform init
  • D) terraform fmt
  1. Best practice for storing Terraform state in a team?
  • A) Local file on laptops
  • B) Remote backend with locking
  • C) Commit to GitHub
  • D) Email the file
  1. Which AWS component allows private subnets to access the internet?
  • A) NAT Gateway
  • B) Internet Gateway
  • C) Route Table
  • D) VPC Peering
  1. If terraform apply is interrupted midway:
  • A) Auto rollback
  • B) Partial resources may remain
  • C) Infrastructure deleted
  • D) Nothing is created
  1. Jenkins pipelines in a Jenkinsfile must be:
  • A) Stored in SCM
  • B) Configured only in UI
  • C) YAML only
  • D) Run on master only
  1. Safest way to manage secrets in Jenkins?
  • A) Hardcode in Jenkinsfile
  • B) Use Credentials plugin
  • C) Plain env vars
  • D) Upload to GitHub
  1. Terraform feature for many similar resources:
  • A) Modules
  • B) count / for_each
  • C) Variables
  • D) Locals
  1. High-availability design for a web app:
  • A) EC2 in one AZ
  • B) EC2 + ELB + Multi-AZ
  • C) Route Table + NAT
  • D) Only Internet Gateway
  1. Command to create/update infrastructure from the plan:
  • A) terraform init
  • B) terraform plan
  • C) terraform apply
  • D) terraform destroy
  1. Why use Jenkins agents?
  • A) Isolate jobs & scale horizontally
  • B) Store Terraform state
  • C) Needed for freestyle jobs
  • D) Master cannot execute

Good luck, try it out

Categorized in:

Blog,