A deployment has two backups. Take both on a schedule and before every update.
| Part | What it holds | Size | Section |
|---|---|---|---|
| The DataMind Installer | Its database, its secrets and the deployment files it writes | Small, a few files | Back up and Restore |
| DataMind OS data | Your lake, warehouse and dashboards, in the deployment's Docker volumes | Large, grows with your data | Back up DataMind OS data |
The DataMind Installer keeps its state in two Docker volumes and one directory:
| What | Where | What is in it |
|---|---|---|
| Database | delamain_pgdata | Every deployment setting, the user accounts and their sessions, the audit trail and the job history |
| Secrets | delamain_delamain_secrets | jwt., encr., pg., curato. |
| Files | / | docker-compose., the optional ., and the deployment directory |
Compose prefixes each volume with the project name, which is the install directory's name. The
commands below use the names for /. If you installed elsewhere, list the real names first:
docker volume ls --filter name=delamain_
Check it worked. The output lists both volumes:
DRIVER VOLUME NAME local delamain_delamain_secrets local delamain_pgdata
Back up encr. with the database dump it belongs to. What each secret file does is in
What lives on the host.
Keep encr. with its dump. It encrypts the Azure client secret, every configuration value
marked secret and every value the DataMind Installer generated, including the DataMind OS database
passwords. Without it, those values read back as empty. The next Start, Apply changes or
Update writes them empty into deployment/, an Update also generates new passwords, and the
DataMind OS services can no longer open their data. Before any of these actions, re-enter each value
in Configuration, copying it from the deployment/ in your
files archive, and re-enter the Azure client secret under System secrets with the
client_secret DataMind issued with your licence.
You do not need to stop the DataMind Installer. Start when Status shows no running job.
Run every step as root, in the same shell: later steps use the variables set here. umask 077 makes
the backup directory readable by root only. Keep the archives in it, and protect them the same way
wherever you copy them.
cd /opt/delamain umask 077 mkdir -p /root/datamind-backup BACKUP=/root/datamind-backup DATE=$(date +%F)
The command reads the database user and name from the container, so a changed POSTGRES_USER or
POSTGRES_DATABASE_NAME still works.
docker exec delamain-postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \ > "$BACKUP/datamind-installer-db-$DATE.sql"
Check it worked.
head -n 3 "$BACKUP/datamind-installer-db-$DATE.sql"
-- -- PostgreSQL database dump --
The command uses the PostgreSQL image the DataMind Installer already runs, so it needs no extra download.
docker run --rm \ -v delamain_delamain_secrets:/data:ro \ -v "$BACKUP":/backup \ unistream.azurecr.io/docker-hub/library/postgres:16 \ tar czf "/backup/datamind-installer-secrets-$DATE.tgz" -C /data .
Check it worked.
tar tzf "$BACKUP/datamind-installer-secrets-$DATE.tgz"
./ ./pg.pass ./jwt.secret ./encr.key ./curato.token
The order can differ. All four names must be there.
The command adds . only when it exists.
FILES="docker-compose.yml deployment" [ -f .env ] && FILES="$FILES .env" tar czf "$BACKUP/datamind-installer-files-$DATE.tgz" $FILES
Check it worked.
tar tzf "$BACKUP/datamind-installer-files-$DATE.tgz" | grep -E '^(docker-compose.yml|\.env|deployment/\.env)$'
The output shows docker-compose., deployment/, and . when you have one.
The file records the exact DataMind Installer build. Give it to DataMind if a restore fails.
docker image inspect -f '{{.RepoTags}} {{index .RepoDigests 0}}' \
"$(docker inspect -f '{{.Image}}' delamain-backend)" \
> "$BACKUP/datamind-installer-version-$DATE.txt"Check it worked. ls / lists the ., the two . files and the .
file with today's date.
Copy these four files to storage outside this host. deployment/ holds every DataMind OS
password, and the secrets archive holds encr..
Use this procedure on the same host or on a new one. The database dump loads into an empty database volume. You never copy the old volume.
Restore the secrets volume before any docker compose up. A first start with an empty secrets
volume generates new keys, and the encrypted settings in the dump can no longer be read.
Run every step as root, in the same shell. Step 1 is for a new host only. Step 2 is for the same host only.
Copy the backup to / on this host, then set the backup location:
BACKUP=/root/datamind-backup DATE=2026-10-01 # replace with the date in your backup file names
Install Docker, as in Install on a fresh host. Then create the
shared network and sign in to the registry with the client_id from DataMind. Enter the
client_secret when prompted for the password.
docker network inspect unistream >/dev/null 2>&1 || docker network create unistream docker login -u <client_id> unistream.azurecr.io
Check it worked. docker login prints Login Succeeded.
The command first checks that both archives can be read. Then it stops the DataMind Installer and removes both volumes: a dump loaded into a database that already has tables fails part-way.
tar tzf "$BACKUP/datamind-installer-secrets-$DATE.tgz" >/dev/null \ && tar tzf "$BACKUP/datamind-installer-files-$DATE.tgz" >/dev/null \ && cd /opt/delamain \ && docker compose down \ && docker volume rm delamain_pgdata delamain_delamain_secrets
Check it worked. This lists no volumes:
docker volume ls --filter name=delamain_
mkdir -p /opt/delamain && cd /opt/delamain tar xzf "$BACKUP/datamind-installer-files-$DATE.tgz"
Check it worked. ls -a in the install directory shows docker-compose. and deployment.
docker volume create delamain_delamain_secrets docker run --rm \ -v delamain_delamain_secrets:/data \ -v "$BACKUP":/backup:ro \ unistream.azurecr.io/docker-hub/library/postgres:16 \ tar xzf "/backup/datamind-installer-secrets-$DATE.tgz" -C /data
Check it worked.
docker run --rm -v delamain_delamain_secrets:/data:ro \ unistream.azurecr.io/docker-hub/library/postgres:16 ls /data
curato.token encr.key jwt.secret pg.pass
Compose later warns that this volume was not created by Docker Compose. The warning is harmless, here and for every volume you create by hand.
Start only PostgreSQL. It creates an empty database volume and the database named by
POSTGRES_DATABASE_NAME in the Installer's ., or the default.
cd /opt/delamain docker compose pull docker compose up -d postgres
Wait until docker compose ps postgres shows (healthy). Then load the dump. ON_ERROR_STOP=1
stops at the first error. Do not run CREATE DATABASE: the container already created it.
docker exec -i delamain-postgres sh -c \ 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \ < "$BACKUP/datamind-installer-db-$DATE.sql" echo "exit code: $?"
Check it worked. The last line reads exit code: 0, and no line starts with ERROR:. On the same
host, an error means the volume was not empty: repeat step 2. On a new host, send the first ERROR:
line to DataMind.
docker compose up -d
The one-shot migration container brings the restored schema up to the running build. Then the backend container starts.
Check it worked.
docker compose ps curl -sS http://localhost:8000/api/health
The backend container shows (healthy), and the health check answers:
{"status":"ok","db":"up"}Sign in with an account you had before the backup. Then open Status and check for updates, as in Updating.
On the same host, to return the DataMind Installer to the dump you took before an update, without touching the files or the secrets:
cd /opt/delamain docker compose down docker volume rm delamain_pgdata
Then run steps 5 and 6.
Your lake, warehouse and dashboards live in the Docker volumes of the DataMind OS deployment. This procedure archives every one of them. Run it as root, in the same shell, in a maintenance window: the deployment is stopped while the archives are written.
Read the Compose project name, then list every named volume its containers mount. Filtering by the project label includes external volumes too.
cd /opt/delamain/deployment
PROJECT=$(docker compose config | awk '/^name:/{print $2; exit}')
echo "project: $PROJECT"
VOLUMES=$(docker ps -aq --filter "label=com.docker.compose.project=$PROJECT" \
| xargs docker inspect -f '{{range .Mounts}}{{if eq .Type "volume"}}{{println .Name}}{{end}}{{end}}' \
| grep -v '^$' | sort -u)
printf '%s\n' $VOLUMESCheck it worked. The first line names the project, and one volume name follows per line.
Check the disk space. The free space on / must exceed the total of the volume sizes listed under
Local Volumes space usage:
docker system df -v df -h /root
Some services mount host directories instead of volumes. List them:
docker ps -aq --filter "label=com.docker.compose.project=$PROJECT" \
| xargs docker inspect -f '{{range .Mounts}}{{if eq .Type "bind"}}{{println .Source}}{{end}}{{end}}' \
| grep -v '^$' | sort -uPut the data directories from that list in HOSTDIRS. Leave out / and /: they
are host infrastructure, not data.
HOSTDIRS="/srv/example-data" # replace with your data directories, separated by spaces
A file-level copy of a database is consistent only while the database is stopped. Click Stop on Status, or run on the host:
cd /opt/delamain/deployment docker compose stop
Check it worked. docker compose ps lists no running services.
DATE=$(date +%F)
OSBACKUP=/root/datamind-backup/os-$DATE
umask 077
mkdir -p "$OSBACKUP"
printf '%s\n' $VOLUMES > "$OSBACKUP/volumes.txt"
for v in $VOLUMES; do
docker run --rm \
-v "$v":/data:ro \
-v "$OSBACKUP":/backup \
unistream.azurecr.io/docker-hub/library/postgres:16 \
tar czf "/backup/$v.tgz" -C /data .
done
printf '%s\n' $HOSTDIRS > "$OSBACKUP/hostdirs.txt"
for d in $HOSTDIRS; do
tar czf "$OSBACKUP/host$(echo "$d" | tr / _).tgz" -C / "${d#/}"
doneCheck it worked. One archive exists per volume, and both counts match:
ls -lh "$OSBACKUP" wc -l < "$OSBACKUP/volumes.txt" ls "$OSBACKUP"/*.tgz | grep -v '/host_' | wc -l
Click Start on Status, or run on the host:
cd /opt/delamain/deployment docker compose start
Copy $OSBACKUP off the host with the DataMind Installer backup.
On a new host, restore the volumes before the deployment starts for the first time. A service that starts on an empty volume fills it with new files, and the archive would then be unpacked on top of them.
Run every step as root, in the same shell. Step 2 is for the same host only.
OSBACKUP=/root/datamind-backup/os-2026-10-01 # replace with your backup directory
Remove the containers and each volume you restore. docker compose down keeps the volumes. The loop
removes a volume only after tar tzf has read its archive.
cd /opt/delamain/deployment docker compose down for v in $(cat "$OSBACKUP/volumes.txt"); do tar tzf "$OSBACKUP/$v.tgz" > /dev/null && docker volume rm "$v" done
for v in $(cat "$OSBACKUP/volumes.txt"); do
docker volume create "$v"
docker run --rm \
-v "$v":/data \
-v "$OSBACKUP":/backup:ro \
unistream.azurecr.io/docker-hub/library/postgres:16 \
tar xzf "/backup/$v.tgz" -C /data
doneCheck it worked. docker volume ls lists every name in volumes..
Each directory returns to its original path.
for d in $(cat "$OSBACKUP/hostdirs.txt"); do tar xzf "$OSBACKUP/host$(echo "$d" | tr / _).tgz" -C / done
Check it worked. docker compose ps in deployment shows every service Up, and your users see
their data.
No. Back it up yourself with Back up DataMind OS data. It archives every volume of the deployment in one loop.
The database dump and the secrets archive. See the encr. warning.
Not for the DataMind Installer backup: wait for any running job to finish, then run it. Stop the DataMind OS deployment while its volumes are archived, then start it again.
After a restore, the DataMind Installer starts with new keys and everyone is signed out. Re-enter the
values as the encr. warning describes, before any Start, Apply
changes or Update.