A working template for writing technical books with executable code examples, tested snippets, infrastructure via Docker Compose, and automated CI validation.
Read the full write-up:
- Writing Technical Books in 2026: Tools, Workflows, and the Case for Executable Code
- Executable Code and Diagrams in Technical Books: A Practical Setup Guide
# Option A: Fork on GitHub (recommended — keeps link to upstream for updates)
# Click "Fork" on https://github.com/jagatsingh/quarto-book-template
# Option B: Clone and re-initialize
gh repo create my-book --private
git clone https://github.com/jagatsingh/quarto-book-template my-book
cd my-book
git remote set-url origin https://github.com/yourusername/my-book.git
git push -u origin mainEdit _quarto.yml:
book:
title: "Your Book Title"
subtitle: "A subtitle for your book"
author: "Your Name"
date: today
chapters:
- index.qmd
- chapters/01-your-first-chapter.qmd
- chapters/02-your-second-chapter.qmd- Edit
index.qmd— your book's introduction - Delete the example chapters, create your own in
chapters/ - Replace
code/ch01/andcode/ch02/with your own tested code - Update
docker-compose.ymlwith the infrastructure your book needs - Update
scripts/start-infra.shandstop-infra.shaccordingly
uv sync
pre-commit install
QUARTO_PYTHON=.venv/bin/python quarto render- Executable code blocks in Chapter 2 that connect to a real Postgres database and capture live output
- Snippet extraction in Chapter 1 from a tested code repository with CI validation
- Mermaid diagrams rendered inline in chapters
- Cross-references (
@sec-,@fig-,@tbl-,@lst-) that survive chapter reordering - Snippet linter that catches broken references and dead snippets
- Docker Compose infrastructure started automatically via Quarto pre-render hooks
- GitHub Actions CI that validates everything: snippets, tests, and full book render
- uv for fast, reproducible Python dependency management
# Clone the template
gh repo clone jagatsingh/quarto-book-template
cd quarto-book-template
# Install Python dependencies
uv sync
# Install pre-commit hooks
pre-commit install
# Start infrastructure, render book
quarto render
# The pre-render hook starts Docker Compose automatically.
# Output is in _book/ (HTML) and _book/*.pdf (PDF)._quarto.yml # Book config with pre/post render hooks
chapters/
index.qmd # Introduction
01-infrastructure-setup.qmd # Snippets from tested repo (eval: false)
02-working-with-data.qmd # Inline executable code (eval: true)
code/
ch01/
schema.py # Schema creation with snippet markers
seed.py # Seed data with snippet markers
tests/
test_ch01.py # Tests for infrastructure code
test_ch02.py # Tests for query examples
diagrams/ # Draw.io/Excalidraw sources (add .drawio files here)
scripts/
start-infra.sh # Pre-render: docker compose up + schema + seed
stop-infra.sh # Post-render: docker compose down
lint_snippets.py # CI: validate snippet references
export-diagrams.sh # CI: Draw.io → SVG export
docker-compose.yml # Postgres 17 (pinned by SHA256 digest)
pyproject.toml # Python dependencies (managed by uv)
.pre-commit-config.yaml # Pre-commit hooks (lint, format, secrets, snippets)
renovate.json5 # Dependency updates with 7-day delay
.github/workflows/book.yml # Full CI pipeline
# Render the book (starts infra automatically)
quarto render
# Render with live preview (watches for changes)
quarto preview
# Run tests against running infrastructure
docker compose up -d --wait
uv run python code/ch01/schema.py
uv run python code/ch01/seed.py
uv run pytest -v
docker compose down
# Lint snippet references
uv run python scripts/lint_snippets.py
# Export Draw.io diagrams to SVG
scripts/export-diagrams.shAll PDF customization goes in the typst-pdf section of _quarto.yml.
format:
typst-pdf:
fontsize: 11pt # Default is 10pt. Common book sizes: 10pt, 11pt, 12ptformat:
typst-pdf:
mainfont: "Linux Libertine" # Serif font for body text
sansfont: "Linux Biolinum" # Sans font for headings
monofont: "Fira Code" # Monospace font for code blocksCommon combinations for technical books:
| Style | mainfont | monofont | Notes |
|---|---|---|---|
| Classic | Linux Libertine | Fira Code | Open source, elegant |
| Modern | Inter | JetBrains Mono | Clean, highly legible |
| O'Reilly style | Georgia | Consolas | Familiar to tech readers |
| Default | (Typst default) | (Typst default) | Works well out of the box |
format:
typst-pdf:
papersize: a4 # Options: a4, us-letter, us-trade (6x9in)
margin:
x: 1in # Left and right margins
y: 1in # Top and bottom margins
# Or set individually:
# top: 1in
# bottom: 1in
# left: 1.2in
# right: 0.8inCommon book sizes:
| Format | papersize | Typical use |
|---|---|---|
| A4 | a4 |
International standard, online distribution |
| US Letter | us-letter |
North American printing |
| US Trade | us-trade |
Standard book size (6"x9"), Amazon KDP |
format:
typst-pdf:
code-overflow: wrap # Wrap long lines instead of clipping
code-line-numbers: true # Show line numbers in code blocks
code-block-bg: "#f5f5f5" # Background color for code blocksformat:
typst-pdf:
fig-width: 5.5 # Default figure width in inches
fig-height: 3.5 # Default figure height in inchesOverride per-diagram in the .qmd file:
```{mermaid}
%%| fig-width: 4
%%| fig-height: 3
flowchart TD
A --> B --> C
```book:
toc: true # Enable TOC (default for books)
toc-depth: 3 # How many heading levels to includeformat:
typst-pdf:
papersize: us-trade
fontsize: 11pt
mainfont: "Linux Libertine"
monofont: "Fira Code"
margin:
x: 0.8in
y: 1in
fig-width: 4.5
fig-height: 3
code-overflow: wrap
code-line-numbers: trueFor advanced Typst customization (custom headers, footers, title pages), see the Quarto Typst documentation.
-
Create
chapters/03-new-chapter.qmdwith YAML frontmatter:--- title: "Your Chapter Title" id: sec-your-chapter ---
-
Add it to
_quarto.ymlunderbook.chapters -
Use
@sec-your-chaptercross-references from other chapters -
For infrastructure-dependent code: add snippet markers in
code/ch03/, reference witheval: false -
For self-contained code: write inline executable
{python}blocks -
Run
QUARTO_PYTHON=.venv/bin/python quarto renderto verify
Mermaid (preferred for simple diagrams): write inline in .qmd files:
```{mermaid}
%%| label: fig-my-diagram
%%| fig-cap: "Description"
%%| fig-width: 4
flowchart TD
A --> B --> C
```Draw.io (for complex architecture diagrams):
- Create
diagrams/my-diagram.drawio - Run
scripts/export-diagrams.shto generate SVG - Reference in chapter:
{#fig-my-diagram}
Tips for PDF-friendly diagrams:
- Use
flowchart TD(top-down) instead ofLR(left-right) for narrow pages - Set
fig-width: 4orfig-width: 4.5to keep diagrams within margins - Keep participant names short in sequence diagrams (
C as Client, notparticipant Client) - ERD diagrams with many columns can be tall — consider splitting into multiple diagrams
The GitHub Action (.github/workflows/book.yml) runs on every push and PR:
- Pre-commit checks — linting, formatting, secrets detection
- Lint snippets — broken references fail the build
- Export diagrams — regenerate SVGs from Draw.io sources
- Start Postgres — Docker Compose with health check
- Run tests — pytest validates all code examples
- Render book — Quarto executes inline code, resolves snippet includes
- Upload artifacts — PDF and HTML available as build artifacts
- Create release — on push to main, publishes PDF as a GitHub Release
When Renovate or Dependabot bumps a dependency, the full pipeline runs — catching breakage before merge.
| Extension | Purpose |
|---|---|
| Quarto | .qmd syntax highlighting, preview, cell execution, YAML intellisense |
| Mermaid Markdown Syntax Highlighting | Syntax highlighting for Mermaid code blocks |
| Draw.io Integration | Edit .drawio files directly in VS Code |
| Python | Python language support, debugging, linting |
| Jupyter | Required by the Quarto extension for executing Python cells |
| Docker | Docker Compose management, container logs |
| YAML | YAML validation for _quarto.yml and docker-compose.yml |
| markdownlint | Markdown linting (also catches issues in .qmd files) |
| GitLens | Git blame, history, and diff for tracking chapter changes |
Add to your workspace .vscode/settings.json:
{
"files.associations": {
"*.qmd": "quarto"
},
"[quarto]": {
"editor.wordWrap": "on",
"editor.rulers": [100]
},
"quarto.render.renderOnSave": false
}- uv — fast Python package manager, replaces pip/venv
- pre-commit — git hook manager for automated checks on every commit
- Quarto CLI — the book build tool
- Draw.io Desktop — standalone diagram editor (also available as VS Code extension above)
- Excalidraw — browser-based hand-drawn style diagrams
- D2 — text-based diagramming for complex layouts
This template includes pre-commit hooks that run automatically on every git commit:
# Install hooks (one-time setup)
pre-commit install
# Run all hooks manually
pre-commit run --all-files| Hook | What it does |
|---|---|
| trailing-whitespace | Strips trailing whitespace from code files |
| end-of-file-fixer | Ensures files end with a newline |
| check-yaml | Validates YAML syntax (_quarto.yml, docker-compose.yml) |
| check-added-large-files | Blocks files over 500KB (catches accidentally committed binaries) |
| check-merge-conflict | Prevents committing unresolved merge conflict markers |
| detect-private-key | Blocks accidental commits of private keys |
| gitleaks | Scans for hardcoded secrets, API keys, and tokens |
| ruff | Python linting and auto-fixing (replaces flake8, isort, pyupgrade) |
| ruff-format | Python code formatting (replaces black) |
| lint-snippets | Validates all snippet references resolve correctly (custom local hook) |
The snippet linter runs on every commit that touches .qmd or .py files, catching broken references before they reach CI.
Technical books have a larger attack surface for quality issues than typical code:
- Secrets in code examples — it's easy to paste a real connection string into a chapter.
gitleaksanddetect-private-keycatch this. - Stale snippets — the snippet linter prevents broken
includedirectives from reaching readers. - Inconsistent formatting —
ruff-formatensures all Python examples in the book follow the same style. Readers notice when Chapter 3 uses single quotes and Chapter 7 uses double quotes. - Large files — accidentally committing a rendered PDF or database dump bloats the repo permanently. The size check prevents this.
This template includes supply chain protections: 7-day Renovate delay on dependency upgrades (renovate.json5), Docker images pinned by SHA256 digest, and locked dependencies via uv.lock. See the full write-up on supply chain security for the rationale and implementation details.
MIT