My Learn Base
Live site: mylearnbase.com Repository: github.com/RustWright/mylearnbase
My Learn Base is a personal learning journal and portfolio site. It documents projects, experiments, and things I find interesting — serving both as a reference for my future self and a resource for others.
Tech Stack
| Layer | Tool | Purpose |
|---|---|---|
| Static site generator | Zola | Converts markdown into HTML |
| Theme | Serene | Provides the visual design |
| Hosting | Cloudflare Pages | Serves the static site globally |
| Documentation | mdBook | This documentation |
How It Works
The site is fully static — there’s no server-side code running. The workflow is:
- Write a post in markdown with TOML frontmatter
- Zola combines the markdown with HTML templates and generates plain HTML/CSS/JS
- Push to GitHub, which triggers Cloudflare Pages to rebuild and deploy
The result is a fast, secure site with zero hosting costs.
Project Goals
- Blog/Learning Journal — Document processes when working on projects, with series-based multi-part posts
- Portfolio — Showcase completed work and host interactive demos (future)
- Monetization — Organic revenue generation to cover maintenance costs (future)
Design Priority
Authoring experience first. If it’s frictionless to create content, content gets created consistently.
Prerequisites
Required
Zola
Zola is the static site generator that builds the site. Install version 0.22.1 or later.
Linux (recommended):
# Download and extract
curl -sL https://github.com/getzola/zola/releases/download/v0.22.1/zola-v0.22.1-x86_64-unknown-linux-gnu.tar.gz | tar xz
# Move to a directory in your PATH
sudo mv zola /usr/local/bin/
# Verify
zola --version
Other platforms: See the Zola installation guide.
Git
Required for cloning the repo and managing the Serene theme (which is a git submodule).
git --version # Should be 2.x or later
Optional
mdBook
Only needed if you want to build this documentation locally.
# Requires Rust/Cargo
cargo install mdbook
# Verify
mdbook --version
Rust / Cargo
Only needed for installing mdBook or for future WASM demo development.
See rustup.rs for installation.
Local Development
Clone the Repository
# Clone with submodules (important — the theme is a submodule)
git clone --recurse-submodules https://github.com/RustWright/mylearnbase.git
cd mylearnbase
If you already cloned without --recurse-submodules:
git submodule update --init
Set Up Git Hooks
The repo includes a pre-commit hook at .githooks/pre-commit that automatically sets the updated frontmatter field on any modified post. The hook runs on every commit and does the following:
- Finds staged
.mdfiles incontent/posts/that were modified (ignores_index.md) - Reads each file’s last-modified date from the filesystem
- Skips the file if that date matches the original
datefield (i.e. no update needed) - Inserts an
updatedfield afterdateif one doesn’t exist, or updates it if it does - Re-stages the file so the new frontmatter is included in the commit
The template already handles the display — if updated exists and differs from date, it renders “Updated on [date]” next to the publish date.
To enable the hook, configure git to use the tracked .githooks/ directory:
git config core.hooksPath .githooks
This only needs to be run once per clone.
Run the Dev Server
zola serve
This starts a local server at http://127.0.0.1:1111 with live reload — any changes to content, templates, or styles automatically rebuild and refresh the browser.
Project Structure
mylearnbase/
├── zola.toml # Site configuration
├── content/ # Markdown content (what you write)
│ ├── _index.md # Homepage
│ └── posts/
│ ├── _index.md # Blog section config
│ └── *.md # Individual posts
├── templates/ # Template overrides (customizations)
├── themes/serene/ # Serene theme (git submodule — don't edit)
├── sass/ # SCSS style overrides (currently empty)
├── static/ # Static files copied as-is to output
├── public/ # Build output (gitignored)
└── docs/ # This documentation (mdBook)
Build for Production
zola build
Output goes to public/. This is what gets deployed to Cloudflare Pages.
Check for Errors
zola check
Validates internal links, external links, and configuration without building the full site.
Deployment
The site deploys automatically to Cloudflare Pages on every push to the main branch.
How It Works
Push to GitHub → Cloudflare detects change → Runs build command → Deploys to CDN
No manual deployment steps needed.
Cloudflare Pages Configuration
| Setting | Value |
|---|---|
| Production branch | main |
| Build command | curl -sL https://github.com/getzola/zola/releases/download/v0.22.1/zola-v0.22.1-x86_64-unknown-linux-gnu.tar.gz | tar xz && ./zola build |
| Build output directory | public |
| Custom domain | mylearnbase.com |
Why the custom build command? Cloudflare Pages no longer pre-installs Zola. The build command downloads Zola on each build, then runs
zola build. This adds a few seconds but is reliable.
Domain Setup
The domain mylearnbase.com was migrated to Cloudflare’s nameservers (from Porkbun) and configured as a custom domain in the Cloudflare Pages dashboard. HTTPS is handled automatically by Cloudflare.
Known Issue: Preview URLs
Cloudflare Pages generates preview URLs for non-production branches (e.g., abc123.mylearnbase.pages.dev). These previews may have broken CSS because Zola generates URLs based on base_url in zola.toml, which is set to https://mylearnbase.com. The preview URL doesn’t match, so absolute paths to CSS files break.
Workaround: Test locally with zola serve instead of relying on preview URLs for visual verification.
Architecture Overview
The Data Flow
When you run zola build (or push to GitHub, triggering Cloudflare), this is what happens:
1. Zola reads zola.toml for configuration
2. Scans content/ for all .md files
3. Parses frontmatter (TOML between +++) and body (markdown)
4. For each page: picks the right template, injects the content
5. Auto-generates taxonomy pages (/tags/meta/, /series/building-my-website/)
6. Compiles sass/ into CSS
7. Copies static/ files as-is to the output
8. Writes everything to public/
The output is entirely static HTML, CSS, and JS — no server-side processing at request time.
The Override System
Zola has a layered file resolution system. When it needs a template or stylesheet, it checks:
- Your project’s files first (
templates/,sass/) - The theme’s files second (
themes/serene/templates/,themes/serene/sass/)
If you place a file in your project with the same name and path as one in the theme, yours wins. This is how you customize behavior without editing the theme directly — and it means theme updates won’t overwrite your changes.
Template Inheritance
Every page template follows a chain:
Your template (e.g., templates/blog.html)
└── extends → Theme's _base.html
└── defines blocks: {% block content %}, {% block title %}, etc.
The base template (_base.html from Serene) provides the HTML skeleton — <head>, navigation, dark mode toggle, scripts. Your templates fill in specific {% block %} sections with page-specific content.
Technology Decisions
| Decision | Choice | Alternatives Considered | Deciding Factor |
|---|---|---|---|
| Framework | Zola | Dioxus 0.7, Leptos, Astro | Dioxus SSG was broken; Zola validated and mature |
| Theme | Serene | Tabi, Abridge | Minimal, dark mode, blog-focused |
| Hosting | Cloudflare Pages | GitHub Pages, Vercel, Netlify | Free, global CDN, future Workers option |
| Content format | Markdown + TOML | Database, CMS | Simplicity, editor-friendly, portable |
| Future interactivity | WASM islands | Full SPA rewrite | Additive approach, no rewrite needed |
Future: WASM Islands
When interactive demos are needed, the approach is additive:
- Create a separate Rust crate for each demo (e.g.,
demos/sorting-viz/) - Compile to WASM
- Output
.wasm+.jsfiles tostatic/wasm/ - Embed in posts via
<div>+<script>tags - Zola serves these as static files — no backend needed
The existing site structure, URLs, and deployment pipeline stay unchanged.
Content Structure
Sections vs Pages
Zola has a two-level content system:
- Sections — defined by
_index.mdfiles. They set rules that apply to all pages within that directory. - Pages — any other
.mdfile. These are the actual content (blog posts, standalone pages).
content/
├── _index.md ← Section: the homepage
└── posts/
├── _index.md ← Section: configures the blog listing
└── hello-world.md ← Page: an individual post
The _index.md in posts/ is not a post itself — it configures the entire section. Its page_template = "post.html" setting means every .md file in content/posts/ automatically uses post.html as its template without you specifying it per post.
Homepage (content/_index.md)
Uses the home.html template from Serene. Configured to show the 5 most recent posts. The markdown body after the frontmatter becomes the homepage description text.
Key settings:
recent = true— shows recent postsrecent_max = 5— limits how manylinks = []— no social links yet (can add GitHub, Twitter, etc.)
Blog Section (content/posts/_index.md)
Configures how the blog listing and all posts behave:
sort_by = "date"— newest posts firsttemplate = "blog.html"— the listing page templatepage_template = "post.html"— template for every post in this sectioninsert_anchor_links = "right"— clickable#links appear beside headingsgenerate_feeds = true— RSS feed for this section
The [extra] section controls Serene-specific features:
toc = true— table of contents sidebar on postscopy = true— “copy” button on code blocksoutdate_alert = false— staleness warning (disabled by default)
Taxonomies
Taxonomies are Zola’s grouping system. Three are configured:
| Taxonomy | Purpose | Example |
|---|---|---|
tags | Topic labels | ["rust", "zola", "web"] |
categories | Broad grouping | (not actively used yet) |
series | Multi-part post sequences | ["Building My Website"] |
When a post declares tags = ["rust"] in its frontmatter, Zola automatically:
- Creates a page at
/tags/rust/listing all posts with that tag - Creates a page at
/tags/listing all tags
The same applies to series — /series/building-my-website/ is auto-generated.
File Naming
Post filenames don’t affect URLs when a slug is set in frontmatter. The file placeholder-test-post.md with slug = "hello-world" produces the URL /posts/hello-world/.
This means you can rename or reorganize files without breaking links — the slug is what determines the URL.
Templates & Overrides
The project overrides 6 template files from the Serene theme. Some are modifications of existing theme templates; others are entirely new.
Override vs Custom
| Template | Type | What it does |
|---|---|---|
templates/blog.html | Override | Blog listing page — changed back link to “Home” |
templates/post.html | Override | Individual post — added series link display |
templates/tags/list.html | Override | All tags page — changed back link to “Home” |
templates/tags/single.html | Override | Single tag page — changed back link to “Tags” |
templates/series/list.html | Custom | All series page — no theme equivalent exists |
templates/series/single.html | Custom | Single series page — no theme equivalent exists |
blog.html — Post Listing
Renders at /posts/. Lists all posts with title and date.
Key customization: The back link says ← Home instead of the theme’s generic text, so users know where they’re navigating.
The template supports two display modes controlled by section.extra.categorized:
categorized = false(current) — flat list of posts sorted by datecategorized = true— posts grouped by their first category
post.html — Individual Post
The most complex template. It handles:
- Syntax highlighting CSS — loads
giallo-light.cssorgiallo-dark.cssbased on theme - KaTeX math — only loaded when
page.extra.math = true(keeps pages lightweight) - Table of contents — auto-generated sidebar from h2/h3 headings
- Back-to-top button — appears on scroll
- Back navigation —
← Postslink - Post metadata — publish date, update date, tags with links
- Series link — “Part of series: Building My Website” with clickable link
- Outdate alert — configurable warning for old posts
- Post content — the rendered markdown
- Reactions & comments — both disabled currently
The series link (lines 117-124) is the most significant customization. Serene doesn’t natively display series information on posts. This block checks if a post has a series taxonomy and renders a link to the series listing page.
{% if page.taxonomies.series is defined %}
<div id="series-info" style="margin-top: 0.5em; font-size: 0.9em;">
{% for s in page.taxonomies.series -%}
{% set series_slugify = s | slugify -%}
Part of series: <a class="instant" href="{{ config.base_url ~ '/series/' ~ series_slugify }}">{{ s }}</a>
{%- endfor %}
</div>
{% endif %}
series/ — Fully Custom Templates
Serene has tag templates but no series templates. These were created from scratch, modeled after the tag templates:
series/list.html— Renders at/series/. Shows all series names as links.series/single.html— Renders at/series/building-my-website/. Lists all posts in a specific series with titles and dates.
Both use the same Tera template inheritance ({% extends "_base.html" %}), block structure, and CSS classes as the theme’s tag templates so they blend in visually.
tags/ — Modified Theme Templates
These override Serene’s tag templates with one change: descriptive back navigation.
tags/list.html: back link goes← Home(instead of generic)tags/single.html: back link goes← Tags(instead of generic)
Static Assets
| File | Purpose |
|---|---|
static/giallo-light.css | Syntax highlighting colors for light mode |
static/giallo-dark.css | Syntax highlighting colors for dark mode |
These exist because zola.toml sets style = "class" for syntax highlighting. Zola wraps code tokens in CSS classes, and these stylesheets provide the colors. The Serene theme’s JavaScript swaps between them when the user toggles dark/light mode.
sass/ — Style Overrides
Currently empty (contains only .gitkeep). To override Serene’s styles, create .scss files here matching the theme’s sass file names. Zola compiles yours instead of the theme’s.
Writing a Post
Quick Start
- Create a new
.mdfile incontent/posts/using the naming conventionYYYY-MM-DD-post-slug.md - Add the frontmatter (see template below)
- Write your content in markdown
- Run
zola serveto preview - Push to GitHub to deploy
File Naming Convention
Post files use a date prefix for easy chronological sorting:
content/posts/2026-02-11-building-my-learnbase-mvp.md
content/posts/2026-03-15-some-other-post.md
The date in the filename is for your convenience only — the slug field in frontmatter controls the actual URL, and the date field controls sort order. The filename itself doesn’t affect the published site.
Frontmatter Template
Copy this to start a new post:
+++
title = "Post Title"
slug = "post-title"
date = 2026-01-01
draft = false
[taxonomies]
tags = ["tag1", "tag2"]
series = ["Series Name"] # Optional
[extra]
series_order = 1 # Optional, position in series
+++
See Frontmatter Fields for the full field reference.
Writing Tips
Headings
Use ## (h2) and ### (h3) for section headings. These automatically appear in the table of contents sidebar. Don’t use # (h1) — the post title is already rendered as h1 by the template.
Code Blocks
Fenced code blocks with language hints get syntax highlighting:
```rust
fn main() {
println!("Hello, world!");
}
```
A “copy” button appears automatically on code blocks (controlled by the copy = true setting in the blog section config).
Images
Place images in the same directory as the post or in static/:

