Versioning Expert
Maintain multiple versions of documentation for different releases.
Ship new releases without breaking your docs. This track teaches you to maintain multiple documentation versions, letting users access the exact docs that match their installed version.
Tip
Duration: ~45 min | Prerequisite: Experience with Bengal content organization
Versioned Documentation
Serve multiple versions of your documentation from a single site
Versioned Documentation
Bengal supports versioned documentation, allowing you to maintain multiple versions of your docs (e.g., v1, v2, v3) and let users switch between them.
Why Version Your Docs?
- API libraries: Users on older versions need docs that match their installed version
- Breaking changes: Major releases often have different APIs or workflows
- Enterprise support: Some users stay on LTS versions for years
- Migration guides: Link between versions to help users upgrade
Two Modes
Bengal supports two versioning modes:
Folder Mode (Default)
Store versions in_versions/directories:
content/
├── docs/ # Latest version (v3)
│ └── guide.md
└── _versions/
├── v2/
│ └── docs/
│ └── guide.md
└── v1/
└── docs/
└── guide.md
Best for: Most projects, simple setup, easy to understand.
Git Mode
Build from Git branches or tags:
versioning:
mode: git
git:
branches:
- name: main
latest: true
- pattern: "release/*"
strip_prefix: "release/"
Best for: Projects that already use release branches, CI/CD pipelines.
Quick Start (Folder Mode)
1. Enable Versioning
# bengal.yaml
versioning:
enabled: true
versions:
- id: v3
latest: true
- id: v2
- id: v1
sections:
- docs
2. Create a Version Snapshot
# Snapshot current docs as v2 before making breaking changes
bengal version create v2
This copies docs/ → _versions/v2/docs/.
3. Build
bengal build
Your site now has:
/docs/guide/→ v3 (latest)/docs/v2/guide/→ v2/docs/v1/guide/→ v1
URL Structure
| Version | URL Pattern | Example |
|---|---|---|
| Latest | /docs/page/ |
/docs/installation/ |
| Older | /docs/{version}/page/ |
/docs/v2/installation/ |
The latest version has no version prefix in URLs—cleaner for most users.
Features
Version Selector
A dropdown appears in the documentation sidebar when versioning is enabled, letting users switch between versions. Bengal's version selector includes smart fallback: if a page doesn't exist in the target version, it navigates to the nearest equivalent (section index or version root) instead of showing a 404.
The default theme includes the version selector automatically. To customize, overridepartials/version-selector.htmlin your theme.
Version Banners
Older versions automatically display a warning banner linking to the latest docs. Customize the banner per version:
versions:
- id: v2
banner:
type: warning # or: info, danger
message: "You're viewing docs for v2. See the latest version."
Banner types: info (blue), warning (yellow), danger(red for deprecated versions).
Cross-Version Links
Link to specific versions from any page:
See the [[v2:docs/migration|v2 Migration Guide]] for upgrade steps.
Version Directives
Mark when features were added, deprecated, or changed:
:::{since} v2.0
This feature was added in version 2.0.
:::
:::{deprecated} v3.0
Use `new_function()` instead.
:::
:::{changed} v2.5
Default timeout changed from 30s to 60s.
:::
SEO Optimization
Bengal automatically handles SEO for versioned docs:
- Canonical URLs: Older versions point to latest version (prevents duplicate content penalties)
- Sitemap priorities: Latest version pages get priority 0.8; older versions get 0.3
- No-index option: Mark deprecated versions as
noindexto remove from search entirely
CLI Commands
# List all versions
bengal version list
# List versions as JSON (for scripting)
bengal version list --format json
# Show version details
bengal version info v2
# Create a new version snapshot
bengal version create v2 --label "2.0 LTS"
# Preview what create would do
bengal version create v2 --dry-run
# Compare versions
bengal version diff v2 v3
# Compare git branches directly
bengal version diff main release/0.1.6 --git
Next Steps
- Folder Mode Setup — Step-by-step guide
- Git Mode Setup — Build from branches
- Cross-Version Links — Link between versions
- Version Directives — Mark version-specific content
Folder Mode Setup
Set up versioned documentation using folder-based versioning
Folder Mode Setup
Folder mode is the simplest way to version your documentation. Each version lives in its own directory.
Directory Structure
your-site/
├── bengal.yaml
├── content/
│ ├── docs/ # Latest version (v3)
│ │ ├── _index.md
│ │ ├── installation.md
│ │ └── guide/
│ │ └── getting-started.md
│ └── _versions/
│ ├── v2/
│ │ └── docs/ # v2 content
│ │ ├── _index.md
│ │ ├── installation.md
│ │ └── guide/
│ │ └── getting-started.md
│ └── v1/
│ └── docs/ # v1 content
│ └── ...
Step 1: Enable Versioning
Add to yourbengal.yaml:
versioning:
enabled: true
versions:
- id: v3
label: "3.0"
latest: true
- id: v2
label: "2.0"
- id: v1
label: "1.0"
deprecated: true
sections:
- docs # Which content sections are versioned (default)
If you omit sections, it defaults to ["docs"].
Step 2: Create Version Directories
Option A: Use the CLI (Recommended)
# Before making breaking changes, snapshot current docs
bengal version create v2
# This creates _versions/v2/docs/ with a copy of docs/
Option B: Create Manually
mkdir -p content/_versions/v2/docs
cp -r content/docs/* content/_versions/v2/docs/
Step 3: Build and Verify
bengal build
Check the output:
public/
├── docs/
│ ├── index.html # /docs/ (latest)
│ └── installation/
│ └── index.html # /docs/installation/
├── docs/v2/
│ ├── index.html # /docs/v2/
│ └── installation/
│ └── index.html # /docs/v2/installation/
└── docs/v1/
└── ...
Configuration Options
Full Configuration Reference
versioning:
enabled: true
# Version definitions
versions:
- id: v3 # Required: unique identifier
label: "3.0 (Current)" # Optional: display label
latest: true # One version must be latest
- id: v2
label: "2.0 LTS"
banner: # Optional: warning banner
type: warning # info, warning, or danger
message: "This is an older version. See v3 for latest."
show_latest_link: true
- id: v1
label: "1.0"
deprecated: true # Marks version as deprecated
end_of_life: "2024-12-31" # Optional: EOL date
# Aliases for version IDs
aliases:
latest: v3
stable: v3
lts: v2
# Which sections are versioned (default: ["docs"])
sections:
- docs
# - api # Add more if needed
# Shared content across all versions (default: ["_shared"])
shared:
- _shared # Content in _shared/ appears in all versions
Shared Content
Content that should appear in all versions goes in_shared/:
content/
├── docs/
├── _versions/
└── _shared/
└── docs/
└── changelog.md # Appears in all versions
Workflow: Creating a New Version
When you're about to release a breaking change:
1. Snapshot Current Docs
# Create v2 from current docs
bengal version create v2 --label "2.0"
2. Update Configuration
versions:
- id: v3 # NEW: will be the latest
latest: true
- id: v2 # Previous latest
- id: v1
3. Make Breaking Changes
Editcontent/docs/ freely—v2 is preserved in _versions/v2/docs/.
4. Build and Deploy
bengal build
Tips
Keep Versions in Sync
For pages that exist in all versions (like changelog), use shared content:
_shared/docs/changelog.md
Mark Deprecated Versions
versions:
- id: v1
deprecated: true
banner:
type: danger
message: "v1 is no longer supported. Please upgrade to v3."
Version-Specific Banners
Each version can have its own banner:
versions:
- id: v2
banner:
type: info
message: "v2 is in LTS. Security updates until 2025."
Troubleshooting
Version Not Appearing
- Check that the version is listed in
bengal.yaml - Verify the directory exists:
_versions/{id}/docs/ - Ensure content exists in the version directory
Wrong Version Showing as Latest
Only one version should havelatest: true:
versions:
- id: v3
latest: true # ✓ Only this one
- id: v2
# latest: true # ✗ Remove this
URLs Incorrect
Checksectionsin your config matches your content structure:
sections:
- docs # Must match your content/docs/ directory
Next Steps
- Cross-Version Links — Link between versions
- Version Directives — Mark version-specific content
- Git Mode — Alternative: build from branches
Git Mode Setup
Build versioned documentation from Git branches and tags
Git Mode Setup
Git mode builds documentation from Git branches or tags instead of folder copies. This is ideal for projects that already use release branches.
When to Use Git Mode
✅ Use Git Mode if:
- You already have release branches (e.g.,
release/1.0,release/2.0) - You want versions tied to Git history
- You prefer not to duplicate content in folders
- Your CI/CD pipeline builds from branches
❌ Use Folder Mode if:
- You want simple, visible version directories
- You don't use release branches
- You're new to versioning
How It Works
┌─────────────────────────────────────────────────────────┐
│ Your Repo │
├─────────────────────────────────────────────────────────┤
│ main ──────────────────────────────► v3.0 (latest) │
│ │ │
│ └─ release/2.0 ──────────────────► v2.0 │
│ │ │
│ └─ release/1.0 ─────────────► v1.0 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Bengal Build │
├─────────────────────────────────────────────────────────┤
│ 1. Discover branches matching patterns │
│ 2. Create worktrees for each version │
│ 3. Build each version in parallel │
│ 4. Merge outputs into single site │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Output Site │
├─────────────────────────────────────────────────────────┤
│ /docs/ ← from main (latest) │
│ /docs/2.0/ ← from release/2.0 │
│ /docs/1.0/ ← from release/1.0 │
└─────────────────────────────────────────────────────────┘
Bengal uses Git worktrees to check out each branch without affecting your working directory.
Configuration
Configuration works in bothbengal.yaml and bengal.tomlformats.
Basic Setup
# bengal.yaml (or bengal.toml)
versioning:
enabled: true
mode: git
git:
branches:
# Explicit branch
- name: main
latest: true
# Pattern matching
- pattern: "release/*"
strip_prefix: "release/"
default_branch: main
parallel_builds: 4
Pattern Matching
Match multiple branches with glob patterns:
git:
branches:
# Match release/1.0, release/2.0, etc.
- pattern: "release/*"
strip_prefix: "release/" # release/2.0 → version "2.0"
# Match v1.0.0, v2.0.0, etc.
- pattern: "v*"
# No strip_prefix → version "v1.0.0"
Using Tags
Build from Git tags instead of (or in addition to) branches:
git:
branches:
- name: main
latest: true
tags:
- pattern: "v*"
strip_prefix: "v" # v1.0.0 → version "1.0.0"
Full Configuration Reference
versioning:
enabled: true
mode: git
git:
# Branch patterns
branches:
- name: main # Explicit branch name
latest: true # This is the latest version
- pattern: "release/*" # Glob pattern
version_from: branch # How to extract version ID (see below)
strip_prefix: "release/"
latest: false
# version_from options:
# branch - Use branch name (default)
# tag - Use tag name
# <regex> - Custom regex pattern
# Tag patterns (optional)
tags:
- pattern: "v*"
strip_prefix: "v"
# Settings
default_branch: main # Fallback if no latest specified (default: "main")
cache_worktrees: true # Keep worktrees for faster rebuilds (default: true)
parallel_builds: 4 # Number of concurrent builds (default: 4)
# Standard versioning options still apply
sections:
- docs
aliases:
latest: main
stable: "2.0"
Building
Build All Versions
# Discover and build all matching branches/tags
bengal build --all-versions
Output:
🔍 Discovering versions from git...
Found 3 versions to build
• main
• 2.0
• 1.0
📦 Building version main...
✅ Built 45 pages
📦 Building version 2.0...
✅ Built 42 pages
📦 Building version 1.0...
✅ Built 38 pages
✅ Built 3 versions
Build Specific Version
# Build only version 2.0
bengal build --version 2.0
Regular Build (Current Branch Only)
# Build current branch as unversioned site
bengal build
Comparing Versions
Compare content between Git refs:
# Compare branches
bengal version diff main release/2.0 --git
# Output as markdown (for release notes)
bengal version diff main release/2.0 --git --output markdown
# Output as JSON (for automation)
bengal version diff main release/2.0 --git --output json
Example output:
📊 Version Diff: release/2.0 → main
Version diff: release/2.0 → main
Added: 3 pages
Removed: 1 pages
Modified: 12 pages
Unchanged: 28 pages
✨ Added pages:
+ docs/new-feature.md
+ docs/api/webhooks.md
+ docs/guide/advanced.md
🗑️ Removed pages:
- docs/deprecated-guide.md
📝 Modified pages:
~ docs/installation.md (45.2% changed)
~ docs/configuration.md (12.8% changed)
CI/CD Integration
GitHub Actions Example
# .github/workflows/docs.yml
name: Build Docs
on:
push:
branches:
- main
- 'release/*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Need full history for branches
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
- name: Install Bengal
run: pip install bengal
- name: Build All Versions
run: bengal build --all-versions
- name: Deploy
# Pin to SHA for supply chain security (v3.9.3)
uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
GitLab CI Example
# .gitlab-ci.yml
build-docs:
image: python:3.14
script:
- pip install bengal
- bengal build --all-versions
artifacts:
paths:
- public/
Worktrees
Bengal uses Git worktrees to check out multiple branches simultaneously:
.bengal/
└── worktrees/
├── 2.0/ # Checked out from release/2.0
└── 1.0/ # Checked out from release/1.0
Caching Worktrees
By default, worktrees are cached for faster rebuilds:
git:
cache_worktrees: true # Keep worktrees between builds
Set to falseto always create fresh worktrees (slower but cleaner).
Manual Cleanup
# Remove cached worktrees
rm -rf .bengal/worktrees
git worktree prune
Troubleshooting
Branch Not Found
❌ Version 2.0 not found
Check that the branch exists:
git branch -a | grep release
Verify your pattern matches:
branches:
- pattern: "release/*" # Must match exactly
Worktree Errors
Failed to create worktree
Clean up stale worktrees:
git worktree prune
rm -rf .bengal/worktrees
Permission Errors
error: unable to create file: Permission denied
Ensure write access to the.bengal/directory:
chmod -R u+w .bengal/
If running in CI, ensure the checkout step has write permissions.
Slow Builds
Reduce parallel builds if memory is limited:
git:
parallel_builds: 2 # Reduce from default 4
Next Steps
- Cross-Version Links — Link between versions
- Version Directives — Mark version-specific content
- Folder Mode — Alternative: folder-based versioning
Cross-Version Links
Link to specific documentation versions from any page
Cross-Version Links
Bengal extends its[[link]]syntax to support cross-version links—links that point to a specific version of a page.
Syntax
[[version:path]]
[[version:path|Custom Text]]
[[version:path#anchor]]
Examples
Basic Cross-Version Link
See the [[v2:docs/installation]] for the old setup process.
Renders as a link to /docs/v2/installation/with the page title as link text.
With Custom Text
Check the [[v2:docs/migration|v2 Migration Guide]] before upgrading.
Renders as a link to /docs/v2/migration/with text "v2 Migration Guide"
With Anchor
The [[v2:docs/api#authentication|v2 auth section]] explains the old flow.
Links to /docs/v2/api/#authentication.
Using Aliases
You can use version aliases instead of IDs:
[[latest:docs/guide]] <!-- Links to latest version -->
[[stable:docs/api]] <!-- Links to stable alias -->
[[lts:docs/security]] <!-- Links to LTS alias -->
Use Cases
Migration Guides
When writing upgrade documentation, link to the old version for reference:
# Upgrading from v2 to v3
## Breaking Changes
### Authentication
In v3, we switched from API keys to OAuth. See [[v2:docs/auth|v2 authentication docs]]
for the old approach.
**Before (v2):**
```python
client = Client(api_key="...")
```
**After (v3):**
```python
client = Client(oauth_token="...")
```
Changelogs
Reference specific version documentation in your changelog:
## v3.0.0
### Breaking Changes
- Removed `legacy_mode` option ([[v2:docs/config#legacy-mode|see v2 docs]])
- Changed default timeout from 30s to 60s
### New Features
- Added webhook support ([[latest:docs/webhooks|see docs]])
Deprecation Notices
Point users to the new approach while documenting the old:
:::{deprecated} v3.0
The `old_function()` is deprecated. See [[latest:docs/api#new-function]]
for the replacement.
:::
Behavior
Version Not Found
If the specified version doesn't exist, Bengal renders a broken reference indicator:
[[v99:docs/guide]]
Renders as:
<span class="broken-ref" data-ref="v99:docs/guide"
title="Version not found: v99">[docs/guide]</span>
The broken-ref class allows styling via CSS, and the titleattribute shows the error on hover.
Page Not Found in Version
If the version exists but the page doesn't, Bengal still generates the link (it might be a valid page in that version's build).
Latest Version Links
Links to the latest version use clean URLs without version prefix:
[[latest:docs/guide]] → /docs/guide/ (not /docs/v3/guide/)
Configuration
Cross-version links work automatically when versioning is enabled. No additional configuration needed.
# bengal.yaml
versioning:
enabled: true
versions:
- id: v3
latest: true
- id: v2
- id: v1
aliases:
latest: v3
stable: v3
lts: v2
Combining with Regular Links
You can mix cross-version links with regular links:
## Related Documentation
- [[docs/installation]] — Current installation guide
- [[v2:docs/installation]] — v2 installation guide
- [[v1:docs/installation]] — v1 installation guide (deprecated)
Tips
Use for Upgrade Paths
Create a clear upgrade path by linking between versions:
# v2 → v3 Migration
1. Review [[v2:docs/breaking-changes|v2 breaking changes]]
2. Update your config ([[v3:docs/config|v3 config reference]])
3. Test your integration
Don't Overuse
Cross-version links are powerful but can clutter content. Use them primarily in:
- Migration guides
- Changelogs
- Deprecation notices
- Version comparison pages
For regular content, users expect links to stay within their current version.
Next Steps
- Version Directives — Mark version-specific content
- Versioning Overview — Full versioning documentation
Version Directives
Mark content as added, deprecated, or changed in specific versions
Version Directives
Bengal provides three directives for marking version-specific content:
| Directive | Purpose | Rendered As |
|---|---|---|
:::{since} |
Feature added in version X | New in X |
:::{deprecated} |
Feature deprecated in version X | Deprecated since X |
:::{changed} |
Behavior changed in version X | Changed in X |
Each directive renders with an SVG icon and themed styling using CSS classes.
Since Directive
Marks content that was added in a specific version.
Syntax
:::{since} v2.0
This feature was added in version 2.0.
:::
Rendered Output
This feature was added in version 2.0.
Use Cases
Document new features:
## Webhooks
:::{since} v2.5
Webhooks allow you to receive real-time notifications when events occur.
:::
To configure webhooks:
1. Go to Settings → Webhooks
2. Add your endpoint URL
3. Select events to subscribe to
Mark new parameters:
### Parameters
| Name | Type | Description |
|------|------|-------------|
| `timeout` | `int` | Request timeout in seconds |
| `retries` | `int` | Number of retries :::{since} v2.1 ::: |
Deprecated Directive
Marks content that is deprecated and will be removed in a future version.
Syntax
:::{deprecated} v3.0
Use `new_function()` instead. This will be removed in v4.0.
:::
Rendered Output
Usenew_function()instead. This will be removed in v4.0.
Use Cases
Deprecate a function:
## old_function()
:::{deprecated} v3.0
This function is deprecated. Use [[docs/api#new-function|new_function()]] instead.
:::
```python
# Don't use this
result = old_function(data)
# Use this instead
result = new_function(data)
```
Deprecate a configuration option:
### legacy_mode
:::{deprecated} v2.5
The `legacy_mode` option is deprecated and will be removed in v3.0.
Migrate to the new configuration format.
:::
```yaml
# Deprecated
legacy_mode: true
# New approach
compatibility:
version: 2
```
Changed Directive
Marks content where behavior changed in a specific version.
Syntax
:::{changed} v2.5
Default timeout changed from 30 seconds to 60 seconds.
:::
Rendered Output
Default timeout changed from 30 seconds to 60 seconds.
Use Cases
Document behavior changes:
## Configuration
### timeout
The request timeout in seconds.
:::{changed} v2.5
Default changed from 30 to 60 seconds for better reliability with slow networks.
:::
**Default:** 60 seconds
Document API changes:
## Response Format
:::{changed} v3.0
The response now includes a `metadata` field with additional information.
:::
```json
{
"data": [...],
"metadata": { // New in v3.0
"total": 100,
"page": 1
}
}
```
Inline Usage
All directives can be used inline for brief annotations:
| Option | Default | Description |
|--------|---------|-------------|
| `timeout` | 60s | Request timeout :::{changed} v2.5 was 30s ::: |
| `retries` | 3 | Retry count :::{since} v2.0 ::: |
| `legacy` | false | Legacy mode :::{deprecated} v3.0 ::: |
Without Version Number
Thedeprecated and changeddirectives support omitting the version number for generic notices:
:::{deprecated}
This feature is deprecated.
:::
:::{changed}
This behavior has changed.
:::
Note
Thesincedirective requires a version number. Without one, no output is rendered.
Styling
Each directive renders with an inline SVG icon and themed styling. The directives use CSS classes you can customize:
/* Since directive - success/green theme */
.version-since {
--version-color: var(--color-success);
background-color: var(--color-success-bg);
}
/* Deprecated directive - warning/orange theme */
.version-deprecated {
--version-color: var(--color-warning);
background-color: var(--color-warning-bg);
}
/* Changed directive - info/blue theme */
.version-changed {
--version-color: var(--color-info);
background-color: var(--color-info-bg);
}
Badges include Lucide-style SVG icons (.version-badge-icon) that inherit the theme color via currentColor. Full directive containers (when content is provided) feature a luminescent glow animation and neumorphic styling.
Combining Directives
You can combine directives for comprehensive version history:
## process_data()
:::{since} v1.0
Core data processing function.
:::
:::{changed} v2.0
Added support for streaming data.
:::
:::{changed} v2.5
Default batch size increased from 100 to 1000.
:::
Processes data with automatic batching and retry logic.
Best Practices
Be Specific
❌ Vague:
:::{changed} v2.0
This was changed.
:::
✅ Specific:
:::{changed} v2.0
Return type changed from `list` to `iterator` for memory efficiency.
:::
Include Migration Path
For deprecations, always explain what to use instead:
:::{deprecated} v3.0
Use `new_api()` instead. See the [[docs/migration|migration guide]] for details.
:::
Don't Overuse
Version directives are most valuable for:
- Breaking changes
- New major features
- Deprecations
Don't annotate every minor change—it creates noise.
Next Steps
- Cross-Version Links — Link between versions
- Folder Mode — Set up folder-based versioning
- Git Mode — Set up git-based versioning