# π Muaz Bhutta β Portfolio
**A blazing-fast, zero-dependency static portfolio with automated CI/CD deployment**
[](https://muazbhutta.online)
[](#-cicd--auto-deploy-on-every-push)
[](LICENSE)






*No build step. No framework. No node_modules black hole. Just clean HTML/CSS/JS β deployed automatically to a self-hosted VPS on every push.*
[Live Demo](https://muazbhutta.online) Β· [Report Bug](../../issues) Β· [Request Feature](../../issues)
---
## π Table of Contents
- [β¨ Features](#-features)
- [ποΈ Architecture](#οΈ-architecture)
- [π File Structure](#-file-structure)
- [βοΈ Updating Content](#οΈ-updating-content-the-important-part)
- [π Getting Started](#-getting-started)
- [Run Locally](#step-1--run-it-locally)
- [Push to GitHub](#step-2--push-to-github)
- [Manual Deploy](#step-3--manual-deploy-to-the-vps)
- [CI/CD Setup](#step-4--cicd--auto-deploy-on-every-push)
- [π Deployment Flow](#-deployment-flow)
- [π οΈ Tech Stack](#οΈ-tech-stack)
- [π License](#-license)
- [π¬ Contact](#-contact)
---
## β¨ Features
| Feature | Description |
|---|---|
| β‘ **Fully Static** | Zero dependencies beyond Google Fonts β loads instantly |
| π **Light / Dark / Auto Theme** | Auto mode follows the visitor's system setting and updates **live** if it changes |
| π **Scroll-Reveal Timeline** | Animated career timeline, fully responsive down to mobile |
| ποΈ **Single-File Content** | All content lives in `data.js` β edit one file, never touch markup |
| π€ **Auto-Deploy CI/CD** | Every push to `main` deploys to the VPS via GitHub Actions + rsync |
| π **Self-Hosted** | Served by Caddy with automatic HTTPS on a personal VPS |
---
## ποΈ Architecture
```
βββββββββββββββ git push ββββββββββββββββββββ
β Developer β βββββββββββββββββΆ β GitHub (main) β
βββββββββββββββ ββββββββββ¬ββββββββββ
β triggers
βΌ
ββββββββββββββββββββ
β GitHub Actions β
β (deploy.yml) β
ββββββββββ¬ββββββββββ
β rsync over SSH
βΌ
βββββββββββββββ HTTPS (443) ββββββββββββββββββββ
β Visitor β βββββββββββββββββ β VPS + Caddy β
βββββββββββββββ auto-SSL π β /var/www/portfolioβ
ββββββββββββββββββββ
```
---
## π File Structure
```
portfolio/
βββ index.html β page structure
βββ style.css β design & theme tokens
βββ script.js β renders data.js into the page (no need to edit)
βββ data.js β β EDIT THIS FILE to add content
βββ .gitignore β keeps secrets & system files out of git
βββ .github/
βββ workflows/
βββ deploy.yml β auto-deploy pipeline
```
---
## βοΈ Updating Content (the important part)
To add a new project, open **`data.js`** and add an object to the `PROJECTS` array:
```js
{
id: "unique-id",
title: "Project Name",
summary: "1β2 line description.",
stack: ["Tech1", "Tech2"],
link: "https://...", // or "#" if there's no link
status: "production", // "production" or "lab"
},
```
> π‘ **Skills, credentials, About paragraphs, and the career timeline all work the same way** β add or edit an object in the matching array. Nothing in `index.html`, `style.css`, or `script.js` ever needs to change.
---
## π Getting Started
### Step 1 β Run it locally
These are static files, so "running" it just means opening it in a browser:
**Option A (simplest):**
```
double-click index.html
```
**Option B (recommended β mirrors production behavior):**
```bash
python3 -m http.server 8000
```
Then open **http://localhost:8000**. This matches how Caddy serves the files on the VPS, so local behavior = production behavior.
---
### Step 2 β Push to GitHub
```bash
cd portfolio
git init
git add .
git commit -m "Initial portfolio"
git branch -M main
git remote add origin https://github.com//portfolio.git
git push -u origin main
```
Why this matters:
1. π Your code becomes **version-controlled** β every change is tracked
2. π€ It becomes the **trigger source** for the CI/CD pipeline β every push to `main` auto-deploys
---
### Step 3 β Manual Deploy to the VPS
*(first time, or without CI/CD)*
The VPS already runs Caddy serving `muazbhutta.online` β just copy files into the directory Caddy points at:
```bash
# from your local machine
rsync -avz --delete ./portfolio/ user@your-server-ip:/var/www/portfolio/
```
Confirm the Caddyfile block:
```caddy
muazbhutta.online {
root * /var/www/portfolio
file_server
}
```
Then reload:
```bash
sudo systemctl reload caddy
```
β
**`https://muazbhutta.online` is now live** β Caddy handles SSL automatically.
> π **Want a subdomain instead?** (e.g. `portfolio.muazbhutta.online`) β add a new Caddyfile block with the same directives, using the subdomain as the site address.
---
### Step 4 β CI/CD: Auto-Deploy on Every Push
`.github/workflows/deploy.yml` uses **GitHub Actions** to rsync files to the VPS over SSH on every push to `main`.
#### π Required Repository Secrets
Go to: **Repo β Settings β Secrets and variables β Actions β New repository secret**
| Secret | Value |
|---|---|
| `DEPLOY_HOST` | Your VPS IP address |
| `DEPLOY_USER` | SSH username on the VPS |
| `DEPLOY_PORT` | `22` (or your custom SSH port) |
| `DEPLOY_PATH` | `/var/www/portfolio/` |
| `DEPLOY_SSH_KEY` | Private SSH deploy key *(see below)* |
#### ποΈ Generating a Dedicated Deploy Key *(best practice)*
```bash
# on your local machine
ssh-keygen -t ed25519 -C "github-deploy" -f ./deploy_key -N ""
```
This creates `deploy_key` (private) + `deploy_key.pub` (public).
Authorize the **public** key on the VPS:
```bash
ssh-copy-id -i deploy_key.pub user@your-server-ip
```
*(or manually append `deploy_key.pub` contents to `~/.ssh/authorized_keys` on the VPS)*
Then paste the **full contents of `deploy_key`** (the private key) into the `DEPLOY_SSH_KEY` secret on GitHub.
> β οΈ **Never commit the private key to the repo.** It lives only in GitHub Secrets. The included `.gitignore` blocks `*.key` and `.ssh/` as a safety net.
#### π§ͺ Testing the Pipeline
```bash
# make a small change, e.g. add a project in data.js
git add .
git commit -m "Add new project"
git push
```
Watch the **Actions** tab β pipeline runs β within **30β60 seconds** the change is live. No manual rsync. No SSH. β¨
---
## π Deployment Flow
```
Edit data.js (add a project / skill / timeline entry)
β
βΌ
git add . && git commit -m "update" && git push
β
βΌ
GitHub Actions triggers automatically
β
βΌ
rsync copies files to VPS over SSH
β
βΌ
Caddy is already serving β change is LIVE π
```
**From now on: edit `data.js`, push, done.** Everything else is automatic.
---
## π οΈ Tech Stack
| Layer | Technology | Why |
|---|---|---|
| **Frontend** | Vanilla HTML5 / CSS3 / JS | Zero build step, instant loads, easy maintenance |
| **Theming** | CSS custom properties + `prefers-color-scheme` | Native Light/Dark/Auto with live OS sync |
| **Web Server** | [Caddy](https://caddyserver.com/) | Automatic HTTPS, dead-simple config |
| **CI/CD** | GitHub Actions | Free, integrated, zero-maintenance pipeline |
| **Transport** | rsync over SSH | Fast delta transfers β only changed files move |
| **Hosting** | Self-hosted VPS | Full control, part of a larger self-hosted infra lab |
---
## π License
Distributed under the **MIT License**. See `LICENSE` for more information.
---
## π¬ Contact
**Muaz Bhutta** β Junior Network / Cloud & DevOps Engineer
[](https://muazbhutta.online)
[](https://github.com/your-username)
[](https://linkedin.com/in/your-profile)
β **If this repo helped you build your own portfolio pipeline, consider giving it a star!** β
*Built with β€οΈ and zero frameworks*