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 | Where | Why |
|---|---|---|
PostgreSQL | the | Accounts, organisations, spaces, notes and their history, comments, access tokens, published-site settings. Everything except files. |
File storage |
| Uploaded images, attachments and avatars, and the built sites of the built-in target |
Meilisearch (optional) |
| The search index. Kribble can rebuild it from the database, see below. |
Configuration |
| Holds every secret. Without |
Publish target keys | for example | SSH keys and |
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.
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:
Dump the database first.
Then copy the files, within 24 hours of the dump.
Every file the dump refers to is then still in the copy.
# 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).
sudo systemctl stop kribble-web kribble-builder
With Docker: docker compose stop kribble-web kribble-builder.
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
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.
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.
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).
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.
Make a database backup right before upgrading. It is your way back if the new version changes the database.
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.
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.
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.
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.