Back up and restore
What holds data
Section titled “What holds data”The volume names have the prefix of the Compose project, mcp-platform_ by default (the value of COMPOSE_PROJECT_NAME if you
set it). List them with docker volume ls.
| Volume | Content | Back up |
|---|---|---|
secrets |
The internal CA and certificates, every generated password and key, the OpenBao AppRole | yes |
openbao-keys |
The OpenBao unseal key and the provisioner token | yes |
openbao-data |
OpenBao storage (Raft): users’ stored credentials, OAuth client secrets, Transit keys | yes, as a snapshot |
keycloak-db |
Keycloak database: realm, users, OTP | yes, as a dump |
gateway-db |
Gateway database: servers, approvals, scope mapping, overrides, audit | yes, as a dump |
broker-db |
Broker database: systems, connections | yes, as a dump |
gateway-redis, gateway-redis-replica, broker-redis |
Short-lived state: MCP client sessions (1 h idle), pending confirmations of destructive tools (about 4 min), rate-limit counters (1 min), account connections in progress (10 min), locks and replay markers | no: start them empty |
Outside the volumes, keep .env, certs/ (including tls.key) and servers.yaml. Service account keys that you wrote to
OpenBao with bao kv put are in openbao-data.
Back up
Section titled “Back up”Run these commands in the directory of compose.yaml, with the platform running.
-
Dump the three databases:
Terminal window for db in gateway-postgres:gateway keycloak-postgres:keycloak broker-postgres:broker; dodocker compose exec -T "${db%%:*}" pg_dump -U "${db##*:}" "${db##*:}" > "${db##*:}.sql"done -
Save a snapshot of OpenBao:
Terminal window docker compose exec -e BAO_CACERT=/trust/ca.crt openbao-unseal sh -c \'BAO_TOKEN=$(cat /keys/provisioner-token) bao operator raft snapshot save /tmp/openbao.snap && cat /tmp/openbao.snap' > openbao.snap -
Pack the
secretsandopenbao-keysvolumes:Terminal window docker run --rm -v mcp-platform_secrets:/s:ro -v mcp-platform_openbao-keys:/k:ro -v "$PWD":/b alpine tar czf /b/secrets.tgz -C / s k -
Copy
gateway.sql,keycloak.sql,broker.sql,openbao.snap,secrets.tgz,.envandcerts/to the backup location.
Restore
Section titled “Restore”Restore onto the bundle of the same version, on the same host or a new one. Replace the volume prefix if your project name differs.
-
If the platform already runs on this host, delete it together with its volumes:
Terminal window docker compose down -v -
Restore the two secret volumes from the tarball:
Terminal window docker volume create mcp-platform_secretsdocker volume create mcp-platform_openbao-keysdocker run --rm -v mcp-platform_secrets:/s -v mcp-platform_openbao-keys:/k -v "$PWD":/b alpine tar xzf /b/secrets.tgz -C / -
Put
.envandcerts/in place. -
Start the databases, then load the dumps into them:
Terminal window docker compose up -d keycloak-postgres gateway-postgres broker-postgresdocker compose exec -T gateway-postgres psql -U gateway gateway < gateway.sqldocker compose exec -T keycloak-postgres psql -U keycloak keycloak < keycloak.sqldocker compose exec -T broker-postgres psql -U broker broker < broker.sqlIf the services need a moment, wait until
docker compose psshows the three databases healthy before you load.
-
Start OpenBao alone. Do not start
openbao-unsealyet: with the restored key it refuses to initialise an empty server, and everything that waits for it stays down.Terminal window docker compose up -d openbao -
Load the snapshot. Initialise the empty server once with a throwaway key, unseal it with that key, restore the snapshot and restart OpenBao:
Terminal window docker compose exec openbao bao operator init -key-shares=1 -key-threshold=1docker compose exec openbao bao operator unseal # paste "Unseal Key 1" from the init outputdocker compose exec -T openbao sh -c 'cat > /tmp/openbao.snap' < openbao.snapread -rs BAO_TOKEN && export BAO_TOKEN # paste "Initial Root Token" from the init outputdocker compose exec -e BAO_TOKEN openbao bao operator raft snapshot restore -force /tmp/openbao.snapunset BAO_TOKENdocker compose exec openbao rm -f /tmp/openbao.snapdocker compose restart openbaoThe snapshot replaces the throwaway key and root token. From now on OpenBao opens with the restored key from
openbao-keys. Use a snapshot younger than 32 days: the provisioner token in it expires after 768 hours without renewal, and without itopenbao-unsealcannot provision OpenBao. -
Start the rest:
Terminal window docker compose up -ddocker compose logs openbao-unsealends withopenbao-unseal: unsealed and provisioned.
docker compose psshows every service healthy.- You can sign in to the portal, and the servers appear in Administration.
- A user who connected an account before the backup can call a tool without connecting again. If users get
credential_required, the OpenBao data was not restored.
Keep the unseal key off the host
Section titled “Keep the unseal key off the host”openbao-keys lets anyone who can read Docker volumes on the host unseal OpenBao. To keep the key elsewhere, back up
openbao-keys, then remove it from the host:
docker compose stop openbao-unsealdocker volume rm mcp-platform_openbao-keysAfter every start, unseal by hand with bao operator unseal from a shell on the secrets network. The broker does not start until
OpenBao is unsealed.
