There are two backup scopes, and they are independent:
| Scope | Holds | Who backs it up |
|---|---|---|
| The Installer | Its own database, its secrets, and the deployment files it generated | You |
| DataMind OS | The business data of every deployed service | You — the Installer does not back up the stack it deploys |
Five things, and only one of them is truly irreplaceable.
| Store | Contents | Recreatable? |
|---|---|---|
configurations table | Every generated and operator-supplied deployment value — the whole deployment environment | Only from a backup |
users, refresh_tokens | Administrator accounts and live sessions | Yes, by registering again |
system_secrets | The Azure client secret and the VM user, encrypted | Yes, if you still have the values |
deployment_actions, deployment_job_events | The audit trail and the log history of every job | No — history cannot be recreated |
| Secrets volume | jwt.secret, encr.key, curato.token, pg.pass | Signing and service tokens regenerate; the encryption key does not |
The configuration rows are the important part. They are what renders deployment/.env, and a lost
configuration set means reconstructing the entire environment by hand.
Back up the database and the secrets volume together. A database dump without encr.key
restores rows whose secret values can never be decrypted again.
In the pgdata Docker volume; dump it with pg_dump from inside the container.
docker exec delamain-postgres pg_dump -U postgres -d delamain > delamain-$(date +%F).sql
pg_dump produces a consistent snapshot even while the Installer is running, so you do not need a
maintenance window for this. Restoring is the reverse:
cat delamain-2026-09-29.sql | docker exec -i delamain-postgres psql -U postgres -d delamain
On a development install the container is delamain-postgres-dev and the database name is still
whatever POSTGRES_DATABASE_NAME says — delamain by default in both files.
Two, by default.
| Volume | Mounted at | Contents |
|---|---|---|
pgdata | /var/lib/postgresql/data | The Installer's database |
delamain_secrets | /usr/src/app/secrets in the backend, /run/secrets in Postgres | The four generated secret files |
The development compose file uses the _dev variants, pgdata_dev and delamain_secrets_dev, so a
development install and a production install on the same host do not share state.
docker volume ls | grep delamain
Three classes of file, in descending order of how much trouble losing them causes.
| File | Why it matters |
|---|---|
encr.key in the secrets volume | Encrypts every stored secret and generated value. Losing it means re-entering them all |
pg.pass in the secrets volume | The password of the running database role; needed to reach existing data |
the Installer's own .env | Holds POSTGRES_USER, POSTGRES_DATABASE_NAME and DOCKER_SOCK, which must keep matching the existing installation |
deployment/.env | Regenerable from configuration rows — but only if the database survives |
deployment/docker-compose.override.yml | Yours, and never written by the Installer. Nothing recreates it |
deployment/docker-compose.yml | Re-downloadable from blob storage |
deployment/.env.unified.template | Re-downloadable from blob storage |
Archive the volume's contents with a throwaway container.
docker run --rm \ -v delamain_secrets:/secrets:ro \ -v "$PWD":/backup \ alpine tar czf /backup/delamain-secrets-$(date +%F).tgz -C /secrets .
Restoring is the same operation in reverse. Keep the archive somewhere with the same protection as the secrets themselves — it is, effectively, the key to the deployment's credentials.
delamain_secrets volume?The Installer comes back up, but stored secrets do not. On the next start it generates whatever is missing: a new JWT signing secret, a new encryption key, a new service token. The consequences follow from that:
| Lost file | Consequence |
|---|---|
encr.key | Every stored secret and generated value fails to decrypt. The Azure secret reports a decryption error and must be re-entered |
jwt.secret | Everyone is signed out |
curato.token | The service channel breaks until the deployment .env is regenerated with the new value |
pg.pass | Worse than the others. PostgreSQL generates a password only when the file is empty, and the existing database role keeps its original password — so the Installer cannot connect until you reconcile the two |
Losing encr.key is not a self-healing failure. Restore it from your backup, or re-enter every
secret and generated value by hand. See
Configuration and variables.
Per service, using each service's own mechanism — plus a copy of the resolved configuration and the compose file. The Installer has no backup feature and does not run backups for the stack.
A workable minimum:
deployment/.env, deployment/docker-compose.yml and deployment/docker-compose.override.yml.GET /api/configs) so you can reconstruct the
environment if the Installer's database is lost.Stop the stack before copying raw data-directory volumes. A file-level copy of a running database's data directory is not a backup — it is a plausible-looking file that will not restore.
.env live, and should I back it up?At deployment/.env on the host — and yes, take a copy, but know what it is. It is a generated
artifact: every save, seed and restore rewrites it in full from the Installer's configuration rows.
It also contains live service credentials, so treat the copy as a secret.
It is worth having because it is the fastest way to see what the deployment was actually running at a point in time, and because it lets you start the stack if the Installer is unavailable.
Yes, with two caveats. Your host-specific and encrypted values come with the dump, and you must also move the secrets volume.
.env values for POSTGRES_USER,
POSTGRES_DATABASE_NAME and DOCKER_SOCK.The caveats: host-computed values are recalculated, not restored. Memory limits are derived from the new machine's total memory, so a restored configuration set will be re-sized for the new host. And the deployment files are re-downloaded rather than restored, so a hand-edited override file must be copied across deliberately.
Moving to a new host walks through the same procedure as a planned migration, including the checks to run before you switch production over.
Dump the database live; quiesce only the services whose data you are copying. The split is:
| What | Safe live? |
|---|---|
Installer database (pg_dump) | Yes — the dump is consistent |
| Installer secrets volume (files) | Yes — the files are written once and then only read |
Configuration export (GET /api/configs) | Yes |
| DataMind OS service data volumes | No — stop or quiesce the service first |
The deployment docker-compose.yml and .env | Yes |
The database dump and the secrets archive should be taken close together. A dump restored beside an older encryption key produces exactly the decryption failure described above.
The Installer's self-update backs up its own compose file, and nothing else. Before the switch,
the helper copies the live compose file to docker-compose.yml.pre-update next to it, and restores
that file if the update fails. That backup protects the update itself; it is not a data backup.
A DataMind OS update takes no backup at all. Take one before you update production.
docker-compose.yml.pre-update is overwritten by the next self-update. Do not treat it as a
version history.
Yes, from blob storage, with an administrator call. Both files are published artifacts, so they can be fetched again at any time:
curl -X POST http://localhost:8000/api/registry/download-compose-file \ -H "Authorization: Bearer <access-token>" curl -X POST http://localhost:8000/api/registry/download-env-template \ -H "Authorization: Bearer <access-token>"
The Installer also restores them by itself at boot if they are missing and the Azure client secret is configured. Downloading the environment template re-seeds the configuration rows as a side effect, so read that question in Configuration and variables before you run it.
These downloads use the Azure client secret. Without it they fail, and the failure is reported as a missing deployment file rather than as an authentication problem — check the secret first.
Page through the actions endpoint and save the JSON, or query the table directly.
curl -s "http://localhost:8000/api/deployment/actions?limit=100&offset=0" \ -H "Authorization: Bearer <access-token>" \ -o actions-$(date +%F).json
-- from psql inside delamain-postgres
\copy (SELECT action, services, job_id, status, exit_code, user_id, detail, created_at
FROM deployment_actions ORDER BY created_at) TO 'actions.csv' WITH CSV HEADERThe table is never pruned, so a periodic export gives you an audit history that outlives the host.
Stop the containers, remove the volumes, and start again — this destroys everything the Installer holds. It is the right action when you want to rebuild a deployment from scratch and you have decided the configuration set is not worth keeping.
Run these from the Installer's install directory:
docker compose down docker volume rm delamain_secrets pgdata rm -rf deployment/ docker compose up -d
This is irreversible. The database holds the entire deployment configuration and the audit trail; the secrets volume holds the encryption key. If either matters, back it up first. Removing the deployment files does not touch the DataMind OS stack's own data or its Docker volumes — those are separate and must be removed deliberately if that is what you want.
For step-by-step backup and restore procedures, see Backup and restore.