WHAT WE ARE BUILDING
You will write a simple website (HTML + CSS + JS), put it inside Docker, upload it to Docker Hub, host it on AWS, and set up automatic deployment so every time you change your code and push to GitHub, your website updates itself on AWS automatically.
TOOLS WE NEED
Before starting anything, install these tools on your Windows 11 machine.
1. VS Code (Code Editor) Go to https://code.visualstudio.com → Download for Windows → Install it. This is where you write your code.
2. Git Go to https://git-scm.com/download/win → Download → Install with all default options. This is needed even if you use GitHub Desktop.
3. GitHub Desktop (GUI Option) Go to https://desktop.github.com → Download → Install → Sign in with your GitHub account. This lets you push code without using any commands.
4. Docker Desktop Go to https://www.docker.com/products/docker-desktop → Download for Windows → Install. During installation make sure "Use WSL 2 instead of Hyper-V" is checked. After install, restart your PC. Open Docker Desktop and sign in with your Docker Hub account (create one at https://hub.docker.com if you don't have one).
5. Accounts You Need
- GitHub account: https://github.com
- Docker Hub account: https://hub.docker.com
- AWS account: https://aws.amazon.com
PART 1 — CREATE YOUR PROJECT
Step 1: Create the Project Folder
Open File Explorer on your Windows 11. Go to C drive, then your Users folder, then your username folder. Create a new folder and name it "my-website". The full path will be something like C:\Users\YourName\my-website.
Step 2: Open the Folder in VS Code
Two ways to do this:
GUI Way: Right-click on the my-website folder → you will see "Open with Code" in the menu → click it.
CLI Way: Open the folder in File Explorer, then click on the address bar at the top, type "cmd" and press Enter. A black command prompt window opens. Then type:
code .
This opens VS Code in that folder.
Step 3: Create the Folder Structure
In VS Code, on the left side you see the Explorer panel. You will create folders and files there.
To create a folder: Click the "New Folder" icon (it looks like a folder with a plus sign) in the Explorer panel. To create a file: Click the "New File" icon (it looks like a paper with a plus sign).
Create this exact structure:
my-website/
├── src/
│ ├── index.html
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── main.js
├── .github/
│ └── workflows/
│ └── cicd.yml
├── Dockerfile
├── nginx.conf
└── .dockerignore
How to create it step by step:
- Click New Folder icon → type "src" → press Enter
- Click on the src folder to select it → click New Folder icon → type "css" → press Enter
- Click on the src folder again → click New Folder icon → type "js" → press Enter
- Click New Folder icon at root level → type ".github" → press Enter
- Click on .github folder → click New Folder icon → type "workflows" → press Enter
- Now create files: click on src folder → New File → "index.html"
- Click on css folder → New File → "style.css"
- Click on js folder → New File → "main.js"
- Click on workflows folder → New File → "cicd.yml"
- Click on root (my-website) level → New File → "Dockerfile" (no extension, exactly this)
- New File → "nginx.conf"
- New File → ".dockerignore"
PART 2 — WRITE THE CODE
Step 4: Write index.html
Click on index.html in VS Code and paste this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>My Website</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="container">
<h1>Hello World!</h1>
<p id="message">My website is live on AWS with Docker and CI/CD!</p>
<button id="btn">Click Me</button>
</div>
<script src="js/main.js"></script>
</body>
</html>
Notice that the CSS file is linked using href="css/style.css" and the JS file is linked at the bottom using src="js/main.js". This is how separate files connect to HTML.
Step 5: Write style.css
Click on style.css and paste this:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: white;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
text-align: center;
padding: 40px;
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 16px;
color: #e94560;
}
p {
font-size: 1.1rem;
margin-bottom: 24px;
color: #a8b2d8;
}
button {
padding: 12px 32px;
background: #e94560;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
}
button:hover {
background: #c73652;
}
Step 6: Write main.js
Click on main.js and paste this:
document.getElementById('btn').addEventListener('click', function() {
document.getElementById('message').textContent = 'JavaScript is working! CI/CD pipeline deployed this.';
document.getElementById('btn').textContent = 'Clicked!';
document.getElementById('btn').style.background = '#2ecc71';
});
Step 7: Write nginx.conf
Nginx is a web server that will serve your HTML files inside Docker. Click on nginx.conf and paste this:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
Step 8: Write Dockerfile
The Dockerfile tells Docker how to package your website. Click on Dockerfile and paste this:
# Use official nginx image based on Alpine Linux (small and fast)
FROM nginx:alpine
# Remove the default nginx config file
RUN rm /etc/nginx/conf.d/default.conf
# Copy our custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy our website files into the nginx web root folder
COPY src/ /usr/share/nginx/html/
# Tell Docker this container uses port 80
EXPOSE 80
# Start nginx when container runs
CMD ["nginx", "-g", "daemon off;"]
Step 9: Write .dockerignore
This tells Docker to ignore certain files when building. Click on .dockerignore and paste this:
.git
.github
*.md
PART 3 — TEST LOCALLY WITH DOCKER
Step 10: Open Terminal in VS Code
In VS Code, press Ctrl + ` (backtick key, below Escape key). This opens a terminal at the bottom.
Make sure Docker Desktop is running (you should see the Docker whale icon in your taskbar).
Step 11: Build the Docker Image
In the terminal, type this command and press Enter:
docker build -t my-website:latest .
What this means: "docker build" means build an image, "-t my-website:latest" means name it "my-website" with tag "latest", and the dot "." means use the current folder.
You will see Docker downloading things and running steps. Wait for it to finish. You will see "Successfully built" at the end.
GUI Way to verify: Open Docker Desktop → click "Images" on the left sidebar → you will see "my-website" listed there.
Step 12: Run the Container Locally
docker run -d -p 8080:80 --name my-website-test my-website:latest
What this means: "-d" means run in background, "-p 8080:80" means map your computer's port 8080 to the container's port 80, "--name my-website-test" gives the container a name.
Now open your browser and go to: http://localhost:8080
You should see your website! If it works, your Docker setup is correct.
To stop the container:
CLI Way:
docker stop my-website-test
docker rm my-website-test
GUI Way: Open Docker Desktop → click "Containers" on the left → you will see "my-website-test" → click the Stop button (square icon) → then click the Delete button (trash icon).
PART 4 — PUSH TO GITHUB
Step 13: Initialize Git and Push to GitHub
GUI Way using GitHub Desktop:
- Open GitHub Desktop
- Click "Add an Existing Repository from your Hard Drive"
- Click "Choose" and browse to your my-website folder → click "Select Folder"
- It will say "This directory does not appear to be a Git repository" → click "create a repository" link
- Name: my-website
- Local path: already set to your folder
- Click "Create Repository"
- Now click "Publish repository" button at the top
- Uncheck "Keep this code private" if you want it public (you can keep private too)
- Click "Publish Repository"
- Your code is now on GitHub. You can verify by going to https://github.com/yourusername/my-website
CLI Way:
Open terminal in VS Code and run these commands one by one:
git init
git add .
git commit -m "Initial commit: add website and Docker files"
Now go to https://github.com, log in, click the "+" button at top right, click "New repository", name it "my-website", leave other settings default, click "Create repository".
GitHub will show you commands. Copy and run them. They look like this:
git remote add origin https://github.com/yourusername/my-website.git
git branch -M main
git push -u origin main
Replace "yourusername" with your actual GitHub username.
PART 5 — DOCKER HUB SETUP
Step 14: Create a Docker Hub Repository
- Go to https://hub.docker.com and log in
- Click the "Create Repository" button (blue button)
- Repository name: my-website
- Visibility: Public
- Click "Create"
Your Docker Hub repo address will be: hub.docker.com/r/yourusername/my-website
Step 15: Create a Docker Hub Access Token
This token is like a password that GitHub Actions will use to push images to Docker Hub. You never share your actual password with GitHub.
- On Docker Hub, click your profile photo (top right) → "Account Settings"
- Click "Security" in the left menu
- Click "New Access Token"
- Name it: github-actions
- Access permissions: Read, Write, Delete
- Click "Generate"
- A token will appear on screen — COPY IT NOW and save it in Notepad. You will never see this again after closing the window.
Step 16: Push Your Image to Docker Hub (Test)
In VS Code terminal:
docker tag my-website:latest yourusername/my-website:latest
docker push yourusername/my-website:latest
Before pushing, you may need to login in the terminal:
docker login
Enter your Docker Hub username and password.
GUI Way to push: Actually for pushing from your local machine, the terminal commands above are the simplest. Docker Desktop does not have a direct "push" button, so just use the 2 commands above.
After pushing, go to https://hub.docker.com/r/yourusername/my-website and you should see your image listed there with the "latest" tag.
PART 6 — AWS SETUP
Step 17: Create AWS Account
Go to https://aws.amazon.com and click "Create an AWS Account". Fill in your email, password, and account name. You will need a credit or debit card but will not be charged as long as you use free tier resources. Complete phone verification and sign in to the AWS Console at https://console.aws.amazon.com.
Step 18: Create an EC2 Instance (Your Server on AWS)
EC2 is basically a computer that runs 24/7 in the cloud. This is where your website will live.
GUI Way (AWS Console):
-
In the AWS Console, look at the top search bar. Type "EC2" and click on EC2 from the results.
-
You will see the EC2 Dashboard. Click the orange "Launch Instance" button.
-
On the "Launch an instance" page, fill in these settings:
Under "Name and tags":
- Name: my-website-server
Under "Application and OS Images":
- Click on "Amazon Linux" (it should already be selected)
- Make sure "Amazon Linux 2023 AMI" is selected
- You should see "Free tier eligible" next to it
Under "Instance type":
- Select "t2.micro"
- You should see "Free tier eligible" next to it
Under "Key pair (login)":
- Click "Create new key pair"
- Key pair name: my-website-key
- Key pair type: RSA
- Private key file format: .pem
- Click "Create key pair"
- A file called "my-website-key.pem" will automatically download to your Downloads folder
- MOVE THIS FILE to C:\Users\YourName.ssh\ folder. Create the .ssh folder if it doesn't exist. This file is very important — it's how you access your server. Do not lose it.
Under "Network settings":
- Click the "Edit" button on the right side
- You will see one rule for SSH (port 22). In the "Source type" dropdown for this rule, select "My IP". This means only your computer can SSH into the server.
- Click "Add security group rule" to add a second rule:
- Type: HTTP
- Protocol: TCP
- Port range: 80
- Source type: Anywhere (0.0.0.0/0)
- This allows everyone to access your website on port 80
- Click "Add security group rule" again for a third rule:
- Type: HTTPS
- Protocol: TCP
- Port range: 443
- Source type: Anywhere (0.0.0.0/0)
Under "Configure storage":
- Keep default 8 GB
-
Click the orange "Launch instance" button on the right side summary panel.
-
Click "View all instances".
-
You will see your instance. Wait about 1-2 minutes until the "Instance state" column shows "Running" with a green dot. Refresh the page if needed.
-
Click on your instance name to open its details. Look for "Public IPv4 address" on the right side. Write down this IP address. It looks something like 13.233.45.67. This is your server's address.
Step 19: Connect to Your EC2 Server and Install Docker
You need to go inside your server and install Docker on it.
GUI Way (using EC2 Instance Connect in browser):
- In AWS Console, go to EC2 → Instances → click on your instance
- Click the "Connect" button at the top
- Click on the "EC2 Instance Connect" tab
- Username should say "ec2-user" — leave it as is
- Click the orange "Connect" button
- A browser terminal window opens. You are now inside your server!
CLI Way (using terminal on Windows):
Open VS Code terminal and run:
ssh -i C:\Users\YourName\.ssh\my-website-key.pem ec2-user@YOUR_EC2_PUBLIC_IP
Replace YOUR_EC2_PUBLIC_IP with the IP you noted in step 18.
If you get a permission error on Windows, you need to fix the .pem file permissions. Right-click the .pem file → Properties → Security → Advanced → Disable inheritance → Remove all inherited permissions → Add → Select a principal → type your Windows username → Full control → OK.
Now whether you used GUI or CLI, you are in the server terminal. Run these commands:
sudo yum update -y
Wait for it to finish. This updates the server's software.
sudo yum install docker -y
This installs Docker on the server.
sudo systemctl start docker
This starts Docker.
sudo systemctl enable docker
This makes Docker start automatically if the server restarts.
sudo usermod -aG docker ec2-user
This allows ec2-user to run Docker commands without typing "sudo" each time.
docker --version
This confirms Docker is installed. You should see a version number.
You can close this terminal window. The server is ready.
PART 7 — ADD SECRETS TO GITHUB
Step 20: Add Repository Secrets
GitHub Actions (your CI/CD pipeline) needs some private information like your Docker Hub token and AWS server details. You store these as "secrets" in GitHub so they are not visible in your code.
- Go to https://github.com/yourusername/my-website
- Click the "Settings" tab (it's in the top navigation of your repo, not your account settings)
- In the left sidebar, scroll down and click "Secrets and variables"
- Click "Actions" under it
- You will see a "Repository secrets" section
- Click the "New repository secret" button
Add these 5 secrets one by one:
Secret 1:
- Name: DOCKERHUB_USERNAME
- Secret: your Docker Hub username (just the username, nothing else)
- Click "Add secret"
Secret 2:
- Name: DOCKERHUB_TOKEN
- Secret: paste the token you copied and saved in Notepad from Step 15
- Click "Add secret"
Secret 3:
- Name: EC2_HOST
- Secret: your EC2 public IP address (e.g., 13.233.45.67)
- Click "Add secret"
Secret 4:
- Name: EC2_USERNAME
- Secret: ec2-user
- Click "Add secret"
Secret 5:
- Name: EC2_SSH_KEY
- Secret: you need to paste the entire content of your .pem file here
- To get the content: go to VS Code → File → Open File → browse to C:\Users\YourName.ssh\my-website-key.pem → open it → press Ctrl+A to select all → Ctrl+C to copy
- Paste it as the secret value
- Click "Add secret"
You should now see 5 secrets listed. These are encrypted and nobody can see them except GitHub Actions when it runs.
PART 8 — CREATE THE CI/CD PIPELINE
Step 21: Write the GitHub Actions Workflow
Click on cicd.yml in VS Code (it's in .github/workflows/ folder) and paste this:
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-push:
name: Build and Push Docker Image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Step 1 - Checkout the code
uses: actions/checkout@v4
- name: Step 2 - Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Step 3 - Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Step 4 - Build the Docker image and push it to Docker Hub
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/my-website:latest
${{ secrets.DOCKERHUB_USERNAME }}/my-website:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
name: Deploy to AWS EC2
runs-on: ubuntu-latest
needs: build-and-push
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Step 5 - SSH into EC2 and deploy
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
echo "Pulling latest image from Docker Hub..."
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/my-website:latest
echo "Stopping old container if it exists..."
docker stop my-website || true
docker rm my-website || true
echo "Starting new container..."
docker run -d \
--name my-website \
--restart unless-stopped \
-p 80:80 \
${{ secrets.DOCKERHUB_USERNAME }}/my-website:latest
echo "Removing old unused images..."
docker image prune -f
echo "Deployment done! Website is live."
Save the file with Ctrl+S.
Let me explain what this pipeline does in simple words. When you push code to the main branch on GitHub, GitHub Actions automatically starts two jobs. The first job called "build-and-push" picks up your code, builds a Docker image from it, and pushes that image to Docker Hub. The second job called "deploy" only runs after the first job succeeds. It connects to your AWS EC2 server via SSH, pulls the new Docker image from Docker Hub, stops the old container that was running, and starts a new container with the new image. Your website is then updated.
PART 9 — PUSH EVERYTHING AND GO LIVE
Step 22: Push All Files to GitHub
You now have all your files ready. Push everything to GitHub.
GUI Way using GitHub Desktop:
- Open GitHub Desktop
- On the left side you will see all your changed files listed
- At the bottom left, in the "Summary" field type: Add all project files with CI/CD pipeline
- Click "Commit to main"
- Click "Push origin" button at the top
CLI Way:
In VS Code terminal:
git add .
git commit -m "Add all project files with CI/CD pipeline"
git push origin main
Step 23: Watch the Pipeline Run
- Go to https://github.com/yourusername/my-website
- Click the "Actions" tab in the top navigation
- You will see a workflow run starting. It will have a yellow circle which means it is running.
- Click on the workflow run to open it
- You will see two jobs: "Build and Push Docker Image to Docker Hub" and "Deploy to AWS EC2"
- Click on any job to see its real-time logs
- The build job takes about 2-3 minutes
- The deploy job takes about 1 minute
- When both jobs show green checkmarks, your website is deployed
If any job fails, click on it and read the error message. Common issues are wrong secret values or wrong EC2 IP.
Step 24: See Your Live Website
Open your browser and type:
http://YOUR_EC2_PUBLIC_IP
For example: http://13.233.45.67
Your website is now live on the internet, running on AWS!
PART 10 — HOW TO UPDATE YOUR WEBSITE
This is the beauty of CI/CD. Every time you want to update your website, you just change your code and push. Everything else happens automatically.
For example, open src/index.html and change "Hello World!" to "Hello World! Updated!". Save the file.
GUI Way: Open GitHub Desktop → you see the changed file → write a commit message → Commit to main → Push origin.
CLI Way:
git add .
git commit -m "Update heading text"
git push origin main
Then go to GitHub Actions tab and watch the pipeline run. In about 3-4 minutes your change will be live on AWS automatically.
FINAL FOLDER STRUCTURE (What You Should Have)
my-website/
│
├── .github/
│ └── workflows/
│ └── cicd.yml ← GitHub Actions pipeline
│
├── src/
│ ├── index.html ← Main HTML file
│ ├── css/
│ │ └── style.css ← All styling
│ └── js/
│ └── main.js ← All JavaScript
│
├── Dockerfile ← Instructions to build Docker image
├── nginx.conf ← Nginx web server config
└── .dockerignore ← Files to ignore when building Docker image
COMPLETE FLOW IN SIMPLE WORDS
You write code in VS Code on your Windows 11 laptop. You push the code to GitHub using GitHub Desktop (clicking) or git push (typing). GitHub sees new code on the main branch and automatically starts the CI/CD pipeline. The pipeline builds a Docker image of your website and uploads it to Docker Hub. Then the pipeline connects to your AWS server and tells it to download the new image and restart the website container. Your website is updated on the internet without you doing anything manually on the server.
TROUBLESHOOTING COMMON ISSUES
Docker Desktop not starting: Make sure virtualization is enabled in your BIOS. On Windows 11 it should be on by default. Also make sure WSL 2 is installed. Open PowerShell as Administrator and run: wsl --install
Permission denied when pushing to Docker Hub: Make sure you are logged in. Run "docker login" in terminal and enter your credentials.
GitHub Actions failing at deploy step: Double-check your EC2_SSH_KEY secret. Open your .pem file in VS Code, make sure you copied the ENTIRE content including the first line "-----BEGIN RSA PRIVATE KEY-----" and the last line "-----END RSA PRIVATE KEY-----".
Website not opening on EC2 IP: Check that port 80 is open in your EC2 security group. Go to AWS Console → EC2 → Security Groups → find your security group → check Inbound rules → HTTP port 80 should be there with source 0.0.0.0/0.
SSH connection refused when using CLI: Make sure your EC2 instance is in Running state. Also make sure the Security Group has port 22 open for your IP. Your IP might have changed if you are on a home network — go to Security Group rules and update the SSH source to "Anywhere" temporarily for testing, then change back to your IP.