Skip to content

Backups

Every POS device also keeps a full copy of its shop’s sales, so a lost hub doesn’t lose sales. Backups of the hub keep the rest: the menu and staff as the back office left them, back office accounts, device pairings and plugin data.

Every 24 hours the hub takes a snapshot of every database in its data folder into <data>/backups/<date and time>/, and keeps the last 7. The first one is taken a minute after the hub starts, unless a recent one exists.

A snapshot is a consistent copy taken while the hub keeps running (SQLite’s online backup), one gzip file per database, plus a manifest.json written last. A folder without a manifest is unfinished; ignore it.

Setting Default
OPENKEDAI_BACKUP_INTERVAL_HOURS 24; 0 turns snapshots off
OPENKEDAI_BACKUP_KEEP 7 snapshots

Snapshots are in the same place as the data, so they protect against mistakes and bad updates, not against losing the machine or its disk. For that, copy them elsewhere.

Copies to S3-compatible storage (optional)

Section titled “Copies to S3-compatible storage (optional)”

The hub can upload every snapshot to a bucket you own: Cloudflare R2, Backblaze B2, AWS S3, DigitalOcean Spaces, MinIO and others. Nothing leaves the hub unless you set this up.

  1. Create a bucket, and an access key that can write to it (read and delete aren’t needed).

  2. Set:

    Setting Example
    OPENKEDAI_BACKUP_S3_URL the bucket’s URL: https://<account>.r2.cloudflarestorage.com/kedai-backups, https://s3.us-west-004.backblazeb2.com/kedai-backups, https://kedai-backups.s3.ap-southeast-1.amazonaws.com
    OPENKEDAI_BACKUP_S3_ACCESS_KEY_ID the key’s id
    OPENKEDAI_BACKUP_S3_SECRET_ACCESS_KEY the key’s secret
    OPENKEDAI_BACKUP_S3_REGION optional: read from AWS and Backblaze URLs, else us-east-1 (R2 and MinIO accept it)
    OPENKEDAI_BACKUP_S3_PREFIX optional, default openkedai/
  3. Restart the hub. Its log says backups with the bucket on start, and backup uploaded to S3 after each snapshot.

Objects go to <prefix><snapshot>/…, with manifest.json last. Old snapshots stay in the bucket: set a lifecycle rule on the bucket (e.g. delete after 30 days) to keep its size in check.

When an update changes a shop’s database, the hub first keeps a plain copy of the file in <data>/backups/pre-migration/<date and time>/. These aren’t deleted automatically.

  1. Stop the hub (docker compose stop, sudo systemctl stop openkedai…).

  2. Move the current data folder aside, keeping it until you’re sure.

  3. For each .gz file in the snapshot, unzip it to the same path in a new data folder. With the snapshot folder as the current directory:

    Terminal window
    for f in $(find . -name '*.sqlite.gz'); do
    mkdir -p "/var/lib/openkedai/$(dirname "$f")"
    gunzip -c "$f" > "/var/lib/openkedai/${f%.gz}"
    done
    chown -R openkedai:openkedai /var/lib/openkedai # installer: the service's user

    (With Docker, unzip into the openkedai-data volume the same way, e.g. from a temporary alpine container with the volume mounted.)

  4. Start the hub. Every snapshot carries its own log id, so when a device next syncs, it sees the hub was put back from a backup. It sends all of its own sales and its copy of the menu again, and reads the hub’s records from the start. The hub keeps one copy of each, so sales made since the snapshot come back from the devices that made them, and every device ends up with all of them.

What a snapshot can’t bring back: back office accounts and settings changed after it, and devices paired after it. Those devices say they were removed; pair them again from Settings → Hub, and their sales follow.

A pre-migration copy is a plain SQLite file: put it back at tenants/<shop>/shop.sqlite with the older version of OpenKedai.

For AI tools: llms.txt ·llms-full.txtopenkedai.com