Publishing this site
From a local folder to a live site on your own domain
What you will do
- Put the site under version control and push it to GitHub
- Turn on GitHub Pages using the workflow already in this repository
- Point a domain you own at the site
- Force HTTPS and verify everything resolves
- Learn the two-command routine for publishing changes afterwards
Before you start
| You need | How to check | If missing |
|---|---|---|
| Git | git --version |
git-scm.com/downloads |
| A GitHub account | Sign in at github.com | Free tier is sufficient |
| Quarto | quarto --version |
quarto.org |
| A domain | You have bought one | Any registrar — see Part 3 |
| The site renders locally | quarto render completes |
Fix errors first |
quarto renderIf this fails on your machine it will fail in GitHub Actions, and debugging is slower there. Get a clean local render first.
Part 1 — Put the site on GitHub
1.1 Initialise the repository
From the project folder:
cd path/to/rlearning_site
git init
git branch -M maingit branch -M main renames the default branch to main, which is what the publishing workflow expects.
1.2 Check what will be committed
The repository already has a .gitignore that excludes _site/, .quarto/ and .Rproj.user/. Confirm nothing unwanted is staged:
git add .
git status --shortYou should see roughly 60 files — the .qmd lessons, _quarto.yml, assets/, the workflow and the README. You should not see _site/, .quarto/ or any data files.
Anything committed stays in the history even if you delete it in a later commit. If a file with confidential content is ever pushed, treat it as disclosed. Removing it properly means rewriting history, which is disruptive and does not help if someone already cloned the repository.
1.3 First commit
git commit -m "Add R Learning Lab Quarto site
Four courses (R Programming, R Shiny, Python, Clinical Programming with R),
46 lessons plus landing, about, training and contact pages. Pharma-corporate
theme and a GitHub Pages publishing workflow."If Git asks who you are:
git config --global user.name "Your Name"
git config --global user.email "you@example.com"1.4 Create the GitHub repository
With the GitHub CLI — one command, creates and pushes:
gh auth login # first time only
gh repo create rlearning-site --public --source=. --remote=origin --pushThrough the website — go to github.com/new and:
- Repository name:
rlearning-site - Public — required for GitHub Pages on the free tier
- Do not add a README,
.gitignoreor licence; you already have them
Then connect and push:
git remote add origin https://github.com/YOUR-USERNAME/rlearning-site.git
git push -u origin mainThe repository name determines your default Pages URL:
| Repository name | Default URL | Type |
|---|---|---|
rlearning-site |
username.github.io/rlearning-site |
Project site |
username.github.io |
username.github.io |
User site |
Once a custom domain is attached, both serve from the domain root, so this choice stops mattering. Pick whichever you prefer.
Part 2 — Turn on GitHub Pages
2.1 Set the publishing source
In your repository on GitHub:
- Settings (top row of the repository, not your account settings)
- Pages in the left sidebar
- Under Build and deployment → Source, choose GitHub Actions
Do not choose “Deploy from a branch”. The workflow in .github/workflows/publish.yml uses the Actions method, and the two are mutually exclusive.
2.2 Watch the first deployment
Open the Actions tab. A run called Publish site should be in progress.
It does three things in order:
- Checks every internal link resolves — a broken cross-reference fails the build
- Renders the site with Quarto
- Uploads
_site/and deploys it
The first run takes two to three minutes. When it finishes, Settings → Pages shows the live URL.
2.3 If the run fails
| Symptom | Cause | Fix |
|---|---|---|
| “Get Pages site failed” | Source not set to GitHub Actions | Do step 2.1 first |
| Link check fails | A .qmd link points nowhere |
The log names the file and target |
| Quarto render error | Same error you would see locally | Reproduce with quarto render |
| “Resource not accessible” | Workflow permissions | Settings → Actions → General → Workflow permissions → Read and write |
Part 3 — Connect your domain
3.1 Decide on the shape
You have three options. The third is what most sites use.
| Option | Visitors type | DNS needed |
|---|---|---|
| Apex only | example.com |
4 A records (+ 4 AAAA for IPv6) |
| Subdomain only | www.example.com |
1 CNAME record |
| Both, apex redirects to www | Either works | A records and a CNAME |
The third is recommended: www is a CNAME, which behaves better with CDNs and lets your DNS provider fail over cleanly, and GitHub creates the redirect between the two automatically once both are configured.
3.2 Add the DNS records
At your registrar’s DNS panel, add the following. Delete any existing A, AAAA, CNAME or parking records for the same names first — conflicting records are the most common reason this does not work.
For the apex domain (example.com), four A records:
| Type | Name / Host | Value | TTL |
|---|---|---|---|
| A | @ |
185.199.108.153 |
3600 |
| A | @ |
185.199.109.153 |
3600 |
| A | @ |
185.199.110.153 |
3600 |
| A | @ |
185.199.111.153 |
3600 |
Optionally add IPv6, four AAAA records:
| Type | Name / Host | Value |
|---|---|---|
| AAAA | @ |
2606:50c0:8000::153 |
| AAAA | @ |
2606:50c0:8001::153 |
| AAAA | @ |
2606:50c0:8002::153 |
| AAAA | @ |
2606:50c0:8003::153 |
For the www subdomain, one CNAME record:
| Type | Name / Host | Value | TTL |
|---|---|---|---|
| CNAME | www |
YOUR-USERNAME.github.io. |
3600 |
The value is your GitHub username followed by .github.io — not the repository name, and not the full site URL. The trailing dot is required by some providers and ignored by others.
The IP addresses above are GitHub’s published values, but they have changed before and could change again. Confirm them at Managing a custom domain for your GitHub Pages site before you rely on them.
3.3 Registrar-specific notes
| Registrar | Watch out for |
|---|---|
| Cloudflare | Set the proxy status to DNS only (grey cloud), not Proxied (orange). Proxying breaks GitHub’s certificate issuance. You can turn it on later, after HTTPS is working. |
| Namecheap | Use Advanced DNS. The host field is @ for apex, www for the subdomain. Remove the default “CNAME Record → parkingpage” entry. |
| GoDaddy | Remove the default A record pointing at their parking IP. GoDaddy sometimes re-adds it — check after 24 hours. |
| Google Domains / Squarespace | Under DNS → Custom records. Apex is a blank name or @. |
| Route 53 | Use an Alias record for the apex if you prefer, or the four A records. |
3.4 Tell GitHub about the domain
Back in Settings → Pages → Custom domain:
- Enter your domain — use
www.example.comif you set up both - Click Save
- GitHub runs a DNS check. A green tick means it resolved.
With branch-based publishing, GitHub stores the custom domain in a CNAME file, and workflows that overwrite it cause the domain to reset on every push. That is the cause of most “my custom domain keeps disappearing” reports.
This repository publishes with a GitHub Actions workflow, where a CNAME file is ignored and not required. The domain lives in the repository settings and persists across deployments. Adding a CNAME file to the project will do nothing — set the domain in Settings, and only there.
3.5 Wait, then enforce HTTPS
DNS changes take anywhere from a few minutes to 24 hours to propagate. GitHub also has to obtain a Let’s Encrypt certificate, which it does automatically once the DNS check passes.
Once the certificate is issued, return to Settings → Pages and tick Enforce HTTPS. The checkbox is greyed out until the certificate exists — if it is still grey after an hour, the DNS is not yet correct.
Part 4 — Point the site config at the domain
Quarto uses site-url for the sitemap, canonical URLs and social preview cards. Update it:
_quarto.yml
website:
title: "R Learning Lab"
site-url: https://www.example.comThen publish the change:
quarto render # confirm it still builds
git add _quarto.yml
git commit -m "Set site-url to the custom domain"
git pushThe push triggers the workflow and the site redeploys in about two minutes.
Part 5 — Verify
5.1 Check DNS resolution
# Apex should return the four GitHub IPs
dig +short example.com
# www should return the github.io alias, then the IPs
dig +short www.example.com
# On Windows
nslookup example.com5.2 Check the site responds
# Should return HTTP/2 200
curl -sSI https://www.example.com | head -1
# Apex should return a 301 redirect to www
curl -sSI https://example.com | head -3
# HTTP should redirect to HTTPS
curl -sSI http://www.example.com | head -35.3 Final checklist
Routine updates
Once set up, publishing a change is two commands:
git add .
git commit -m "Describe what changed"
git pushThe workflow renders and deploys automatically. Check the Actions tab if the change does not appear within a few minutes.
A safer habit for anything substantial — render locally first, so a mistake never reaches the live site:
quarto render # catches errors before they reach CI
git add .
git commit -m "Add lesson on X"
git pushTroubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 404 at the custom domain | DNS not propagated | Wait; check with dig |
404 at username.github.io/repo |
Pages source not set | Settings → Pages → Source → GitHub Actions |
| “Domain does not resolve to the GitHub Pages server” | Wrong or conflicting DNS records | Remove old A/CNAME/parking records |
| “Enforce HTTPS” greyed out | Certificate not yet issued | Wait up to 24h after DNS is correct |
| Certificate warning in the browser | Certificate issued for the other name | Ensure both apex and www records exist |
| Custom domain keeps resetting | Usually a branch-deploy problem | Not applicable here — confirm Source is GitHub Actions |
| CSS missing, unstyled page | Wrong site-url, or a stale cache |
Fix site-url; hard-refresh |
| Site not updating after a push | Workflow failed | Check the Actions tab |
| Cloudflare 522 or redirect loop | Proxy enabled | Set the record to DNS only (grey cloud) |
Optional hardening
Verify the domain against takeover
If you later delete the repository but leave the DNS pointing at GitHub, someone else can claim the domain by creating a repository with the same name. GitHub’s domain verification prevents that.
Account settings → Pages → Add a domain, then add the TXT record it gives you. Do this once per domain.
Add a custom 404 page
Create 404.qmd at the project root:
404.qmd
---
title: "Page not found"
sidebar: false
toc: false
---
That page does not exist. Try the [course index](index.qmd), or use the search
box in the navigation bar.Quarto renders it to 404.html and GitHub Pages serves it automatically.
Deploy on a tag instead of every push
For a site that should only update deliberately, change the trigger in .github/workflows/publish.yml:
.github/workflows/publish.yml
on:
push:
tags: ["v*"]
workflow_dispatch:Then publish with:
git tag -a v1.1 -m "Add Python testing lesson"
git push --tagsThe short version
git init, commit, push to a new public GitHub repository- Settings → Pages → Source → GitHub Actions
- Add four A records for the apex and a CNAME for
www - Settings → Pages → Custom domain → enter it → Save
- Wait for the DNS check, then tick Enforce HTTPS
- Set
site-urlin_quarto.yml, commit and push - Afterwards:
git add . && git commit -m "..." && git push