Upgrading
Migrations first, in every shape, and the two things that make an upgrade unrecoverable rather than annoying.
An upgrade is: new image, migrations applied, server ready. The app checks and applies pending migrations on every startup, before serving requests, whether its PostgreSQL is embedded or external.
While the version is 0.0.x, nothing in the application is promised to
stay put. Read the release notes before upgrading
a deployment other people use.
The short version, per shape
The account menu — the avatar in the top right — shows the version you are running and checks for a newer one when you press the button. What it tells you to do about it is one of these.
| You run | Upgrading is |
|---|---|
| The desktop app | Nothing. It updates itself: it offers the new version and installs it when you accept. |
bunx @datagripe/cli personal |
bunx @datagripe/cli@latest personal. The data directory is untouched. |
| One container | docker pull … && docker restart datagripe. It migrates itself on start. |
| Compose | docker compose pull && docker compose up -d. The migration service runs first. |
| Kubernetes or Helm | Upgrade the image or chart, then restart. The app checks migrations before serving; existing hooks and init containers may still pre-apply them. Image tags and pull policy determine which version starts. |
| A checkout | git pull && bun install && bun run db:migrate. |
The rest of this page is what those commands are doing, and the two things worth doing before any of them.
Back up first
Two things make an upgrade unrecoverable rather than annoying, and both are cheap to avoid.
The database. The durable state is the application PostgreSQL: accounts, sessions, workspaces, datasource metadata and encrypted secrets, documents, layouts, query history.
pg_dump --format=custom --file=datagripe-$(date +%Y%m%d-%H%M).dump "$APP_DATABASE_URL"
CONNECTION_ENCRYPTION_KEY. The datasource passwords in that dump
are encrypted with it. A backup without the key restores everything
except the ability to open any datasource. Store the key with the same
care as the backup and not in the same place.
Embedded shapes
The personal CLI, the desktop app and the zero-config container migrate themselves on start. Upgrading is replacing the binary or pulling the image.
The desktop app is the one shape that needs nothing from you: it checks its own release feed, offers the new version, and replaces itself when you accept. Declining asks again next time it starts.
bunx @datagripe/cli@latest personal docker pull ghcr.io/datagripe/datagripe && docker restart datagripe
The data directory is untouched. It is the thing worth backing up.
Shared shapes
The app checks schema_migrations on every startup, in embedded and
external database modes, and applies missing files before accepting HTTP
or WebSocket connections. Each migration and its history row commit
together. A PostgreSQL advisory lock serializes concurrent app starts and
manual runners; a failed migration rolls back and stops startup. The app
database account must have permission to apply the schema changes.
The image also has a migration-only entry point for an explicit pre-rollout step:
docker run --rm \ -e APP_DATABASE_URL=… -e CONNECTION_ENCRYPTION_KEY=… -e SESSION_SECRET=… \ ghcr.io/datagripe/datagripe migrate
| Shape | Runs the migration as |
|---|---|
| Compose | a one-shot service, before the app |
| Helm | a pre-install,pre-upgrade hook Job |
| Plain manifests | an init container |
| By hand | bunx @datagripe/cli migrate |
So in practice: docker compose pull && docker compose up -d, or
helm upgrade datagripe oci://ghcr.io/datagripe/charts/datagripe, and
the migration is already in the path.
Already-recorded migrations are skipped. Concurrent runners wait on the same advisory lock and recheck history before executing each file.
Restarting into a pulled image
With imagePullPolicy: Always on a tag that moved, an app-container
restart may pull the new image. Completed init containers do not rerun,
but the app itself checks migration history before serving. The
restart server button therefore includes the schema check in both
database modes. A pinned image still needs its tag updated to get a
new release.
The button requires a supervisor that will restart the process. Kubernetes
enables it by default; other supervised shapes can set
RESTART_TO_UPDATE=true — see updates.
MCP missing after a 0.0.13 upgrade
Version 0.0.13 silently hid the MCP sidebar header when its status request failed. One cause is a shared app database missing migration 0027: the status query attempted to read the new functionality columns. Apply the pending migrations using the upgraded image or CLI, then reload the app. This does not revoke tokens or reset the project's MCP setting.
The corrected client keeps the launcher visible on status failures and
shows status unknown with a retry action. Its management tab reports
missing migrations explicitly. If migration 0027 is already applied,
check the server log for the failed mcp.status action; a missing header
alone does not prove the server is off.
Pin the version
The chart's version is the app's version, and the image's tag should be
too. latest is fine for trying it and wrong for a service other people
depend on — an upgrade should be a thing you did, not a thing that
happened.
helm upgrade datagripe oci://ghcr.io/datagripe/charts/datagripe --version 0.0.16
Restoring
createdb datagripe_restore pg_restore --dbname=datagripe_restore datagripe-YYYYMMDD-HHMM.dump APP_DATABASE_URL=postgres://…/datagripe_restore bun run db:migrate
Run the migration after every restore: it is idempotent, and it brings an older dump up to the current schema before the server starts.
Sessions survive a restore. They only become invalid if you also rotate
SESSION_SECRET, and the two can coexist safely.
Worth doing quarterly into a scratch database rather than discovering the
procedure during an incident. Compare
select count(*) from users and select count(*) from connections
between the two databases.
Rotating the encryption key
Add the new key as version 2 in the keyring, restart, and re-save connections opportunistically. Old key versions keep decrypting, so there is no flag day and no window where a datasource cannot be opened.
Related
- Release notes — what changed.
- Configuration — every variable, a page per decision.
- Updates — the version check and the restart button.
docs/operations.md— backups, the audit log, and the full production checklist.
This page as Markdown, for an agent or a diff.