Documentation

Running a server·Updated 13 Oct 2026

Backups and upgrades

What to back up on a Kribble server, how to restore it, and how to upgrade and roll back

A Kribble server keeps its data in PostgreSQL and the file storage, and a search index in Meilisearch. Back up the database, the files and your configuration. A backup of Meilisearch is optional, because Kribble can rebuild the index from the database.

What to back up

What

Where

Why

PostgreSQL

the kribble database

Accounts, organisations, spaces, notes and their history, comments, access tokens, published-site settings. Everything except files.

File storage

[storage].dir (disk), or the buckets [storage.s3].bucket and public_bucket

Uploaded images, attachments and avatars, and the built sites of the built-in target

Meilisearch (optional)

/var/lib/meilisearch/data, or the meili_data volume

The search index. Kribble can rebuild it from the database, see below.

Configuration

/opt/kribble/config.toml, systemd drop-ins, Docker .env and docker-compose.override.yml, the reverse proxy config

Holds every secret. Without jwt_secret and refresh_secret everyone has to sign in again; without the storage keys you can't reach the files.

Publish target keys

for example /etc/kribble/keys

SSH keys and known_hosts for [[publish_targets.ssh]]

You don't need to back up:

  • Redis. It holds only caches.

  • /opt/kribble/downloads. It comes from the release tarball.

  • Sites on external targets. They are built again from the notes.

The search index can be rebuilt. Everything in Meilisearch comes from the database. If you lose the Meilisearch data, click Rebuild index in the admin panel, or restart kribble-web, which finds the index empty and rebuilds it from the database in the background. Until the rebuild is done, search misses the notes it hasn't reached yet. A backup of Meilisearch only saves that wait, which matters on a server with many notes.

Keeping the database and files consistent

Files that no note or kept version uses any more are deleted by kribble-builder 24 hours after they were released, and uploads never saved into a note after 1 hour. So:

  1. Dump the database first.

  2. Then copy the files, within 24 hours of the dump.

Every file the dump refers to is then still in the copy.

Making a backup

# Database (custom format, compressed)
sudo -u postgres pg_dump -Fc kribble > /backup/kribble-$(date +%F).dump

# Files, disk backend
sudo tar czf /backup/kribble-files-$(date +%F).tar.gz -C /opt/kribble data

# Files, S3 backend (rclone remote "s3" configured for your provider)
rclone sync s3:kribble-uploads /backup/kribble-uploads
rclone sync s3:kribble-public  /backup/kribble-public

# Search index (optional): stop Meilisearch briefly and copy its data
sudo systemctl stop meilisearch
sudo tar czf /backup/meili-$(date +%F).tar.gz -C /var/lib/meilisearch data
sudo systemctl start meilisearch

# Configuration
sudo cp /opt/kribble/config.toml /backup/config-$(date +%F).toml
cd /opt/kribble-docker

# Database
docker compose exec -T db pg_dump -U kribble -Fc kribble > /backup/kribble-$(date +%F).dump

# Files, disk backend (the kribble_files volume)
docker run --rm -v "$(basename "$PWD")_kribble_files:/data" -v /backup:/backup alpine \
  tar czf /backup/kribble-files-$(date +%F).tar.gz -C /data .

# Search index (optional): stop Meilisearch briefly and copy the volume
docker compose stop meilisearch
docker run --rm -v "$(basename "$PWD")_meili_data:/meili_data" -v /backup:/backup alpine \
  tar czf /backup/meili-$(date +%F).tar.gz -C /meili_data .
docker compose start meilisearch

# Configuration
cp config.toml .env /backup/

Compose names volumes <project>_<volume>, where the project defaults to the directory name. docker volume ls shows the real names.

While Meilisearch is stopped, notes still save, but full-text search fails and the notes changed meanwhile aren't added to the index. Copy it at a quiet time, or use Meilisearch's own dump feature, which works while it runs. If notes changed while it was stopped, click Rebuild index on the System tab of the admin panel afterwards (see Troubleshooting).

Restoring

Stop Kribble
sudo systemctl stop kribble-web kribble-builder

With Docker: docker compose stop kribble-web kribble-builder.

Restore the database

Into an empty database owned by kribble:

sudo -u postgres dropdb kribble
sudo -u postgres createdb kribble -O kribble
sudo -u postgres pg_restore --no-owner --role=kribble -d kribble /backup/kribble-2026-10-01.dump

