Built for developers who just want a fast blog.

Every decision in Pressbin is about reducing complexity. Less to install, less to break, less to think about.

Single Binary Deployment

The entire Pressbin engine — HTTP server, markdown renderer, SQLite database, and default theme — compiles into one executable file. No runtime, no interpreter, no package manager on the server.

Deploying a new version means copying one file. Rolling back means copying the old file back. That's it.

  • ~12mb server binary (linux amd64, stripped)
  • Zero install dependencies on the server
  • Cross-compile for Linux from Mac or Windows
  • Works on any VPS, bare metal, or even a Raspberry Pi
install.sh
# Install to ~/.pressbin (no root)
$ curl -fsSL https://raw.githubusercontent.com/pressbin/pressbin/main/scripts/install.sh \
| bash -s -- --site-url https://blog.example.com
 
~/.pressbin/config.yml
~/.pressbin/data/pressbin.db
~/.pressbin/assets/
~/.pressbin/admin.key ~/.pressbin/sync.key
 
$ pressbin serve
 
Listening on 127.0.0.1:8080

Git Push to Publish

Your GitHub repository is the source of truth for all content. Write a post, commit it, push to main — a GitHub Action downloads the matching pressbin-sync client and syncs changed posts and images to your server via the sync API.

No login portal. No "publish" button to click. No deploy hook to configure. Just your normal git workflow.

  • Triggered on push to main for posts/**/*.md and assets/images/**
  • Incremental sync via git diff; first push uploads all posts and images
  • Deleted posts and images are removed via the sync API
  • Start from the blog template repo
sync.yml
on:
push:
branches: [main]
paths: ['posts/**/*.md', 'assets/images/**']
 
jobs:
sync:
steps:
- uses: actions/checkout@v4
- run: curl …/api/version → pressbin-sync
./pressbin-sync-linux-amd64 run $GITHUB_WORKSPACE
 
Syncing post posts/hello.md…
✓ done

SQLite with Full-Text Search

Pressbin uses SQLite with WAL mode for its embedded database. Every post is stored as rendered HTML — no re-rendering on each request. Just one query per page load.

FTS5 (SQLite's full-text search extension) powers the search box. Triggers keep the index in sync automatically on every post create, update, or delete.

  • WAL mode for concurrent reads during writes
  • Pre-rendered HTML stored in DB — zero render cost at serve time
  • FTS5 index with automatic trigger-based updates
  • Sub-millisecond response times on commodity hardware
schema.sql
CREATE VIRTUAL TABLE fts_index
USING fts5(
slug UNINDEXED,
title,
content_md,
content=posts,
content_rowid=rowid
);
 
-- Triggers keep index in sync
CREATE TRIGGER posts_ai
AFTER INSERT ON posts ...

REST Admin API

Every Pressbin action is available via a REST API. Build a mobile app, a CLI, a custom dashboard — or just use curl. The binary is the backend. The UI is up to you.

Two key types, strict separation: admin keys manage posts, settings, and keys via /api/admin/* (they cannot sync). Sync keys publish from GitHub via /api/sync* only. Both are bcrypt-hashed in SQLite.

  • Admin: list, update, delete posts; manage keys and settings
  • Sync: POST /api/sync, asset upload, deletions (GitHub Actions)
  • GitHub secret PRESSBIN_KEY = sync.key (pb_sync_…)
  • pressbin setup writes admin.key and sync.key once
api — curl
# List all posts
$ curl /api/admin/posts \
-H "Authorization: Bearer pb_admin_..."
 
{
"posts": [...],
"total": 42,
"page": 1
}
 
# Create a new sync key
$ curl -X POST /api/admin/keys \
-d '{"label":"ci","type":"sync"}'

Five minutes to your
first live post.

Run the installer, add the GitHub Action, push a Markdown file.

Install Pressbin
pressbin serve GitHub secret: PRESSBIN_KEY from ~/.pressbin/sync.key