Documentation

Running a server·Updated 13 Oct 2026

Overview

What a Kribble server is made of, how the parts connect, and what each one needs

A Kribble server is two Go programs, kribble-web and kribble-builder, built from the same source and reading the same config.toml. They need PostgreSQL, Meilisearch and file storage. Redis and Chromium are optional. This page describes each part. Installing covers setting them up.

The parts

Part

Required

What it does

kribble-web

yes

The HTTP server: the web app, the REST API, the MCP endpoint, published sites, file downloads and the CLI downloads. Stateless; you can run several behind a load balancer.

kribble-builder

yes

Builds published sites from a queue and runs the hourly clean-up jobs. Run exactly one.

PostgreSQL 16+

yes

The database. Holds everything except uploaded files, built sites and the search index.

Meilisearch v1.12+

yes

Full-text search over notes. Kribble updates the index after each change is saved, and when kribble-web starts it rebuilds the index from the database if it is empty or was built by a Kribble version that indexed notes differently, so the index holds nothing the database doesn't. While Meilisearch is unreachable, notes still save but full-text search fails.

File storage

yes

Uploads (images, attachments, avatars) and built sites. Either a local directory ([storage].backend = "disk") or S3-compatible object storage ("s3": Cloudflare R2, AWS S3, MinIO, Backblaze B2 and others).

Redis 7+

no

Shared caches between several kribble-web processes. Only needed when you run more than one.

Chromium or Chrome

no

PDF export. kribble-web starts a headless browser the first time someone exports a PDF. Without it, PDF export returns an error and everything else works.

What kribble-web serves

Everything is on one host name, by default on port 8080:

Path

What

/

The landing page, then the web app (the client is embedded in the binary)

/api/v1/…

The REST API used by the web app, the CLI, the desktop app and CI scripts

/mcp

The MCP server for AI agents (streamable HTTP; takes an access token)

/p/<owner>/<slug>/

Sites published to the built-in target. <owner> is the organisation name, or ~username for a personal vault

/files/…

Uploaded files, for signed-in users with access to the space

/files-public/…

Assets of public published sites

/install.sh, /install.ps1, /downloads/…

The kribble CLI, the editor extensions and the desktop apps (see The CLI and desktop downloads)

/healthz, /readyz

Health checks. /readyz returns 503 while the server shuts down

There are no WebSockets. Browser tabs stay current by polling /api/v1/workspace/version, so a load balancer needs no sticky sessions.

What kribble-builder does

  • Site builds. When someone publishes or rebuilds a site, kribble-web writes the site's row to the database with status queued. The builder checks the database for queued rows every 10 seconds, claims them one at a time, generates the static site and copies it to its destination: file storage for the built-in target, or a directory, an SSH server or Cloudflare Pages for an operator-configured target. [builder].workers (default 2) builds run in parallel.

  • Clean-up jobs, every hour: expired sessions, pending registrations and password-reset tokens are deleted, expired invites are marked expired, uploads that were never saved into a note are deleted after 1 hour, and files no longer used by any note or kept version are deleted after 24 hours.

  • A health server on [builder].port (default 8081) with /healthz and /readyz. It serves nothing else.

Both binaries connect to PostgreSQL, Meilisearch and storage. The database is the only connection between them: if the builder is stopped, publishing still works in the web app, but sites stay queued until the builder runs again.

Never run more than one kribble-builder against the same database. The clean-up jobs are not coordinated between processes. To build faster, raise [builder].workers and give the host more CPU.

How they connect

flowchart LR
  users[Browsers, CLI, desktop app, agents] -->|HTTPS| proxy[Reverse proxy with TLS]
  proxy -->|HTTP :8080| web[kribble-web]
  web --> pg[(PostgreSQL)]
  web --> meili[(Meilisearch)]
  web --> storage[(Disk or S3 storage)]
  web -.-> redis[(Redis, optional)]
  web -.-> chrome[Chromium, for PDF]
  builder[kribble-builder] --> pg
  builder --> meili
  builder --> storage
  builder -.-> redis
  builder -->|local, SSH, Cloudflare Pages| targets[External publish targets]
  • Clients reach only kribble-web, through a reverse proxy that terminates TLS. The proxy must use HTTPS, because outside dev mode the session cookies are marked Secure. See Installing for the proxy settings.

  • PostgreSQL, Meilisearch and Redis should listen only on localhost or a private network.

  • With the disk backend, kribble-web and kribble-builder must see the same directory, so they run on the same host, or share a volume in Docker. With s3, they can run on different hosts.

  • With s3, files up to [storage.s3].proxy_threshold_bytes (15 MiB by default) are streamed through kribble-web. Larger files are sent as a redirect to a presigned URL, so browsers must be able to reach the S3 endpoint directly.

Running more than one kribble-web

kribble-web keeps no state of its own. To run several:

  1. Use the s3 storage backend, or a shared directory that every replica mounts.

  2. Install Redis and set [redis].url in every replica's config, and in the builder's. Without Redis, each process has its own in-memory caches (workspace versions, unread notifications, site and space access), and users may see stale data for up to a minute.

  3. Put any HTTP load balancer in front. No sticky sessions are needed.

Keep a single kribble-builder.

Sizing

The repository has no benchmarks. These per-process limits help you size a host:

Limit

Value

Where it comes from

Database connections

up to 25 per process (10 idle)

Every kribble-web replica and the builder each open their own pool. Make sure PostgreSQL's max_connections covers 25 × the number of processes, plus your own tools.

Concurrent PDF renders

the number of CPU cores, at most 4

One headless Chromium per kribble-web, restarted every 30 minutes or 100 exports to limit its memory use.

Upload size

500 MB per attachment, 100 MB per image

With the s3 backend an upload is held in memory while it is sent to storage. Plan RAM for your largest concurrent uploads.

Note size

5 MB per note

Measured as the stored document, not the markdown

Markdown import and kribble push

100 MB in total, 10 MB per file

Per request

Site builds

[builder].workers at a time (default 2)

Each build writes the site to a temporary directory first.

Redis

a few MB

Used only as a cache. PostgreSQL holds the real data.

Ports

Port

Service

Expose it publicly?

8080

kribble-web ([server].port)

No. Only the reverse proxy should reach it

8081

kribble-builder health checks ([builder].port)

No

5432

PostgreSQL

No

7700

Meilisearch

No

6379

Redis

No

443

Your reverse proxy

Yes

Next