A Linux host running Docker Engine with the Compose v2 plugin, reached over SSH. Everything in
the Installer assumes that shape: the runtime image is Alpine-based and installs the Docker CLI and
Compose plugin, the memory calculation reads /proc/meminfo, the host-retirement script requires
systemd, and the socket defaults to /var/run/docker.sock.
| Requirement | Detail |
|---|---|
| Operating system | Linux with systemd (systemd is needed only for the bare-metal migration) |
| Container runtime | Docker Engine, rootful or rootless |
| Compose | The docker compose subcommand, version 2.19.1 or later |
| Shared network | A Docker network named unistream, created once by you |
| Outbound access | HTTPS to Azure AD, Azure Container Registry, blob storage, and Docker Hub for uncached images |
There is no Windows or macOS target for the deployment host.
You can run it, but expect a degraded evaluation and do not read anything into the results. Two things make a laptop a poor stand-in for a production host:
The Installer itself, the configuration model, the deployment and the update machinery all work the same way. For a lab or a demo, that is enough.
If you are evaluating on Linux, use a virtual machine that matches your production sizing rather than a container-on-a-laptop. The memory limits are derived from the machine's total memory, so a small VM produces small limits.
Modest, and unbounded — because the compose file declares no memory limits for its own containers. The Installer runs one Node.js backend container (Alpine, with the Docker CLI) plus one standard PostgreSQL 16 container.
Two things follow from "unbounded":
If you want a hard bound, set one on the host's Docker daemon or in an override file — the compose file is a published artifact and will be replaced by an update.
Derived from the host's total memory by the deployment's own schema, not guessed. The Installer reads the machine's total memory, picks a host class, and emits a memory limit and reservation per service. The schema controls the arithmetic:
| Setting | Meaning | Default |
|---|---|---|
| Host reserve | Percentage withheld from the host before anything is sized | 0% |
| Reservation budget | Share of the available memory that reservations may total | 75% |
| Default reservation ratio | Reservation as a fraction of a service's limit | 0.75 |
| Budget tolerance | How far over budget is tolerated before scaling kicks in | 2% |
| Minimum scaled reservation | Floor a reservation may be scaled down to | 16 MB |
When the reservations exceed the budget beyond tolerance, every reservation is scaled by a single factor, and the generated file records the calculation so you can see what happened.
A host that is too small does not fail loudly. Services are started with limits that were scaled down to fit, and the only signal is the comment written into the generated environment file. Read it after the first install.
Not out of the box. The Installer requires outbound HTTPS at install time and at every update. Four destinations are involved:
| Destination | Used for |
|---|---|
| Azure AD | Exchanging the client secret for a token |
| Azure Container Registry | Pulling the DataMind OS images and cached Docker Hub images |
| Azure Blob Storage | Downloading the compose file and the environment schema |
| Docker Hub | Pulling images that are not cached in the registry |
The artifact URLs for the compose file and environment schema are compiled into the backend, not configurable. There is no supported way to repoint them at a mirror, so a fully air-gapped install would require a code change. Treat air-gapped deployment as out of scope today.
Blob storage and Azure AD are non-negotiable; registry access is needed for any image change. The Installer restores missing deployment files at boot, which means it reaches blob storage on a normal start. Registry access is needed when you pull, deploy or update — not for a service that is simply running.
So a tightly restricted network will typically allow: Azure AD and blob storage permanently, and registry access during change windows.
No. There is no telemetry, analytics or crash-reporting feature in the code. Its outbound requests are the ones listed above, plus whatever the forwarded documentation proxy and log shipping in the deployment do — which belong to the DataMind OS stack, not to the Installer.
No. The Installer manages exactly one host. It drives the Docker daemon of the machine it runs on, and the deployment it creates is a single-host compose project.
There is no clustering, no remote-host targeting, and no failure-over behaviour. If you need resilience, the answer is host-level: backups, a documented rebuild, and a second deployment elsewhere that you switch to deliberately.
Not in production — the container names collide. The production compose file uses fixed names:
delamain-backend, delamain-postgres and, during an update, delamain-self-updater. Two
production instances on one host would fight over those names.
A development instance can coexist, because the development compose file gives every container a
-dev suffix and declares a different compose project name. That is what it is for.
Even with distinct names, two Installers would drive the same Docker daemon and could each deploy the DataMind OS project. Use separate hosts for separate deployments.
It depends which compose file, and the answer is different for each.
| File | Hand-editing it | What survives |
|---|---|---|
deployment/docker-compose.yml (the DataMind OS stack) | Your edits are overwritten | Nothing — it is re-downloaded on every deploy and restored at boot |
deployment/docker-compose.override.yml | Supported. This is the place for your changes | Everything — the Installer merges it and never writes it |
The Installer's own docker-compose.yml | Overwritten by a self-update | Nothing; the previous version is kept as docker-compose.yml.pre-update |
The override file is merged into every docker compose command the Installer runs, when it exists.
That makes it the correct home for a host-specific volume, an extra bind mount or an added service.
Compose and files describes exactly how the
two files are combined.
Local edits to either of the two managed files are also reported as compose drift by the self-update check. If the check says your compose file differs from the published one, that is your edit being detected — and the next update will replace the file.
Yes, through the override file — with one caveat about the interface. A service you add to
deployment/docker-compose.override.yml is part of the compose project, so full-stack operations
that target every service will start, stop and recreate it along with the rest.
The caveat is grouping. The interface's service list comes from the deployment's environment schema, so a service the schema does not know about shows up as ungrouped rather than inside a named group.
unistream Docker network, and can I rename it?It is the network both the Installer and the DataMind OS stack attach to, and its name is fixed. Create it once, before the first start:
docker network create unistream
Both projects declare it external, so neither creates or owns it. Renaming it would break both
compose projects at once, and the name is a literal identifier — keep the spelling.
Because the network is shared, never let a docker compose down delete it. The external
declaration is what prevents that; do not "fix" it by making one project own the network.
Azure Container Registry, at unistream.azurecr.io by default. The address is overridable with
the ACR_URL environment variable, which the development compose file uses to point at the
non-production registry.
Docker Hub images are fetched through a cache path in the same registry, under the docker-hub/
repository prefix.
BLOB_ARTIFACT_VARIANT, and should I ever set it?It selects which published artifact pair — production or development — the Installer downloads, and
you should not set it on a client host. Empty is the production pair; dev selects the
development schema and compose file. Only those two values are accepted.
It is deliberately independent of the Installer's own environment setting, so raising log verbosity on a client host can never switch it onto development artifacts.
A development schema loaded against a production compose file reports every service as ungrouped and mis-sizes the stack. Leave this variable unset.
Yes, with three things to get right. A proxy in front of the Installer is a normal deployment choice; the interface is served same-origin with the API, so there is only one upstream.
Secure attribute.NEST_ORIGINS if the front end is loaded from a different origin than the API. In the
default same-origin setup this is irrelevant.The front end's API base is read at runtime from config.js, so pointing it at a different origin
does not require a rebuild — but that origin must also be allowed by NEST_ORIGINS.
upgradeInsecureRequests is deliberately disabled in the Installer's content-security policy, so
the interface works over plain HTTP on a management network without the browser rewriting
requests to HTTPS.
No. The Installer is driven through its web interface and its HTTP API. There is no CLI binary and no command to run.
Two shell scripts exist inside the container, but neither is an operator entry point:
| Script | Purpose |
|---|---|
scripts/migrate-host.sh | Runs the bare-metal retirement; normally invoked for you by the migration job |
scripts/self-update.sh | Runs inside the detached helper container during a self-update; never run by hand |
For scripting, use the API with an access token. The audit trail records actions taken through the API exactly as it records actions taken through the interface.
Proprietary. The repository and its packages are marked UNLICENSED, and the product is described
as proprietary software. There is no open-source licence attached to the Installer or to the
container images.
Through DataMind. Prepare the six items listed in
Logs and diagnostics before you open a request —
in particular the running digest from GET /api/version, which identifies the exact build and
cannot be faked by a moved image tag.
Stop its containers, remove its volumes, and remove the shared network only when the DataMind OS stack is gone too.
Run these from the Installer's install directory:
docker compose down docker volume rm delamain_secrets pgdata rm -rf deployment/
This does not uninstall DataMind OS. The deployment is a separate compose project with its own containers and volumes, and its data will keep running until you stop it deliberately. Delete the shared network only after both are gone — see Data and backups for the full picture, including what you lose.
No. The Installer's entire purpose is to drive the host's Docker daemon; without a daemon to talk to there is nothing for it to do, and the deployment it manages is itself a compose project.
For product architecture questions rather than operation, the DataMind OS and Platform sections of this documentation cover the components the Installer deploys.