Documentation Team Lead
Set up scalable documentation workflows with validation and CI/CD.
Scale your documentation with schemas, validation, remote sources, and CI/CD pipelines. Perfect for teams that need content governance.
Tip
Duration: ~75 min | Prerequisite: Experience managing documentation
Content Collections
Validate frontmatter with typed schemas
Content Collections
Define typed schemas for your content to ensure consistency and catch errors early.
Do I Need This?
No. Collections are optional. Your site works fine without them.
Use collections when:
- You want typos caught at build time, not in production
- Multiple people edit content and need guardrails
- You want consistent frontmatter across content types
Quick Setup
bengal collections init
This creates collections.pyat your project root. Edit it to uncomment what you need:
from bengal.collections import define_collection, BlogPost, DocPage
collections = {
"blog": define_collection(schema=BlogPost, directory="blog"),
"docs": define_collection(schema=DocPage, directory="docs"),
}
Done. Build as normal—validation happens automatically.
Built-in Schemas
Bengal provides schemas for common content types:
| Schema | Alias | Required Fields | Optional Fields |
|---|---|---|---|
BlogPost |
Post |
title, date | author, tags, draft, description, image, excerpt |
DocPage |
Doc |
title | weight, category, tags, toc, deprecated, description, since |
APIReference |
API |
title, endpoint | method, version, auth_required, rate_limit, deprecated, description |
Tutorial |
— | title | difficulty, duration, prerequisites, series, tags, order |
Changelog |
— | title, date | version, breaking, summary, draft |
Import any of these:
from bengal.collections import BlogPost, DocPage, APIReference, Tutorial, Changelog
# Or use short aliases:
from bengal.collections import Post, Doc, API
Custom Schemas
Define your own using Python dataclasses:
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class ProjectPage:
title: str
status: str # "active", "completed", "archived"
started: datetime
tech_stack: list[str] = field(default_factory=list)
github_url: str | None = None
collections = {
"projects": define_collection(
schema=ProjectPage,
directory="projects",
),
}
Validation Modes
By default, validation warns but doesn't fail builds:
⚠ content/blog/my-post.md
└─ date: Required field 'date' is missing
Strict Mode
To fail builds on validation errors, add tobengal.toml:
[build]
strict_collections = true
Lenient Mode (Extra Fields)
To allow frontmatter fields not defined in your schema:
define_collection(
schema=BlogPost,
directory="blog",
strict=False, # Don't reject unknown fields
allow_extra=True, # Store extra fields in _extra dict
)
With strict=False, unknown fields are silently ignored. Add allow_extra=True to preserve them in a _extraattribute on the validated instance.
CLI Commands
# List defined collections and their schemas
bengal collections list
# Validate content without building
bengal collections validate
# Validate specific collection
bengal collections validate --collection blog
Advanced Options
Custom File Pattern
By default, collections match all markdown files (**/*.md). To match specific files:
define_collection(
schema=BlogPost,
directory="blog",
glob="*.md", # Only top-level, not subdirectories
)
Migration Tips
Existing site with inconsistent frontmatter?
- Start with
strict=Falseto allow extra fields - Run
bengal collections validateto find issues - Fix content or adjust schema
- Enable
strict=Truewhen ready
Transform legacy field names:
def migrate_legacy(data: dict) -> dict:
if "post_title" in data:
data["title"] = data.pop("post_title")
return data
collections = {
"blog": define_collection(
schema=BlogPost,
directory="blog",
transform=migrate_legacy,
),
}
Remote Content
Collections work with remote content too. Use a loader instead of a directory:
from bengal.collections import define_collection, DocPage
from bengal.content.sources import github_loader
collections = {
"api-docs": define_collection(
schema=DocPage,
loader=github_loader(repo="myorg/api-docs", path="docs/"),
),
}
See Content Sources for GitHub, Notion, REST API loaders.
Seealso
- Content Sources — GitHub, Notion, REST API loaders
Content Sources
Fetch content from external sources
Remote Content Sources
Fetch content from GitHub, Notion, REST APIs, and more.
Do I Need This?
No. By default, Bengal reads content from local files. That works for most sites.
Use remote sources when:
- Your docs live in multiple GitHub repos
- Content lives in a CMS (Notion, Contentful, etc.)
- You want to pull API docs from a separate service
- You need to aggregate content from different teams
Quick Start
Install the loader you need:
pip install bengal[github] # GitHub repositories
pip install bengal[notion] # Notion databases
pip install bengal[rest] # REST APIs
pip install bengal[all-sources] # Everything
Update your collections.py:
from bengal.collections import define_collection, DocPage
from bengal.content.sources import github_loader
collections = {
# Local content (default)
"docs": define_collection(
schema=DocPage,
directory="content/docs",
),
# Remote content from GitHub
"api-docs": define_collection(
schema=DocPage,
loader=github_loader(
repo="myorg/api-docs",
path="docs/",
),
),
}
Build as normal. Remote content is fetched, cached, and validated like local content.
Available Loaders
GitHub
Fetch markdown from any GitHub repository:
from bengal.content.sources import github_loader
loader = github_loader(
repo="owner/repo", # Required: "owner/repo" format
branch="main", # Default: "main"
path="docs/", # Default: "" (root)
token=None, # Default: uses GITHUB_TOKEN env var
glob="*.md", # Default: "*.md" (file pattern to match)
)
For private repos, set GITHUB_TOKEN environment variable or pass tokendirectly.
Notion
Fetch pages from a Notion database:
from bengal.content.sources import notion_loader
loader = notion_loader(
database_id="abc123...", # Required: database ID from URL
token=None, # Default: uses NOTION_TOKEN env var
property_mapping={ # Map Notion properties to frontmatter
"title": "Name",
"date": "Published",
"tags": "Tags",
},
)
Setup:
- Create integration at notion.so/my-integrations
- Share your database with the integration
- Set
NOTION_TOKENenvironment variable
REST API
Fetch from any JSON API:
from bengal.content.sources import rest_loader
loader = rest_loader(
url="https://api.example.com/posts",
headers={"Authorization": "Bearer ${API_TOKEN}"}, # Env vars expanded
content_field="body", # JSON path to content
id_field="id", # JSON path to ID
frontmatter_fields={ # Map API fields to frontmatter
"title": "title",
"date": "published_at",
"tags": "categories",
},
)
Local (Explicit)
For consistency, you can also use an explicit local loader:
from bengal.content.sources import local_loader
loader = local_loader(
directory="content/docs",
glob="**/*.md",
exclude=["_drafts/*"],
)
Caching
Remote content is cached locally to avoid repeated API calls:
# Check cache status
bengal sources status
# Force refresh from remote
bengal sources fetch --force
# Clear all cached content
bengal sources clear
Cache behavior:
- Default TTL: 1 hour
- Cache directory:
.bengal/content_cache/ - Automatic invalidation when config changes
- Falls back to cache if remote unavailable
CLI Commands
# List configured content sources
bengal sources list
# Show cache status (age, size, validity)
bengal sources status
# Fetch/refresh from remote sources
bengal sources fetch
bengal sources fetch --source api-docs # Specific source
bengal sources fetch --force # Ignore cache
# Clear cached content
bengal sources clear
bengal sources clear --source api-docs
Environment Variables
| Variable | Used By | Description |
|---|---|---|
GITHUB_TOKEN |
GitHub loader | Personal access token for private repos |
NOTION_TOKEN |
Notion loader | Integration token |
| Custom | REST loader | Any${VAR}in headers is expanded |
Multi-Repo Documentation
A common pattern for large organizations:
from bengal.collections import define_collection, DocPage
from bengal.content.sources import github_loader, local_loader
collections = {
# Main docs (local)
"docs": define_collection(
schema=DocPage,
directory="content/docs",
),
# API reference (from API team's repo)
"api": define_collection(
schema=DocPage,
loader=github_loader(repo="myorg/api-service", path="docs/"),
),
# SDK docs (from SDK repo)
"sdk": define_collection(
schema=DocPage,
loader=github_loader(repo="myorg/sdk", path="docs/"),
),
}
Custom Loaders
ImplementContentSourcefor any content origin:
from collections.abc import AsyncIterator
from bengal.content.sources import ContentSource, ContentEntry
class MyCustomSource(ContentSource):
source_type = "my-api"
async def fetch_all(self) -> AsyncIterator[ContentEntry]:
items = await self._get_items()
for item in items:
yield ContentEntry(
id=item["id"],
slug=item["slug"],
content=item["body"],
frontmatter={"title": item["title"]},
source_type=self.source_type,
source_name=self.name,
)
async def fetch_one(self, id: str) -> ContentEntry | None:
item = await self._get_item(id)
if not item:
return None
return ContentEntry(
id=item["id"],
slug=item["slug"],
content=item["body"],
frontmatter={"title": item["title"]},
source_type=self.source_type,
source_name=self.name,
)
Zero-Cost Design
If you don't use remote sources:
- No extra dependencies installed
- No network calls
- No import overhead
- No configuration needed
Remote loaders are lazy-loaded only when you import them.
Other Content Sources
- Autodoc — Generate API docs from Python, CLI commands, and OpenAPI specs
Seealso
- Content Collections — Schema validation for any source
Validation
Content validation and health checks
Content Validation
Ensure content quality with health checks and automatic fixes.
Do I Need This?
Note
Skip this if: You manually check all links and content.
Read this if: You want automated quality assurance and CI/CD integration.
Validation Flow
Quick Start
# Run all checks
bengal validate
# Validate specific files
bengal validate --file content/page.md
# Only validate changed files (incremental)
bengal validate --changed
# Verbose output (show all checks)
bengal validate --verbose
# Show quality suggestions
bengal validate --suggestions
# Watch mode (validate on file changes)
bengal validate --watch
# Preview fixes
bengal fix --dry-run
# Apply safe fixes
bengal fix
# Apply all fixes including confirmations
bengal fix --all
# Fix specific validator only
bengal fix --validator Directives
Fixes common issues:
- Unclosed directive fences
- Invalid directive options
- YAML syntax errors
# Fail build on issues
bengal build --strict
# Validate and exit with error code
bengal validate
The --strictflag makes warnings into errors.
Built-in Checks
| Check | What it validates |
|---|---|
links |
Internal and external links work |
assets |
Asset references exist |
config |
Configuration is valid |
navigation |
Menu structure is correct |
rendering |
Templates render without errors |
cross_ref |
Cross-references are valid |
taxonomy |
Tags and categories are consistent |
directives |
MyST directive syntax is correct |
anchors |
Heading IDs are unique and valid |
Custom Validators
Create project-specific rules by extendingBaseValidator:
# validators/custom.py
from bengal.health.base import BaseValidator
from bengal.health.report import CheckResult
class RequireAuthorValidator(BaseValidator):
"""Validator that checks for author field in frontmatter."""
name = "Author Required"
description = "Ensures all pages have an author field"
def validate(self, site, build_context=None):
results = []
for page in site.pages:
if not page.metadata.get("author"):
results.append(CheckResult.error(
f"Missing author in {page.source_path}",
recommendation="Add 'author: Your Name' to frontmatter",
details=[str(page.source_path)],
))
return results
Tip
CI integration: Addbengal validate to your CI pipeline to catch issues before deployment. Use --verbose to see all checks, or --suggestionsfor quality recommendations.
Validate and Fix
Run health checks and automatically fix common content issues
Validate and Fix Content
Bengal's health system validates content and can automatically fix many common issues.
Quick Start
# Run all validators
bengal validate
# Preview auto-fixes
bengal fix --dry-run
# Apply safe fixes
bengal fix
Validation Commands
Basic Validation
# Validate entire site
bengal validate
# Validate specific file
bengal validate --file content/docs/getting-started.md
# Validate changed files only (git-aware)
bengal validate --changed
# Verbose output - show all checks, not just errors
bengal validate --verbose
Validate During Build
# Fail build on validation errors (recommended for CI)
bengal build --strict
# Validate templates before building
bengal build --validate
Check Specific Areas
# Link checking (internal + external)
bengal health linkcheck
# Internal links only (fast)
bengal health linkcheck --internal-only
# External links only
bengal health linkcheck --external-only
# Exclude specific URL patterns
bengal health linkcheck --exclude "^/api/preview/"
Available Validators
Bengal includes validators organized by phase:
Core Validators
| Validator | Checks | Common Issues |
|---|---|---|
| Links | Internal/external links | Broken links, moved pages |
| Directives | MyST directive syntax | Unclosed fences, invalid options |
| Configuration | Site configuration | Invalid YAML, missing required fields |
| Navigation | Page nav (next/prev, breadcrumbs) | Broken navigation links |
| Navigation Menus | Menu structure and links | Missing menu items, broken links |
Content Quality Validators
| Validator | Checks | Common Issues |
|---|---|---|
| Anchors | Heading IDs,[[#anchor]]refs |
Duplicate IDs, broken anchor links |
| Cross-References | Internal page references | Invalid page references |
| Taxonomies | Tags/categories | Orphan terms, inconsistent naming |
| Connectivity | Page link graph | Orphan pages, poor connectivity |
Build & Output Validators
| Validator | Checks | Common Issues |
|---|---|---|
| Rendering | HTML output quality | Template errors, undefined variables |
| Output | Generated pages, assets | Missing output, structure errors |
| Asset URLs | Asset references in HTML | Broken asset paths, fingerprinting mismatches, case issues |
| Performance | Build metrics | Slow builds, large pages |
| URL Collisions | Duplicate output paths | Multiple pages writing to same URL |
Production Validators
| Validator | Checks | Common Issues |
|---|---|---|
| Sitemap | sitemap.xml validity | SEO issues, missing pages |
| RSS Feed | RSS/Atom feed quality | Schema compliance, missing fields |
| Fonts | Font downloads, CSS | Missing fonts, subsetting issues |
| Ownership Policy | Reserved namespaces | Content in system directories |
Validation Output
$ bengal validate
🔍 Running health checks...
✅ Config: 3 checks passed
✅ Navigation: 5 checks passed
⚠️ Links: 2 warnings
→ content/docs/old-page.md references moved page
→ content/api/client.md has broken anchor #deprecated
❌ Directives: 1 error
→ content/tutorials/setup.md:45 - Unclosed code fence
Summary: 1 error, 2 warnings, 8 passed
Auto-Fix
Preview Fixes
Always preview before applying:
bengal fix --dry-run
Output:
🔧 Auto-Fix (dry run)
Found 3 fix(es):
• 2 safe (can be applied automatically)
• 1 requires confirmation
Safe fixes:
• Fix unclosed code fence at content/tutorials/setup.md:45
• Add missing closing tag at content/api/reference.md:112
Would apply 2 fixes. Run without --dry-run to apply.
Apply Fixes
# Apply safe fixes only (default)
bengal fix
# Apply all fixes including those needing confirmation
bengal fix --all
# Ask before each fix
bengal fix --confirm
# Fix specific validator only
bengal fix --validator Directives
Fix Categories
| Safety | Description | Auto-Apply |
|---|---|---|
| Safe | Reversible, no side effects | ✅ Yes |
| Confirm | May have side effects | Needs--all or --confirm |
| Unsafe | Requires manual review | Never auto-applied |
Common Auto-Fixes
Directives:
- Missing closing fences (
:::or```) - Incorrect fence syntax
- Invalid directive options
Links:
- Update paths for renamed files
- Fix relative link depth
- Correct asset references
Frontmatter:
- Fix YAML syntax errors
- Add missing required fields
- Correct date formats
CI/CD Integration
GitHub Actions
name: Validate Docs
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
- name: Install Bengal
run: pip install bengal
- name: Validate Content
run: bengal validate --verbose
- name: Check Links
run: bengal health linkcheck --internal-only
- name: Build (strict mode)
run: bengal build --strict
Pre-Commit Hook
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: bengal-validate
name: Validate Bengal content
entry: bengal validate --changed
language: system
pass_filenames: false
Failing on Errors
# Strict mode - treats warnings as errors
bengal build --strict
# Validate and exit with error code
bengal validate && echo "Validation passed" || echo "Validation failed"
Custom Validators
Create project-specific validation rules:
# validators/require_author.py
from bengal.health.base import BaseValidator
from bengal.health.report import CheckResult, Severity
class RequireAuthorValidator(BaseValidator):
"""Ensure all blog posts have an author."""
name = "Author Required"
description = "Validates that blog posts have author metadata"
def validate(self, site, build_context=None):
results = []
for page in site.pages:
# Only check blog posts
if not page.section or page.section.name != "blog":
continue
if not page.metadata.get("author"):
results.append(CheckResult(
severity=Severity.ERROR,
message=f"Missing author in {page.source_path}",
recommendation="Add 'author: Your Name' to frontmatter",
file_path=page.source_path,
line_number=1,
))
return results
Register Custom Validator
# bengal.py (site configuration)
from validators.require_author import RequireAuthorValidator
validators = [
RequireAuthorValidator(),
]
Severity Levels
| Severity | Meaning | Strict Mode |
|---|---|---|
ERROR |
Must fix | Fails build |
WARNING |
Should fix | Fails build |
INFO |
Consider fixing | Passes |
DEBUG |
Developer info | Hidden |
Configuration
Disable Validators
# config/_default/health.yaml
health:
validators:
links:
enabled: true
external: false # Skip external link checks
performance:
enabled: false # Disable performance checks
Link Check Settings
health:
linkcheck:
external: true
max_concurrency: 10
timeout: 30
retries: 2
exclude:
- "https://example.com/private/*"
exclude_domains:
- "localhost"
- "*.internal.company.com"
Validation Profiles
Different validation for different contexts:
# Full validation (CI)
bengal validate --verbose
# Quick validation (local dev)
bengal validate --changed
# Pre-deploy validation
bengal health linkcheck && bengal build --strict
Available profiles:
| Profile | Use Case | Checks |
|---|---|---|
writer |
Content authors | Fast, content-focused |
theme-dev |
Theme developers | Template validation |
developer |
Full development | All checks, strict |
# Writer profile (fast, less strict) - default
bengal validate --profile writer
# Theme developer profile
bengal validate --profile theme-dev
# Developer profile (strict, all checks)
bengal validate --profile developer
Troubleshooting
False Positives
Suppress specific check codes via CLI:
# Ignore specific check codes
bengal validate --ignore H101 --ignore H202
Or in page frontmatter:
---
validate:
skip:
- links # Skip link validation for this page
- anchors # Skip anchor validation
---
Or via configuration:
# config/_default/health.yaml
health:
ignore_patterns:
- "content/drafts/**"
- "content/archived/**"
Slow External Link Checks
# Check internal links only (fast)
bengal health linkcheck --internal-only
# Exclude specific URL patterns
bengal health linkcheck --exclude "^/api/preview/"
Tip
Advanced options like--max-concurrency, --timeout, and --exclude-domainare available but hidden from help output. Use them when needed for fine-tuning.
Validation Cache
Validation uses the build cache for incremental checks:
# Clear build cache (includes validation state)
bengal clean --cache
# Force full re-validation by clearing cache first
bengal clean --cache && bengal validate
# Use incremental validation (only changed files)
bengal validate --incremental
Quick Reference
# Validate
bengal validate # Run all validators
bengal validate --changed # Only changed files
bengal validate --incremental # Use cached validation state
bengal validate --verbose # Show all checks
bengal validate --suggestions # Show quality suggestions
bengal validate --file path.md # Validate specific file
bengal validate --profile writer # Use writer profile (fast)
bengal validate --ignore H101 # Ignore specific check codes
bengal validate --watch # Watch mode (experimental)
bengal validate --templates # Validate template syntax
# Auto-fix
bengal fix --dry-run # Preview fixes
bengal fix # Apply safe fixes
bengal fix --all # Apply all fixes
bengal fix --confirm # Ask before each fix
bengal fix --validator Directives # Fix specific validator
# Link checking
bengal health linkcheck # Check all links
bengal health linkcheck --internal-only # Internal only (fast)
bengal health linkcheck --external-only # External only
bengal health linkcheck --format json # JSON output for CI
# Build
bengal build --strict # Fail on warnings
bengal build --validate # Validate before building
# Clean
bengal clean --cache # Clear build cache
Seealso
- Validation Overview — Content quality strategies
- CLI Cheatsheet — Quick command reference
- Troubleshooting — Common issues
Analysis
Site structure analysis tools
Site Analysis
Analyze your site's structure to improve navigation and discoverability.
Do I Need This?
Note
Skip this if: Your site has under 50 pages.
Read this if: You have a large site and want to optimize internal linking and find orphan pages.
Analysis Tools
Quick Start
# Unified connectivity report
bengal graph report
# Brief output for CI
bengal graph report --brief
# CI mode with thresholds
bengal graph report --ci --threshold-isolated 5
Shows:
- Connectivity distribution
- Isolated/lightly-linked pages
- Bridge pages
- Actionable recommendations
# Find isolated pages
bengal graph orphans
# Find lightly-linked pages
bengal graph orphans --level lightly
# JSON output for processing
bengal graph orphans --format json
# Paths only (for scripting)
bengal graph orphans --format paths
Identifies pages by connectivity level:
- 🔴 Isolated (score < 0.25)
- 🟠 Lightly linked (0.25-1.0)
- 🟡 Adequately linked (1.0-2.0)
- 🟢 Well-connected (≥ 2.0)
bengal graph pagerank --top-n 20
Identifies:
- Most important pages
- Underlinked valuable content
- Navigation priorities
Use Cases
| Goal | Command | Output |
|---|---|---|
| Get site health overview | bengal graph report |
Connectivity distribution and recommendations |
| Find isolated pages | bengal graph orphans |
Pages needing attention |
| Find bridge pages | bengal graph bridges |
Navigation bottlenecks |
| Identify key content | bengal graph pagerank --top-n 20 |
Pages ranked by importance |
Tip
Start withbengal graph report for a unified view of your site structure. Use --cimode in pipelines to fail builds when connectivity thresholds are exceeded.
Configuration
Configuring Bengal with bengal.toml
Configuration
Control Bengal's behavior throughbengal.tomland environment-specific settings.
Configuration Methods
Bengal loads configuration from either theconfig/ directory (preferred) OR bengal.toml (legacy/simple). If config/ exists, bengal.tomlis ignored.
Overrides apply in order: Base Config → Environment Overrides → CLI Flags.
Quick Start
# bengal.toml
[site]
title = "My Site"
baseurl = "https://example.com"
language = "en"
[build]
output_dir = "public"
[theme]
name = "default"
Configuration Patterns
Best for small sites:
# bengal.toml - everything in one place
[site]
title = "My Blog"
[build]
output_dir = "public"
[theme]
name = "default"
Best for larger sites:
config/
├── _default/
│ ├── site.yaml
│ ├── build.yaml
│ └── theme.yaml
└── environments/
├── production.yaml
└── staging.yaml
Environment Overrides
Run with different settings per environment:
bengal build --environment production
# config/environments/production.yaml
site:
baseurl: "https://example.com"
build:
minify_html: true
strict_mode: true
assets:
fingerprint: true
Tip
Best practice: Keep development settings inbengal.toml, add production overrides in config/environments/production.yaml.
Build Options Reference
Key[build]configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
output_dir |
string | "public" |
Directory for generated files |
minify_html |
bool | true |
Minify HTML output |
validate_templates |
bool | false |
Proactive template syntax validation |
validate_build |
bool | true |
Post-build validation checks |
validate_links |
bool | true |
Check for broken internal links |
strict_mode |
bool | false |
Fail build on any error or warning |
fast_mode |
bool | false |
Enable maximum performance optimizations |
Note
Incremental builds are automatic. First build is full (creates cache), subsequent builds only rebuild changed content. Use--no-incrementalCLI flag for debugging or CI clean builds.
Asset Options
Configure asset processing in the[assets]section:
| Option | Type | Default | Description |
|---|---|---|---|
minify |
bool | true |
Minify CSS/JS assets |
optimize |
bool | true |
Optimize images |
fingerprint |
bool | true |
Add content hash to asset URLs |
[assets]
minify = true
optimize = true
fingerprint = true
Template Validation
Enablevalidate_templatesto catch template syntax errors early during builds:
[build]
validate_templates = true
When enabled, Bengal validates all templates (HTML/XML) in your template directories before rendering. This provides early feedback on syntax errors, even for templates that might not be used by every page.
Enable template validation during development for immediate feedback:
[build]
validate_templates = true
Combine with strict mode in CI pipelines to fail builds on template errors:
[build]
validate_templates = true
strict_mode = true
When to enable:
- During active theme development
- In CI/CD pipelines
- When debugging template issues
What it catches:
- Template syntax errors (unclosed tags, invalid filters) in Kida and Jinja2
- Unknown filter names
- Template assertion errors
Note
Template validation adds a small overhead to build time. For large sites, consider enabling it only in development and CI environments.
Deployment
Deploy your Bengal site to production
Deploy Your Site
Bengal generates static HTML, CSS, and JavaScript files. This means you can host your site anywhere that serves static files (e.g., GitHub Pages, Netlify, Vercel, AWS S3, Nginx).
The Production Build
When you are ready to ship, run the build command:
bengal build --environment production
This command:
- Loads configuration from
config/environments/production.yaml(if it exists) - Minifies HTML output (enabled by default)
- Generates the
public/directory with your complete site
Common Build Flags
| Flag | Description | Use Case |
|---|---|---|
--environment production |
Loads production config overrides. | Always use for shipping. |
--strict |
Fails the build on template errors. | Highly Recommended for CI/CD. |
--clean-output |
Cleans thepublic/directory before building. |
Recommended to avoid stale files. |
--fast |
Maximum performance (quiet output, full parallelism). | Fast CI builds. |
--verbose |
Shows detailed build output (phase timing, stats). | Useful for debugging CI failures. |
Example full command for CI:
bengal build --environment production --strict --clean-output
GitHub Pages
Deploy using GitHub Actions. Create.github/workflows/deploy.yml:
name: Deploy to GitHub Pages
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
- name: Install Bengal
run: pip install bengal
- name: Build Site
run: bengal build --environment production --strict --clean-output
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './public'
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Netlify
Create anetlify.tomlin your repository root:
[build]
publish = "public"
command = "bengal build --environment production"
[build.environment]
PYTHON_VERSION = "3.14"
Vercel
Configure your project:
- Build Command:
bengal build --environment production - Output Directory:
public - Ensure your
requirements.txtincludesbengal.
Automatic Platform Detection
Bengal auto-detects your deployment platform and configuresbaseurlautomatically:
| Platform | Detection | Baseurl Source |
|---|---|---|
| GitHub Pages | GITHUB_ACTIONS=true |
Inferred fromGITHUB_REPOSITORY |
| Netlify | NETLIFY=true |
URL or DEPLOY_PRIME_URL |
| Vercel | VERCEL=true |
VERCEL_URL |
You can override auto-detection with theBENGAL_BASEURLenvironment variable:
BENGAL_BASEURL="https://custom-domain.com" bengal build --environment production
Pre-Deployment Checklist
Before you merge to main or deploy:
- Run
bengal config doctor: Checks for common configuration issues. - Run
bengal build --strictlocally: Ensures no template errors. - Run
bengal validate: Runs health checks on your site content. - Check
config/environments/production.yaml: Ensure yourbaseurlis set to your production domain.
# config/environments/production.yaml
site:
baseurl: "https://example.com"
Seealso
- Configuration — Environment-specific settings
- Performance — Optimize build times
Section 8: Page Not Found
Could not find page:docs/tutorials/automate-with-github-actions