Pebble
A CMS that fits in your pocket.
One binary. One database file. No runtime dependencies. Just your content.
Quick Start • Full Documentation • Why Pebble
Pebble is a lightweight CMS built in Rust. It compiles to a single binary with no external dependencies. Your entire site -- content, media, configuration -- lives in one SQLite file and a config.
pebble init my-blog && cd my-blog && pebble serveThat's it. Open http://localhost:3000/admin, create your account, start writing. When you're ready for production, run pebble deploy. When you want static hosting, run pebble build. Import from WordPress or Ghost. Export to Hugo or Zola. Back up everything with one command. No Docker required. No database server. No Node.js. No PHP. Just Pebble.
Most CMS platforms ask you to manage a database server, a runtime, a package manager, a cache layer, and a deployment pipeline before you write your first post. Pebble asks you to download one file.
Zero infrastructure. SQLite is embedded. The web server is embedded. Templates, CSS, and JavaScript are compiled into the binary. There is nothing to install, configure, or maintain besides Pebble itself.
Two deployment modes. Run pebble serve for a dynamic site with a full admin panel, or run pebble build to generate a static site you can drop onto any hosting provider. Switch between them whenever you want -- your content stays the same.
Import and export freely. Bring your WordPress or Ghost site with one command. Export to Hugo or Zola at any time. Your content is never locked in.
Opinionated defaults, practical control. Fifteen built-in themes. Automatic image optimization with responsive WebP variants. Syntax-highlighted code blocks. RSS feeds. Sitemaps. Analytics. Audit logging. Content versioning. All included, many options configurable, all without plugins.
# Build from source
cargo install --path .
# Or build with optimizations
cargo build --release
# Binary is at ./target/release/pebblepebble init my-blog
cd my-blogThis creates a pebble.toml config file and initializes your SQLite database.
pebble serveOpen http://localhost:3000/admin. On first visit, you'll create your admin account. Then you're ready to write.
pebble deployBinds to 0.0.0.0:8080 by default. The admin panel is disabled in production mode -- manage content in development, serve it in production.
pebble build --output ./public --base-url https://example.comProduces a complete static site with HTML pages, RSS/JSON feeds, a sitemap, a search index, and all your media files. Deploy it to GitHub Pages, Netlify, Vercel, Cloudflare Pages, S3, or any web server.
docker compose up -dOr build and run manually:
docker build -t pebble .
docker run -p 8080:8080 -v pebble-data:/data pebble- Posts, Pages, and Snippets -- three content types covering blogs, static pages, and reusable content blocks
- Markdown with extras -- tables, footnotes, strikethrough, task lists, and fenced code blocks with syntax highlighting for 17+ languages
- Content series -- group posts into ordered sequences with automatic previous/next navigation
- Shortcodes -- embed images, video, audio, and galleries directly in Markdown
- Scheduled publishing -- set a future publish date; Pebble publishes automatically
- Content versioning -- every edit creates a version snapshot you can view, compare, or restore
- Draft previews -- share unpublished content via signed, time-limited preview URLs
- Bulk operations -- publish, unpublish, archive, or delete multiple posts at once
- Full-text search -- built-in search powered by SQLite FTS5
- Automatic image optimization -- uploads are converted to WebP with responsive variants at 400w, 800w, 1200w, and 1600w
- Responsive picture elements -- shortcodes generate
<picture>withsrcsetfor automatic size selection - Drag-and-drop upload -- drop images directly into the Markdown editor
- Clipboard paste -- paste screenshots and copied images into the editor
- Supported formats -- JPEG, PNG, GIF, WebP, SVG, MP4, WebM, MP3, OGG, PDF
Fifteen built-in themes, all supporting light and dark mode:
| default | minimal | magazine | brutalist |
| neon | serif | ocean | midnight |
| botanical | monochrome | coral | terminal |
| nordic | sunset | typewriter |
Every theme can be customized with your own colors, fonts, and spacing via pebble.toml. No CSS editing required.
- Rich Markdown editor with toolbar, keyboard shortcuts (Ctrl+B/I/K), and live preview
- Auto-save drafts to local storage -- never lose work
- SEO metadata -- custom meta titles, descriptions, and canonical URLs per page
- Tag management for organizing posts
- Media library with upload, browse, and delete
- User management with three roles: Admin, Author, Viewer
- Database dashboard -- view stats, run vacuum and analyze operations
- Settings panel -- configure site, theme, homepage layout, and content options from the browser
Built-in, privacy-respecting analytics. No third-party scripts. No cookies.
- Privacy by default -- IPs are anonymized, sessions are hashed, DNT is respected
- Dashboard -- pageviews, unique sessions, top pages, referrers, devices, browsers, and countries
- Real-time view -- active sessions and recent pageviews
- Per-content stats -- see how individual posts perform over time
- Export -- download analytics data as JSON or CSV
A JSON API at /api/v1/ for headless CMS use cases, mobile apps, or integrations.
- Token-authenticated with
Bearerheader; tokens are scoped read or read & write - Read endpoints for posts, pages, tags, series, media, and site info
- Write endpoints for posts and pages — create, update, and delete (require a write-scoped token)
- Paginated responses with a consistent JSON envelope
Write endpoints (require a token with the Read & Write permission):
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/posts |
Create a post |
PUT |
/api/v1/posts/:slug |
Update a post by slug |
DELETE |
/api/v1/posts/:slug |
Delete a post by slug |
POST |
/api/v1/pages |
Create a page |
PUT |
/api/v1/pages/:slug |
Update a page by slug |
DELETE |
/api/v1/pages/:slug |
Delete a page by slug |
# Create a published post
curl -X POST https://example.com/api/v1/posts \
-H "Authorization: Bearer pb_..." \
-H "Content-Type: application/json" \
-d '{"title": "Hello", "body_markdown": "# Hi there", "status": "published", "tags": ["news"]}'A read-only token receives 403 Forbidden on write endpoints; a missing or invalid token receives 401 Unauthorized. Create and update bodies accept the same fields as the admin editor (title, slug, body_markdown, excerpt, featured_image, status, scheduled_at, tags, metadata); update is a partial — only the fields you send change.
HTTP callbacks on content events. Trigger CI/CD rebuilds, Slack notifications, or any integration.
- Events:
content.published,content.updated,content.deleted,media.uploaded,media.deleted - HMAC-SHA256 signing for payload verification
- Automatic retries with exponential backoff
- Delivery log viewable in the admin panel
- Argon2 password hashing
- Rate limiting on all write endpoints (login, content, uploads, settings)
- CSRF protection on admin write routes
- Content Security Policy headers on every response
- HttpOnly, Secure, SameSite=Strict session cookies
- Audit logging -- key admin actions are recorded with user, timestamp, and details
- SVG sanitization -- uploaded SVGs are checked for script injection
- Path traversal protection in backup restore
Import into Pebble from:
- WordPress (WXR XML export)
- Ghost (JSON export)
Export from Pebble to:
- Pebble (YAML frontmatter Markdown)
- Hugo (TOML frontmatter)
- Zola (TOML frontmatter)
- Manual:
pebble backup create/pebble backup restore - Automatic: configure scheduled backups with retention in
pebble.toml - Format: ZIP archive containing the database, all media files, and a manifest
Manage multiple Pebble sites from one machine. Each site gets its own database, config, and port.
pebble registry init blog --title "My Blog"
pebble registry init docs --title "Documentation"
pebble registry serve blog # auto-assigns port 3001
pebble registry serve docs # auto-assigns port 3002
pebble registry list
pebble registry stop-all| Command | What it does |
|---|---|
pebble init [path] |
Create a new site |
pebble serve |
Start development server (localhost:3000) |
pebble deploy |
Start production server (0.0.0.0:8080) |
pebble build |
Generate a static site |
pebble export |
Export content as Markdown (Pebble, Hugo, or Zola format) |
pebble import [path] |
Import from a Pebble export directory |
pebble import-wp <file> |
Import from WordPress WXR export |
pebble import-ghost <file> |
Import from Ghost JSON export |
pebble backup create |
Create a backup ZIP |
pebble backup restore <file> |
Restore from a backup ZIP |
pebble backup list |
List available backups |
pebble user add |
Create a user account |
pebble user list |
List all users |
pebble user passwd <name> |
Change a user's password |
pebble user remove <name> |
Delete a user |
pebble migrate |
Run database migrations |
pebble migrate status |
Show applied and pending migrations |
pebble migrate rollback |
Roll back the most recent migration(s) |
pebble doctor |
Run system health checks |
pebble rerender |
Re-render all content HTML from Markdown |
pebble config list |
View global configuration |
pebble registry init <name> |
Create a registry-managed site |
pebble registry serve <name> |
Start a registry site |
pebble registry list |
Show all registry sites and status |
pebble registry stop-all |
Stop all running registry sites |
Run any command with --help for full options.
Each site is configured with a pebble.toml at the site root:
[site]
title = "My Site"
description = "A personal blog"
url = "https://example.com"
language = "en"
[server]
host = "127.0.0.1"
port = 3000
[database]
path = "./data/pebble.db"
[content]
posts_per_page = 10
excerpt_length = 200
auto_excerpt = true
version_retention = 50 # versions per content item (0 = unlimited)
[media]
upload_dir = "./data/media"
max_upload_size = "10MB"
[theme]
name = "default"
[theme.custom] # optional overrides
primary_color = "#e63946"
font_family = "Georgia, serif"
[auth]
session_lifetime = "7d"
[api]
enabled = false # set true to enable /api/v1/ endpoints
[backup]
auto_enabled = false # set true for scheduled backups
interval_hours = 24
retention_count = 7
directory = "./backups"See docs/usage.md for usage details and the main configuration options.
pebble (single binary)
|
+-- Axum web server
| +-- Public routes (posts, pages, tags, feeds, search, sitemap)
| +-- Admin routes (dashboard, editor, media, settings, users)
| +-- API routes (/api/v1/*)
| +-- HTMX routes (live preview, search, autocomplete)
|
+-- SQLite database (WAL mode, connection pooling, FTS5)
| +-- Content, users, sessions, tags, series, media metadata
| +-- Analytics, audit logs, versions, API tokens, webhooks
|
+-- Embedded assets
+-- Tera HTML templates
+-- CSS bundles (public + admin)
+-- JavaScript (theme toggle, admin editor)
- Language: Rust
- Web framework: Axum
- Database: SQLite via rusqlite with r2d2 pooling
- Templates: Tera
- Markdown: pulldown-cmark with syntect highlighting
- HTML sanitization: ammonia
- Password hashing: Argon2
- Image processing: image crate
| Pebble | WordPress | Ghost | Hugo | |
|---|---|---|---|---|
| Dependencies | None | PHP, MySQL/MariaDB | Node.js, MySQL | None |
| Deployment | Single binary | LAMP stack | Node app | Build step + static host |
| Admin panel | Built-in | Built-in | Built-in | None |
| Dynamic + Static | Both | Dynamic only | Dynamic only | Static only |
| Database | SQLite (embedded) | MySQL/MariaDB | MySQL | None (files) |
| Image optimization | Automatic | Plugins | Built-in | External tools |
| Themes | 15 built-in | Install separately | Install separately | Install separately |
| Analytics | Built-in | Plugins | Built-in | External service |
| API | Built-in (token auth) | Built-in (REST) | Built-in (REST) | None |
- Usage Guide -- reference for the main features, commands, and configuration options
- Deployment Guide -- systemd, nginx, Caddy, TLS, firewall, and monitoring
- Performance Baseline -- benchmarking methodology, memory profile, and SQLite tuning