Prepare the host

Complete every step on the server that will run the deployment, as a user who can run docker and sudo. Each step ends with a verification you can run before moving on.

Create the shared unistream Docker network

The Installer and the DataMind OS stack it deploys both join the same Docker network, and both compose projects declare it external — so neither of them creates it. Create it once, by hand:

bash
docker network create unistream

This is not optional. Without it the first docker compose up fails immediately with:

text
network unistream declared as external, but could not be found

The reason it is created out of band rather than owned by one project is recorded in the compose file:

The Installer's backend also pins a network alias, delamain-backend, so that the stack can reach it by a stable name without depending on the container name (which carries a -dev suffix on the development stack).

Verify:

bash
docker network inspect unistream --format '{{.Name}} driver={{.Driver}}'

Expected output starts with unistream driver=bridge. If the network already exists, the create command reports an error and changes nothing — that is safe to ignore.

Create the install directory on the host

Choose the directory that will hold the Installer's compose file and its .env. Its name becomes the compose project name, because the production compose file sets no name: key. Pick a stable, obvious name such as /opt/delamain and do not rename it later.

bash
sudo mkdir -p /opt/delamain
cd /opt/delamain

Place the Installer's docker-compose.yml and .env here (see Install).

Verify:

bash
ls -l /opt/delamain
docker compose version

docker compose version must print a v2 version, 2.19.1 or newer. This is the plugin invocation (docker compose), not the legacy standalone docker-compose binary.

Check that the Installer's port is free

The Installer publishes one host port: NEST_PORT, 8000 by default. Nothing else may be listening on it.

Verify:

bash
ss -ltnp | grep ':8000' || echo 'port 8000 is free'

If something is already there, either stop it or set NEST_PORT to another free port in .env before the first start.

Note

The Installer's PostgreSQL is not published in the production compose file — it is only reachable on the compose network. If you need a published database port, use the development compose file, which is not intended for a customer host.

Confirm the Docker socket path

The backend container mounts the host Docker socket, and every deployment action runs through it.

Verify on a standard rootful host:

bash
ls -l /var/run/docker.sock

If the socket is not there, or Docker runs rootless, find the correct path and set DOCKER_SOCK in .env:

bash
id -u                      # gives the UID for the rootless socket path
ls -l /run/user/$(id -u)/docker.sock

Then keep the default:

dotenv
DOCKER_SOCK=/var/run/docker.sock

or point it at the rootless socket, for example:

dotenv
DOCKER_SOCK=/run/user/1000/docker.sock
Important

The Installer must be started by docker compose. Its self-update reads the compose project name, working directory and compose-file path from the running container's labels, and refuses to run when they are absent.

Confirm outbound network access

Check the two destinations the Installer contacts before any deployment.

Verify Azure Container Registry reachability:

bash
curl -sS -o /dev/null -w '%{http_code}\n' https://unistream.azurecr.io/v2/

A 401 is the expected answer: the registry is reachable and is asking for credentials.

Verify Blob Storage reachability (an HTTP status other than a DNS or TLS failure is enough — the container is private):

bash
curl -sS -o /dev/null -w '%{http_code}\n' https://unistream.blob.core.windows.net/docker/env.schema.json

Also confirm the host's Docker daemon can reach Docker Hub, directly or through your proxy, since some stack images resolve there. The full destination list is on Requirements.

Confirm the directories the Installer binds

These paths are mounted from the host by the Installer's own compose file. Docker creates a missing bind-mount source as root, but creating them explicitly lets you set ownership and permissions first.

Host pathPurposeMode
./deployment (next to the compose file)Where the DataMind OS stack's docker-compose.yml, .env.unified.template and .env are writtenread-write
/var/tmp/delamainThe compose engine's TMPDIR. Must be the same path on host and in the container — the self-updater receives a staged compose file through itread-write
/opt/delamain (overridable with MIGRATE_HOST_DIR)Where the host-migration script is placed on the hostread-write
/var/lib/jenkins (overridable with JENKINS_HOME)Read-only mount, so the Installer can import configuration from a legacy Jenkins config.xmlread-only

Verify:

bash
sudo mkdir -p /var/tmp/delamain /opt/delamain
ls -ld /var/tmp/delamain /opt/delamain
ls -ld /var/lib/jenkins 2>/dev/null || echo 'no Jenkins on this host - expected for a greenfield install'

/var/tmp/delamain is the one mount whose path must not be changed to something else: it is documented in the code as working "only because docker-compose.yml bind-mounts TMPDIR at the SAME path on host and container".

Understand the volumes the Installer creates

The first docker compose up creates two named volumes. You do not create them by hand, but you must keep them together — they hold the state that cannot be regenerated.

VolumeMounted atHolds
pgdata/var/lib/postgresql/dataThe Installer's database: users, configuration, jobs, audit trail
delamain_secrets/run/secrets in PostgreSQL, /usr/src/app/secrets in the backendpg.pass, jwt.secret, encr.key, curato.token
Warning

The PostgreSQL password is generated once, into delamain_secrets, and the cluster is initialised with it. The JWT signing secret and the encryption key are generated once, into the same volume, and the encryption key is what makes the stored Azure client secret readable. Removing delamain_secrets while the database survives leaves the Installer unable to connect to its own database, unable to validate existing sessions, and unable to decrypt the stored client secret.

Verify after the first start (see Install):

bash
docker volume ls | grep -E 'delamain_(secrets)|pgdata'
docker compose -f /opt/delamain/docker-compose.yml ps

Next: Install.