Skip to main content

09 · GitHub Pages

TL;DR

GitHub Pages hosts static websites straight out of a GitHub repository — for free, with HTTPS, and with your own domain if you want. Every IIT Madras student gets a free .github.io subdomain. This is where you'll host course labs, portfolios, and project demos for the rest of this course.

What Can You Host?

Anything that's static — meaning the output is plain HTML/CSS/JS files (no database, no server-side code).

Perfect fit:

  • Documentation sites (like this one — Docusaurus)
  • Portfolio / résumé pages
  • Landing pages
  • React/Vue/Svelte single-page apps (built + deployed)
  • Blogs (Jekyll, Hugo, Astro)
  • Project demos (after Week 2 you'll deploy dynamic apps elsewhere)

Not a fit: anything requiring a backend, database, or secret API key on the server. For those we'll use Cloud Run (Week 7) or HuggingFace Spaces (Week 2).

The Two Kinds of Sites

TypeURLRepo name
User/Org sitehttps://<username>.github.ioMust be named <username>.github.io
Project sitehttps://<username>.github.io/<repo>Any repo name

You get one user site per account. You get unlimited project sites.

The Simplest Possible Deploy — 3 Minutes

Step 1 — Create a repo

bash
mkdir my-site && cd my-site
git init
echo "# My Site" > README.md

Create index.html:

html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello, GitHub Pages</title>
<style>
body {
font-family: system-ui, sans-serif;
max-width: 42rem;
margin: 4rem auto;
padding: 0 1rem;
line-height: 1.6;
}
h1 { color: #4f46e5; }
</style>
</head>
<body>
<h1>Hello, GitHub Pages 🎉</h1>
<p>If you're reading this, my site is live.</p>
</body>
</html>

Step 2 — Push to GitHub

bash
gh repo create my-site --public --source=. --push

Step 3 — Enable Pages

On GitHub: Settings → Pages:

  • Source: Deploy from a branch
  • Branch: main, folder / (root)
  • Save

Wait ~30 seconds. Your site is live at https://<username>.github.io/my-site/.

For any real project — especially if you're building a static site from source (React, Docusaurus, Hugo) — use GitHub Actions. This is how the TDS course site itself deploys.

yaml
.github/workflows/deploy.yml
name: Deploy to GitHub Pages

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'

- name: Install deps
run: npm ci

- name: Build
run: npm run build
env:
SITE_URL: https://${{ github.repository_owner }}.github.io
BASE_URL: /${{ github.event.repository.name }}/

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./build # or ./dist, ./public, etc.

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

Then in Settings → Pages → Source: pick GitHub Actions.

Every push to main now triggers a build + deploy. You get a link to the deployment in the Actions tab.

Jekyll — The Built-In Blogging Engine

If you commit a folder that looks like a Jekyll site (with a _config.yml), GitHub Pages will build it for you — no Action needed.

yaml
_config.yml
title: My Blog
description: Notes about things.
theme: minima
markdown
_posts/2026-05-10-hello.md
---
layout: post
title: "Hello, world"
date: 2026-05-10
---

This is my first post.

Push → GitHub Pages renders posts into HTML automatically. Zero build config.

For richer sites, switch to a more modern static generator (Hugo, Astro, Docusaurus, Next.js static export).

Custom Domain

Own example.com? Point it at your GitHub Pages site:

  1. On GitHub: Settings → Pages → Custom domain: enter example.com.
  2. At your DNS provider, add:
    • For apex (example.com): 4 A records pointing to GitHub's IPs:
      code
      185.199.108.153
      185.199.109.153
      185.199.110.153
      185.199.111.153
    • For subdomain (docs.example.com): a CNAME → <username>.github.io.
  3. Wait ~1 hour for DNS to propagate.
  4. Tick Enforce HTTPS.

GitHub provisions a TLS certificate automatically via Let's Encrypt.

.nojekyll — Bypass Jekyll Processing

If your build output contains files or folders starting with _ (like _next/ from Next.js), Jekyll's default processing will ignore them. To tell Pages "don't touch my files":

bash
touch build/.nojekyll # or wherever your output lives

Then include this file in the deploy.

Environment Variables for the Base URL

Project sites live at a subpath (/my-site/), which means <link href="/style.css"> will try /style.css (wrong) instead of /my-site/style.css. Every framework has a config for this:

FrameworkConfig
DocusaurusbaseUrl: '/my-site/' in docusaurus.config.ts
Vite / React Routerbase: '/my-site/' in vite.config.ts
Next.jsbasePath: '/my-site' in next.config.js
Astrobase: '/my-site' in astro.config.mjs
HugobaseURL = 'https://user.github.io/my-site/' in config.toml

Set this via an environment variable in your Action so the same codebase works on Pages and locally.

Limits and Considerations

LimitValue
Repo size1 GB
File size100 MB
Bandwidth100 GB/month "soft" limit
Builds10 per hour
Best forLow-to-medium traffic static sites

For anything beyond these limits, use Cloudflare Pages or Vercel — both free tiers are more generous and deploy the same folder.

The TDS Course Site Itself

The site you're reading now is built with Docusaurus 3 + TypeScript + Tailwind, and deployed via a GitHub Actions workflow just like the one above, to iit-madras.github.io/tds-course. Explore the repo to see a production-grade Docusaurus setup.

5-Minute Exercise

  1. Create a new repo <your-username>.github.io (note: the repo name must match your username exactly for a user site).
  2. Add an index.html and optionally a style.css.
  3. Push to main.
  4. Enable Pages → Source: main branch.
  5. Within 30 seconds, your site is live at https://<your-username>.github.io. Share the link!

Further Reading