With Docker:

docker compose exec db dropdb -U kribble kribble
docker compose exec db createdb -U kribble kribble
docker compose exec -T db pg_restore -U kribble --no-owner -d kribble < /backup/kribble-2026-10-01.dump
Restore the files

Put the disk directory back (sudo tar xzf … -C /opt/kribble, then sudo chown -R kribble:kribble /opt/kribble/data), or sync the buckets back with rclone sync.

Restore Meilisearch, or start it empty

To restore a backup, stop Meilisearch, replace its data directory or volume with the backup from the same time as the database, and start it again. Use the same Meilisearch version that wrote the backup.

Without a backup, start Meilisearch with an empty data directory or volume. kribble-web rebuilds the index when it starts in the next step.

Restore the configuration and start

Put back config.toml (with the same jwt_secret, refresh_secret, Meilisearch key and storage credentials), then:

sudo systemctl start kribble-web kribble-builder

A restored search index rarely matches the database exactly: search may miss notes, or find them by older text. Once Kribble runs, click Rebuild index on the System tab of the admin panel to bring it up to date. Notes that no longer exist are never shown, as results are checked against the database.

Sites on the built-in target come back with the file storage. Sites on external targets keep whatever was last deployed there. To bring them up to date, rebuild them (kribble redeploy, or Publish in the web app).

Upgrading

Both binaries create and update the database tables when they start ([database].auto_migrate, on by default). There is no separate migration step, so always run kribble-web and kribble-builder from the same release.

Read the release notes and back up

Make a database backup right before upgrading. It is your way back if the new version changes the database.

Install the new release

On a Linux host, unpack the new tarball and run sudo bash install.sh. It stops both services, keeps the old binaries as /opt/kribble/kribble-web.bak and kribble-builder.bak, installs the new ones and the new downloads/, and starts the services.

With Docker, build or load the new kribble:latest and run docker compose up -d.

Check
journalctl -u kribble-web -n 50       # or: docker compose logs --tail 50 kribble-web
curl -s https://kribble.example.com/api/v1/config

The version in /api/v1/config is the new one. Connected CLIs report the mismatch and update with kribble update; desktop apps update themselves if the release includes them.

If the new release indexes notes differently from the old one, kribble-web rebuilds the search index in the background after it starts. The log shows search: rebuilding the index, then search: index rebuilt with the number of notes and the time it took. Search works meanwhile, but can miss notes or headings until the rebuild is done. With several kribble-web processes, each one rebuilds; the result is the same.

If a migration fails, the binary logs Database migration failed and exits. systemd restarts it every 5 seconds, so the log repeats until you fix the cause or roll back.

With auto_migrate = false, nothing changes the schema. Only use it if you apply the schema changes some other way.

Upgrading PostgreSQL, Meilisearch and Redis

  • PostgreSQL: use a dump and restore, or pg_upgrade, as for any PostgreSQL database. Kribble needs 16 or newer.

  • Meilisearch: Kribble needs 1.12 or newer. Its data directory is tied to the Meilisearch version that wrote it. Either follow Meilisearch's upgrade instructions (export a dump with the old version, import it with the new one), or start the new version with an empty data directory (with Docker, remove the meili_data volume) and restart kribble-web, which rebuilds the index. docker-compose.yml pins getmeili/meilisearch:v1.12; change the tag deliberately.

  • Redis: replace it at will. Kribble refills the caches.

Rolling back

Kribble's automatic migrations add tables, columns and indexes and don't remove any. An older binary therefore often runs on a newer schema, but this isn't guaranteed. The safe way back is the binaries and the database backup together:

sudo systemctl stop kribble-web kribble-builder
cd /opt/kribble
sudo cp kribble-web.bak kribble-web
sudo cp kribble-builder.bak kribble-builder
# restore the database backup made before the upgrade (see Restoring), then:
sudo systemctl start kribble-web kribble-builder

install.sh keeps only one .bak per binary, and the downloads/ directory isn't kept. To get the old CLI downloads back too, run the old release's install.sh instead.

Tag images before you upgrade (docker tag kribble:latest kribble:<old-version>). To roll back, stop the Kribble containers, restore the database, point kribble:latest back at the old image (docker tag kribble:<old-version> kribble:latest) and run docker compose up -d.

Changes made after the backup are lost when you restore it. If you only roll back the binaries and keep the newer database, test it on a copy first.