Upgrades and migrations
A self-hosted deployment is yours to upgrade and to back up. This page covers how releases are published, what happens to the database on an upgrade, the order to do it in, and how to take a backup you can restore.
Releases and image tags
Each release has a Git tag (v0.17.0) and an image with the same version
number and no v: ghcr.io/writeitai/remember-stack:0.17.0. There is no
latest tag. The release on GitHub attaches the compose.yaml and
.env.example for that version (the second downloads as
default.env.example) and openapi.json.
compose.yaml names the image tag, so the version you run is the version
of the compose.yaml you start. The client package on PyPI, remember,
is released from the same tag with the same version number.
What setup does on an upgrade
The setup service runs before the API and workers every time you run
docker compose up. It:
- applies every database migration the new release brings
(
alembic upgrade head); - checks that the deployment id, slug and name still match the database;
- installs any assured operations and example saved queries the release adds;
- checks that the embedding model still matches the stored vectors, then re-publishes the search channels, rebuilding entity profile vectors when needed.
The API and the workers start only after setup exits successfully. If a
migration fails, setup exits with an error, nothing else starts, and
docker compose logs setup shows why.
Migrations only go forward. Some refuse to convert data in a way that would lose it, and there is no supported downgrade. To go back to an earlier release, restore a backup taken before the upgrade.
Upgrading does not reprocess documents already in the memory. They keep what the earlier release extracted from them; new documents are processed by the new code.
Some releases cannot convert existing data
When a release changes what stored data means in a way that cannot be
converted, its migrations stop instead of guessing, and setup exits
with an error that ends in "recreate the deployment and ingest its
sources again". Start from an empty deployment and send your source
documents again. Keep your source files for this reason. See
setup refuses an existing database.
Upgrade step by step
-
Read the release notes on GitHub for the version you are moving to.
-
Back up, as described below.
-
Get the new files:
git fetch --tags git checkout v<new-version> diff .env .env.exampleAdd any new required variable to your
.env. Keep your deployment id, slug and name as they are. -
Stop the old processes, so no old worker runs against a database that is being migrated:
docker compose downdownremoves the containers and keeps the volumes, and with them the memory. -
Get the new images:
docker compose pull api postgresAll app services share the image
pull apifetches. Each release also publishes its own PostgreSQL image, sopostgresis pulled too. -
Start:
docker compose up -d --wait docker compose logs setup curl http://localhost:8000/deploymentbuild_revisionin/deploymentnames the source commit of the image now serving.
PostgreSQL prerelease versions
RememberStack runs on a PostgreSQL 19 beta (19beta3 at v0.17.0). A
PostgreSQL prerelease does not promise that its data directory opens under
the next prerelease or the final release. The project has not published a
procedure for moving a deployment's data across such a change.
If a release changes the PostgreSQL image, its release notes are the place to look. Without instructions there, move the data with a logical dump and restore (below) into a new PostgreSQL volume, or start a new deployment and send your documents again.
The deployment id is permanent
REMEMBERSTACK_SELFHOST_DEPLOYMENT_ID names the deployment: its trust
domain. Every token is bound to it, and every stored row belongs to it.
The install step generates it once, and the first setup records it.
A database holds exactly one deployment. If the id in .env differs from
the recorded one, setup refuses to start and names the recorded id, so
the API and workers do not start either. Restore the recorded value. The
same check covers the slug, the name, the default language and the bucket
names: setup names the ones that changed. Change none of them after the
first start.
To start over under a new id, remove the volumes
(docker compose down --volumes) and send your documents again.
Back up
Backups are your responsibility. RememberStack does not schedule them, and the project does not test a restore procedure. What follows is grounded in how the Compose deployment stores its data.
A deployment's state lives in four Docker volumes:
| Volume | Must be backed up |
|---|---|
rememberstack_postgres-data | Yes: the database |
rememberstack_object-store-data | Yes: original files and derived artifacts |
rememberstack_forget-manifests | Yes, with the others: hard-forget manifests |
rememberstack_app-state | Optional: working directories and debug captures |
Filesystem-view snapshots in the object store can be rebuilt; everything else in the first three volumes cannot. The database and the object store refer to each other, so back them up at the same moment.
A cold backup of the volumes
The simplest consistent backup stops the stack and copies the volumes:
docker compose stop
mkdir -p backup
for volume in postgres-data object-store-data forget-manifests app-state; do
docker run --rm \
-v "rememberstack_${volume}:/source:ro" \
-v "$PWD/backup:/backup" \
alpine tar -czf "/backup/${volume}.tar.gz" -C /source .
done
docker compose startKeep the backup/ directory and your .env together: a restore needs the
same deployment id and the same database and object-store credentials.
To restore it into an empty stack on the same or another machine, with the
same .env:
docker compose down -v # deletes the current volumes
docker compose create # creates empty volumes and containers
for volume in postgres-data object-store-data forget-manifests app-state; do
docker run --rm \
-v "rememberstack_${volume}:/target" \
-v "$PWD/backup:/backup:ro" \
alpine tar -xzf "/backup/${volume}.tar.gz" -C /target
done
docker compose up -d --wait
docker compose exec -T api remember ops graph-catalog ensureThe last command checks that the graph definitions in the restored database match this release (Operating the pipeline).
A logical backup
pg_dump backs up the database while it runs:
docker compose exec -T postgres \
pg_dump -U rememberstack -d rememberstack --format=custom > rememberstack.dump
docker compose exec -T postgres \
pg_dumpall -U rememberstack --roles-only > roles.sqlUse your own user and database names from .env. The second file matters:
the migrations create a separate, restricted PostgreSQL role for SQL
queries, and roles are not part of pg_dump. Copy the object-store buckets
(remember-raw, remember-artifacts, remember-corpusfs) at the same
time with an S3 tool such as rclone sync. Restoring a logical backup means
creating the roles, restoring the dump into an empty database with
pg_restore, copying the buckets back, and running setup; the project
has not tested this path.
Hard-forget manifests
The forget-manifests volume holds the list of documents that were
permanently removed, so a restore can remove them again from an older
backup. A deployment started from compose.yaml has no command that
creates such manifests. If the volume does hold manifests for your
deployment, the API refuses to start: honouring them needs a recovery
procedure the Compose deployment does not include.