Images in static/ are referenced from the site root: .
Drafts
Set draft = true in frontmatter to hide a post from listings and feeds. It won’t appear on the site but will still be accessible by direct URL during local development.
Escaping Tera Template Syntax
If you write a post that includes Tera template code (e.g., documenting how your templates work), you must escape the {{ }} and {% %} syntax. Zola processes these as shortcodes/template directives even inside markdown code blocks.
Escape rules:
- Use
{{/*and*/}}instead of{{and}} - Use
{%/*and*/%}instead of{%and%}
For posts with many template code blocks, it may be more practical to describe the changes and link to the full files in the repository rather than inlining the entire template content.
See the Zola shortcodes documentation for details.
Content Format
Posts follow a two-part format:
- Reflections/commentary at the top — what you learned, why it matters, personal takeaways
- Tutorial-style steps below — reproducible instructions others (or an AI) can follow
Reflections Section
The reflections section is always written by the human author. When an LLM or AI tool is helping to generate or scaffold a post, the reflections section should be left blank with a placeholder comment for the author to fill in later:
## Reflections
<!-- TODO: Write reflections/commentary section -->
This section is where the personal voice lives — it’s not something that should be generated.
Tutorial Section
The tutorial section should be detailed enough that a human or AI could follow the steps to reproduce the project from scratch. Include exact commands, file contents, version numbers, and assumptions about the environment.
This format is informal and will evolve with use.
Using aipack for Post Generation
The aipack tool can generate post files from prompts. The pro@coder agent with knowledge_globs pointing to the frontmatter template works well for scaffolding posts.
# Preview (dry run)
aip run pro@coder -f "Create a blog post about X" --write_mode false
# Write the file
aip run pro@coder -f "Create a blog post about X" --write_mode true
Cost: approximately $0.03/run with Gemini Pro.
Creating a Series
Series group related posts into a multi-part sequence. They’re implemented using Zola’s taxonomy system.
How to Add a Post to a Series
In the post’s frontmatter, add the series name and order:
[taxonomies]
series = ["Building My Website"]
[extra]
series_order = 1
- Series name uses proper case in frontmatter (e.g., “Building My Website”). Zola auto-slugifies it for URLs →
/series/building-my-website/. series_orderdetermines the position within the series (1, 2, 3…).
What Zola Generates Automatically
When at least one post references a series, Zola creates:
| Page | URL | Content |
|---|---|---|
| Series index | /series/ | Lists all series names |
| Series detail | /series/building-my-website/ | Lists all posts in that series |
These pages use the custom templates in templates/series/.
What Appears on Post Pages
The post.html template checks if a post belongs to a series and displays:
Part of series: Building My Website
This appears below the post’s tags, linking readers to the full series listing.
Creating a New Series
No configuration is needed — just use a new series name in a post’s frontmatter:
[taxonomies]
series = ["New Series Name"]
Zola automatically creates the taxonomy pages. The series will appear on /series/ as soon as at least one published (non-draft) post references it.
Series Name Conventions
- Use proper case:
"Building My Website"not"building-my-website" - Keep names consistent across posts — exact string match is required
- The URL slug is auto-generated:
"Building My Website"→/series/building-my-website/
Customizing the Theme
The site uses the Serene theme, installed as a git submodule. Customizations are made by overriding theme files, not editing the theme directly.
The Override Pattern
Zola checks your project’s directories first, then falls back to the theme:
templates/blog.html ← Zola uses this (your override)
themes/serene/templates/blog.html ← Ignored when override exists
To customize any theme template:
- Find the original in
themes/serene/templates/ - Copy it to the same relative path in your project’s
templates/ - Modify your copy
The theme’s original stays untouched, so git submodule update --remote can pull theme updates without conflicts.
Template Customization
Modifying an Existing Template
Example: changing the back link text on the blog page.
- The theme’s
blog.htmlhas a generic back link - Copy
themes/serene/templates/blog.html→templates/blog.html - Change the back link:
<a id="back-link" href="{{ get_url(path="/") }}">← Home</a>
Adding a New Taxonomy Template
Serene provides tags/ templates but no series/ templates. To add series support:
- Create
templates/series/list.htmlandtemplates/series/single.html - Model them after the existing
tags/templates - Use the same
{% extends "_base.html" %}pattern and CSS classes
The templates will automatically be used for URLs matching /series/ and /series/<name>/.
Style Customization
SCSS Overrides
The sass/ directory is currently empty. To override Serene’s styles:
- Find the SCSS file in
themes/serene/sass/ - Create a file with the same name in your
sass/directory - Zola compiles yours instead of the theme’s
Inline Styles
For small, one-off tweaks, inline styles work in templates:
<div id="series-info" style="margin-top: 0.5em; font-size: 0.9em;">
This is acceptable for minor additions but should be moved to SCSS as customizations grow.
Theme Features (Controlled via Config)
These Serene features are toggled in content/posts/_index.md under [extra]:
| Feature | Setting | Current |
|---|---|---|
| Table of contents | toc | true |
| Code copy button | copy | true |
| Comments (Giscus) | comment | false |
| Outdate alert | outdate_alert | false |
Site-wide features are in zola.toml under [extra]:
| Feature | Setting | Current |
|---|---|---|
| Dark mode toggle | force_theme = false | Enabled |
| Emoji reactions | reaction | false |
| Footer credits | footer_credits | true |
Updating the Theme
# Pull latest changes from Serene
git submodule update --remote themes/serene
# Test locally
zola serve
# If everything works, commit the submodule update
git add themes/serene
git commit -m "Update Serene theme"
Caution: If Serene changes the structure of a template you’ve overridden, your override may break. Always test locally after updating.
Frontmatter Fields
Every post requires a TOML frontmatter block between +++ markers at the top of the file.
Required Fields
| Field | Type | Description | Example |
|---|---|---|---|
title | String | Post title (displayed on page and in listings) | "Hello World" |
slug | String | URL path segment — never change once published | "hello-world" |
date | Date | Publication date | 2026-02-04 |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
draft | Boolean | false | Set true to hide from listings and feeds |
updated | Date | — | Last updated date (shown if different from date) |
description | String | — | Used for meta description tag (SEO) |
summary | String | — | Short summary, used in meta tags if set |
Taxonomies
Declared under [taxonomies]:
| Field | Type | Description |
|---|---|---|
tags | Array of strings | Topic labels |
series | Array of strings | Multi-part post grouping |
categories | Array of strings | Broad categories (not actively used) |
[taxonomies]
tags = ["rust", "zola", "web"]
series = ["Building My Website"]
Extra Fields
Declared under [extra]:
| Field | Type | Default | Description |
|---|---|---|---|
series_order | Integer | — | Position within a series (1, 2, 3…) |
toc | Boolean | (inherits from section) | Override table of contents for this post |
comment | Boolean | (inherits from section) | Override comments for this post |
outdate_alert | Boolean | (inherits from section) | Override outdate alert for this post |
outdate_alert_days | Integer | (inherits from section) | Override days threshold |
math | Boolean | false | Enable KaTeX math rendering |
mermaid | Boolean | false | Enable Mermaid diagrams |
featured | Boolean | false | Highlight in post listings |
copy | Boolean | (inherits from section) | Override code copy button |
Full Example
+++
title = "Building My Website — Part 1"
slug = "building-my-website-part-1"
date = 2026-02-10
draft = false
[taxonomies]
tags = ["rust", "zola", "web"]
series = ["Building My Website"]
[extra]
series_order = 1
+++
Notes
- The
slugdetermines the URL:/posts/<slug>/. Changing it after publication breaks existing links. - The
titlecan be changed freely — it doesn’t affect URLs. - Fields under
[extra]that say “inherits from section” get their default fromcontent/posts/_index.md. You only need to set them per-post if you want to override the section default.
Site Configuration
All site configuration lives in zola.toml at the project root.
Core Settings
base_url = "https://mylearnbase.com"
title = "My Learn Base"
description = "A personal learning journal and portfolio"
default_language = "en"
theme = "serene"
| Field | Purpose |
|---|---|
base_url | Used to generate all absolute URLs (RSS, sitemaps, canonical links) |
title | Browser tab title, RSS feed title |
description | Default meta description for pages that don’t set their own |
theme | Which theme directory to use (under themes/) |
Build Settings
compile_sass = true
minify_html = false
build_search_index = false
generate_feeds = true
feed_filenames = ["rss.xml"]
| Field | Purpose | Current |
|---|---|---|
compile_sass | Compile .scss files into CSS | Enabled |
minify_html | Compress HTML output | Disabled (easier to debug) |
build_search_index | Generate client-side search index | Disabled |
generate_feeds | Generate RSS/Atom feeds | Enabled |
feed_filenames | Output filenames for feeds | rss.xml |
Taxonomies
taxonomies = [{ name = "tags" }, { name = "categories" }, { name = "series" }]
Each entry tells Zola to look for this field in post frontmatter and auto-generate listing pages. Each taxonomy needs matching templates in templates/<taxonomy_name>/.
Markdown Processing
[markdown]
external_links_target_blank = false
external_links_no_follow = true
external_links_no_referrer = true
smart_punctuation = false
| Field | Purpose |
|---|---|
external_links_target_blank | Open external links in new tab |
external_links_no_follow | Add rel="nofollow" to external links (SEO) |
external_links_no_referrer | Add rel="noreferrer" to external links (privacy) |
smart_punctuation | Convert "quotes" to curly quotes, -- to em dash |
Syntax Highlighting
[markdown.highlighting]
style = "class"
light_theme = "github-light"
dark_theme = "catppuccin-mocha"
style = "class" means code tokens get CSS classes instead of inline styles. This enables dynamic light/dark switching via the CSS files in static/ (giallo-light.css, giallo-dark.css).
URL Slugification
[slugify]
paths = "on"
taxonomies = "on"
anchors = "on"
Controls automatic URL-safe conversion. With all set to "on":
/posts/My Post Title→/posts/my-post-title- Series “Building My Website” →
/series/building-my-website ## My Heading→#my-heading
Theme-Specific Settings ([extra])
[extra]
sections = [{ name = "posts", path = "/posts", is_external = false }]
blog_section_path = "/posts"
back_link_text = "Back"
force_theme = false
footer_copyright = "© 2026"
footer_credits = true
not_found_error_text = "404 Not Found"
not_found_recover_text = "« back to home »"
reaction = false
These are not read by Zola itself — they’re a contract between your templates and the Serene theme. The theme’s templates check these values with {% if config.extra.reaction %} style conditionals.
| Field | Purpose |
|---|---|
sections | Navigation menu entries |
blog_section_path | Tells Serene where the blog section lives |
force_theme | Lock to light or dark (false = allow toggle) |
footer_copyright | Copyright text in footer |
footer_credits | Show “Powered by Zola & Serene” |
reaction | Enable emoji reactions on posts |
Cloudflare Deployment
Setup Summary
The site is deployed on Cloudflare Pages, connected to the GitHub repository. Every push to main triggers a rebuild and deploy.
Build Configuration
| Setting | Value |
|---|---|
| Framework preset | None (custom) |
| Production branch | main |
| Build command | See below |
| Build output directory | public |
Build Command
curl -sL https://github.com/getzola/zola/releases/download/v0.22.1/zola-v0.22.1-x86_64-unknown-linux-gnu.tar.gz | tar xz && ./zola build
Cloudflare Pages no longer pre-installs Zola, so the build command downloads it first. This adds a few seconds but is reliable.
To update Zola version: Change the version number in the URL (both the directory name and the tarball name).
Custom Domain
| Setting | Value |
|---|---|
| Domain | mylearnbase.com |
| DNS provider | Cloudflare (migrated from Porkbun) |
| HTTPS | Automatic via Cloudflare |
The domain’s nameservers were migrated to Cloudflare to enable tight integration with Pages. HTTPS certificates are provisioned and renewed automatically.
Known Issues
CSS Missing on Preview URLs
Cloudflare generates preview URLs for branches (e.g., abc123.mylearnbase.pages.dev). These may show broken styling because:
zola.tomlsetsbase_url = "https://mylearnbase.com"- Zola generates absolute URLs for CSS based on
base_url - The preview URL (
*.pages.dev) doesn’t match, so CSS paths break
Workaround: Use zola serve locally for visual verification instead of preview URLs.
Build Failures After Zola Updates
If a new Zola version introduces breaking changes:
- Pin to the known-working version in the build command (currently v0.22.1)
- Test the new version locally with
zola buildbefore updating the build command - Update the version in the build command only after local verification
Monitoring
Cloudflare Pages dashboard shows:
- Build logs (stdout/stderr from the build command)
- Deploy history with rollback capability
- Bandwidth and request metrics
No additional monitoring is configured.