This guide shows how to turn your own notes (Markdown files) into a public website on GitHub Pages. At the end your collection is available at https://<user>.github.io/<repository>/ and updates itself after every change pushed to the main branch.
Quick start: the template repository
The easiest way is the ready-made template, which contains the files and the deployment workflow.
Open the atom-forge/bookmd-starter template repository and click Use this template → Create a new repository. Make the repository public.
In the new repository: Settings → Pages → Build and deployment → Source: GitHub Actions.
Edit the Markdown files in the content/ folder (even in the GitHub web editor) and commit to main.
At the end of the Deploy book run on the Actions tab, the address of the site appears: https://<user>.github.io/<repository>/.
After every further commit to main the site updates itself. The sections below describe what the template contains and how to build it from scratch.
Building it from scratch
What you need
A GitHub account and a public repository. On the free plan GitHub Pages is not available from a private repository, and a private repository would not make the finished site private anyway.
Git, or the GitHub web interface for uploading files.
The build runs on GitHub's servers; you do not need to install anything on your own machine.
1. The structure of the repository
A collection is a plain folder with a few files and a content/ directory:
content/books.md lists the books. The quotes are required:
---
books:
- "[[my-first-book/book.md]]"
---# My books
Welcome.
The front page of a book (content/my-first-book/book.md) gives its own data and chapters. language is required:
---
title: My first book
language: en
children:
- "[[01-intro.md]]"
---# My first book
Short introduction.
The complete content rules (hierarchy, tags, chapters) are in the authoring guide. You can also use the examples/minimal folder as a starting point.
2. Turn on GitHub Pages
On the repository page: Settings → Pages → Build and deployment → Source: GitHub Actions.
3. The deployment workflow
Create the file .github/workflows/pages.yml:
title:Deploybookon:push:branches: [main]
workflow_dispatch:permissions:contents:readpages:writeid-token:writeconcurrency:group:pagescancel-in-progress:falsejobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-uses:actions/setup-node@v4with:node-version:'24'-uses:oven-sh/setup-bun@v2with:bun-version:'1.4.0'-run:buninstall-run:bunruncheckenv:# Only needed for books from private repositories (docs: Books from other GitHub repositories).BOOKMD_APP_ID:${{secrets.BOOKMD_APP_ID}}BOOKMD_APP_PRIVATE_KEY:${{secrets.BOOKMD_APP_PRIVATE_KEY}}-run:bunrunbuildenv:BASE_PATH:/${{github.event.repository.name}}# Only needed for books from private repositories (docs: Books from other GitHub repositories).BOOKMD_APP_ID:${{secrets.BOOKMD_APP_ID}}BOOKMD_APP_PRIVATE_KEY:${{secrets.BOOKMD_APP_PRIVATE_KEY}}-uses:actions/upload-pages-artifact@v3with:path:builddeploy:needs:buildruns-on:ubuntu-latestenvironment:name:github-pagesurl:${{steps.deployment.outputs.page_url}}steps:-id:deploymentuses:actions/deploy-pages@v4
The BOOKMD_APP_* lines are used only for books from private repositories (how to set them up); without the secrets they are empty and ignored.
BASE_PATH is needed because "project" sites live at https://<user>.github.io/<repository>/, that is, in a subdirectory. Remove the env block in two cases:
the repository is named <user>.github.io (a "user site" lives at the root), or
You can follow the run on the repository's Actions tab. When it succeeds, the deploy step prints the address of the site; the same address is under Settings → Pages. The first deployment can take a few minutes.
Your own address: a user site and a custom domain
By default the collection lives at https://<user>.github.io/<repository>/, in a subdirectory. There are two ways to give it a shorter address; in both the site is at the root of its address, so the build does not need BASE_PATH.
A user or organisation site: <name>.github.io
Every user and every organisation can have one special site at https://<name>.github.io/.
Name the repository exactly <name>.github.io, where <name> is your user name or the name of the organisation (for an organisation, create the repository inside it).
Turn on Pages as in step 2: Settings → Pages → Build and deployment → Source: GitHub Actions.
In the workflow .github/workflows/pages.yml, remove the env block of the bun run build step (the one that sets BASE_PATH), so that the step is only - run: bun run build.
Push. The site is at https://<name>.github.io/.
Good to know:
You can have only one such site per user or organisation. Your other repositories with Pages remain project sites, at https://<name>.github.io/<repository>/.
Creating Pages for an organisation needs the owner or admin role of the organisation.
Public repositories can use Pages on every plan; a private repository needs a paid plan.
A custom domain
You can serve the site from your own domain, for example books.example.com or example.com. It works with a project site and with a user site alike.
Decide the address. A subdomain (books.example.com) is the simpler choice; an apex domain (example.com) needs more records.
In the DNS settings of your domain provider add the records:
Subdomain: one CNAME record from books (the name you chose) to <name>.github.io (your user or organisation name, without the repository name).
Apex domain: A records for @ to the four addresses GitHub lists for Pages: 185.199.108.153, 185.199.109.153, 185.199.110.153 and 185.199.111.153 (and, if you like, the matching AAAA records for IPv6). Check the current addresses in the GitHub documentation "Managing a custom domain for your GitHub Pages site" before you enter them.
In the repository: Settings → Pages → Custom domain, enter the domain and save. GitHub checks the DNS records; this can take from a few minutes to a day.
When the check passes, tick Enforce HTTPS. The certificate is issued automatically and can take up to an hour.
Remove the env block with BASE_PATH from the workflow (the site is now at the root of the domain), and push.
Good to know:
With a deployment from GitHub Actions you do not add a CNAME file to the repository: the Custom domain setting is enough, and a CNAME file is ignored.
To stop someone else from taking over your domain, an owner of the account can verify the domain in the Pages settings of the user or organisation.
If the site shows up without CSS after the change, BASE_PATH is still set; see Troubleshooting.
Updating
Edit the Markdown files, commit and push to main. The workflow rebuilds and replaces the site. You can also start it by hand with Actions → Deploy book → Run workflow.
Troubleshooting
The site appears without CSS and with broken links: BASE_PATH is missing or wrong. Project sites need /<repository-name> with no trailing slash; a user site (<name>.github.io) or a site with a custom domain must not set it at all.
check reports an error on a link: the error names the file and the link; fix or remove it and push again.
404 at the address: the source of Pages is not "GitHub Actions", or the first deployment has not finished yet.
Publishing a collection on GitHub
This guide shows how to turn your own notes (Markdown files) into a public website on GitHub Pages. At the end your collection is available at
https://<user>.github.io/<repository>/and updates itself after every change pushed to themainbranch.Quick start: the template repository
The easiest way is the ready-made template, which contains the files and the deployment workflow.
atom-forge/bookmd-startertemplate repository and click Use this template → Create a new repository. Make the repository public.content/folder (even in the GitHub web editor) and commit tomain.https://<user>.github.io/<repository>/.After every further commit to
mainthe site updates itself. The sections below describe what the template contains and how to build it from scratch.Building it from scratch
What you need
The build runs on GitHub's servers; you do not need to install anything on your own machine.
1. The structure of the repository
A collection is a plain folder with a few files and a
content/directory:package.json:{ "private": true, "type": "module", "scripts": { "check": "bookmd check", "build": "bookmd build" }, "dependencies": { "@atom-forge/bookmd": "^0.1.4" } }portal.config.ts:export default { title: 'My books', contentRoot: './content', entrypoint: 'books.md' };.gitignore:content/books.mdlists the books. The quotes are required:--- books: - "[[my-first-book/book.md]]" --- # My books Welcome.The front page of a book (
content/my-first-book/book.md) gives its own data and chapters.languageis required:--- title: My first book language: en children: - "[[01-intro.md]]" --- # My first book Short introduction.The complete content rules (hierarchy, tags, chapters) are in the authoring guide. You can also use the
examples/minimalfolder as a starting point.2. Turn on GitHub Pages
On the repository page: Settings → Pages → Build and deployment → Source: GitHub Actions.
3. The deployment workflow
Create the file
.github/workflows/pages.yml:title: Deploy book on: push: branches: [main] workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '24' - uses: oven-sh/setup-bun@v2 with: bun-version: '1.4.0' - run: bun install - run: bun run check env: # Only needed for books from private repositories (docs: Books from other GitHub repositories). BOOKMD_APP_ID: ${{ secrets.BOOKMD_APP_ID }} BOOKMD_APP_PRIVATE_KEY: ${{ secrets.BOOKMD_APP_PRIVATE_KEY }} - run: bun run build env: BASE_PATH: /${{ github.event.repository.name }} # Only needed for books from private repositories (docs: Books from other GitHub repositories). BOOKMD_APP_ID: ${{ secrets.BOOKMD_APP_ID }} BOOKMD_APP_PRIVATE_KEY: ${{ secrets.BOOKMD_APP_PRIVATE_KEY }} - uses: actions/upload-pages-artifact@v3 with: path: build deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment uses: actions/deploy-pages@v4The
BOOKMD_APP_*lines are used only for books from private repositories (how to set them up); without the secrets they are empty and ignored.BASE_PATHis needed because "project" sites live athttps://<user>.github.io/<repository>/, that is, in a subdirectory. Remove theenvblock in two cases:<user>.github.io(a "user site" lives at the root), orBoth are described in your own address.
If
bun run checkreports an error, the workflow stops and nothing reaches the site.4. Upload
git init -b main git add . git commit -m "First books" git remote add origin git@github.com:<user>/<repository>.git git push -u origin mainYou can follow the run on the repository's Actions tab. When it succeeds, the
deploystep prints the address of the site; the same address is under Settings → Pages. The first deployment can take a few minutes.Your own address: a user site and a custom domain
By default the collection lives at
https://<user>.github.io/<repository>/, in a subdirectory. There are two ways to give it a shorter address; in both the site is at the root of its address, so the build does not needBASE_PATH.A user or organisation site:
<name>.github.ioEvery user and every organisation can have one special site at
https://<name>.github.io/.<name>.github.io, where<name>is your user name or the name of the organisation (for an organisation, create the repository inside it)..github/workflows/pages.yml, remove theenvblock of thebun run buildstep (the one that setsBASE_PATH), so that the step is only- run: bun run build.https://<name>.github.io/.Good to know:
https://<name>.github.io/<repository>/.A custom domain
You can serve the site from your own domain, for example
books.example.comorexample.com. It works with a project site and with a user site alike.books.example.com) is the simpler choice; an apex domain (example.com) needs more records.CNAMErecord frombooks(the name you chose) to<name>.github.io(your user or organisation name, without the repository name).Arecords for@to the four addresses GitHub lists for Pages:185.199.108.153,185.199.109.153,185.199.110.153and185.199.111.153(and, if you like, the matchingAAAArecords for IPv6). Check the current addresses in the GitHub documentation "Managing a custom domain for your GitHub Pages site" before you enter them.envblock withBASE_PATHfrom the workflow (the site is now at the root of the domain), and push.Good to know:
CNAMEfile to the repository: the Custom domain setting is enough, and aCNAMEfile is ignored.BASE_PATHis still set; see Troubleshooting.Updating
Edit the Markdown files, commit and push to
main. The workflow rebuilds and replaces the site. You can also start it by hand with Actions → Deploy book → Run workflow.Troubleshooting
BASE_PATHis missing or wrong. Project sites need/<repository-name>with no trailing slash; a user site (<name>.github.io) or a site with a custom domain must not set it at all.checkreports an error on a link: the error names the file and the link; fix or remove it and push again.