Configuration and variables

There are two separate layers of variables, and confusing them is the most common source of avoidable mistakes:

LayerWhere it livesWho edits itWhat it configures
Installer's own environment.env next to the Installer's docker-compose.ymlYou, over SSHThe Installer containers themselves — port, socket, database, session lifetimes
DataMind OS deployment environmentdeployment/.env, rendered by the InstallerThe Installer, from configuration rowsEvery service in the DataMind OS stack

Change the first by editing a file and recreating the Installer. Change the second through the interface or the configuration API — never by hand.

Which values can I rotate after installation?

Stored secrets and operator-supplied values are rotatable; the Installer's own cryptographic material is not. The rotatable set, and how each one is rotated:

ValueRotate withEffect
AZURE_CLIENT_SECRETAdvanced configuration → System secrets, or PUT /api/system-secrets/azureReplaces the encrypted value; validated on demand by a real token request
Any user-source configuration valueAdvanced configuration, or PATCH /api/configs/valuesRewrites deployment/.env and marks affected services stale
VM_USERPOST /api/migrate/vm-userChanges what <USER> expands to in path values
A generated seed whose public key is derived (ed25519-pub:<CODE>)Replace the seed valueThe public key is recomputed automatically on the next sync
JWT_ACCESS_EXPIRY, JWT_REFRESH_EXPIRYInstaller .env, then recreate the backend containerChanges session lifetimes for new tokens
NEST_PORTInstaller .env, then recreate the backend containerMoves the interface and API to another port
NEST_TYPEORM_LOGGING, SWAGGER_ENABLED, TZInstaller .env, then recreate the backend containerVerbosity, API docs, container timezone
Note

Rotating a secret in the deployment layer changes the file, not the running process. A value reaches a container when that container is recreated — that is what Apply changes does.

Which values must never be changed after installation, and why?

The Installer's signing material, its encryption key and its database identity. Changing any of these either locks you out or destroys data:

ValueWhere it livesWhy it must not change
JWT_SECRETdelamain_secrets/jwt.secretEvery issued access and refresh token becomes invalid; all sessions drop
NEST_ENCR_KEYdelamain_secrets/encr.keyEvery stored secret and every generated configuration value becomes undecryptable. The Installer surfaces this as a decryption error on the Azure secret and asks you to re-enter it
pg.passdelamain_secrets/pg.passThe file is the password of the running PostgreSQL role. Editing the file without changing the role password breaks the Installer's database connection
CURATO_SERVICE_TOKENdelamain_secrets/curato.tokenThe Installer owns this value and injects it into the deployment .env on every render. It is deliberately not a configuration row, so no edit can drift it away from the value the deployment compares against
POSTGRES_USER, POSTGRES_DATABASE_NAMEInstaller .envThese identify the existing database. Changing them points the Installer at a different (empty) database
DOCKER_SOCKInstaller .envMust be the host path that actually exists for your Docker installation
Memory rows (*_MEM_LIMIT, *_MEM_RESERVATION, *_INTERNAL_LIMIT_MB, *_ENABLED)Configuration rows, managedThese are recomputed from the host on every generation. Editing one freezes it — see the memory-limits question below
Warning

does not decrypt is what an encryption-key change looks like from the outside. If you ever see that on a stored secret and you did not touch encr.key, treat it as a restored-backup problem, not as a lost key. See Data and backups.

What happens to a running deployment when a configuration value changes?

Nothing immediately — the value is written to deployment/.env, and the affected services are reported as stale until you apply the change. The sequence is:

  1. Saving marks the configuration row userModified and stamps its updated_at.
  2. The Installer rewrites deployment/.env from all configuration rows.
  3. GET /api/deployment/stale-services compares each container's creation time with the updated_at of every configuration row that targets it. A row newer than the container makes that service stale.
  4. The interface shows a "Configuration updated" prompt and an Apply changes button. The job recreates only the stale services, in dependency order, and waits for each tier to become healthy before moving to the next.
  5. Restart does not do this. A restart keeps the old environment; only Apply changes recreates the affected containers.
Important

Some services read the mounted .env at start but keep their baked-in environment, so a plain restart can leave them running a mix of old and new values. The Installer emits an explicit warning when such a service is in the target set — use Apply changes for it.

What is the difference between user, constant, generated and computed values?

They describe where a value comes from, and whether an edit sticks. The interface marks each row with a source icon:

SourceMeaningEditable?Does an edit persist?
constantA fixed value the template declaresYesYes, but there is rarely a reason to
userA value only you can supply — URLs, ports, credentialsYesYes
generatedDrawn once when the template is seeded, then left aloneYesYes — editing sets userModified and freezes it
computedDerived from the host — memory limitsYesYes, and a hand-set value wins over the calculation

generated and secret values are encrypted at rest with the Installer's encryption key. In GET /api/configs they are returned as a fixed mask for anyone who is not an administrator.

Note

A generated value is drawn once and then never redrawn — that is deliberate. Rotating it means replacing it deliberately, not re-seeding. Re-seeding the template does not touch any row you have modified.

How do I rotate the Azure client secret?

Enter the new secret in Advanced configuration, or call the system-secrets endpoint. The value is encrypted before it is stored, and you can validate it immediately.

bash
curl -X PUT http://localhost:8000/api/system-secrets/azure \
  -H "Authorization: Bearer <access-token>" \
  -H "Content-Type: application/json" \
  -d '{"clientSecret":"<new-secret>"}'

Then confirm that it actually works:

bash
curl -s http://localhost:8000/api/system-secrets/azure/validate \
  -H "Authorization: Bearer <access-token>"

Validation attempts a real Azure AD token request. A failure returns a code such as AADSTS…, NETWORK_ERROR, NOT_CONFIGURED or DECRYPT_ERROR, and the interface shows it against the field.

How do I change PLATFORM_URL, and why is it special?

Edit it like any other value — but expect the Installer to rewrite it. PLATFORM_URL is the root of every derived URL in the deployment .env: the CORS origin the edge compares against, the front end's base URLs, the reporting host and the internal platform URL all reference it.

The Installer normalizes it on every write path — the wizard, the Advanced editor, a direct PATCH /api/configs/values, a Jenkins import and a restored configuration set all funnel through the same gate — so a scheme-less host cannot reach the .env:

You typeWhat lands in the file
localhosthttp://localhost
192.168.10.20http://192.168.10.20
portal.example.comhttps://portal.example.com

A non-http/https value is refused with an operator-facing message.

Warning

A scheme-less PLATFORM_URL used to crash-loop one of the deployment's services. The middleware that panicked is gone, so today the same bad value fails silently instead of loudly — which is worse. Keep the scheme in the value you inspect.

What happens if I edit the deployment .env by hand?

Your change is overwritten the next time the Installer generates the file. Every save, every seed and every deployment-file restore rewrites deployment/.env in full from the configuration rows. The generated file wraps its memory-limits block in marker comments saying the block was generated and must not be edited, and it records any hand-edited rows inside that same block.

Edit the value in the interface instead, so it survives and so the Installer knows which services are stale.

Where is the deployment .env on disk?

At deployment/.env, next to the Installer's own compose file on the host. Inside the container the same file is at /usr/src/app/deployment/.env, and that is the path handed to every docker compose invocation as --env-file.

See Installation and setup for the full layout of the deployment directory.

Which memory limits does the Installer compute, and can I override them?

It derives them from the host's total memory, and yes — a hand-set value wins. The Installer reads /proc/meminfo, picks a host class from the schema's buckets, and emits a set of rows per service:

Code patternMeaning
<SERVICE>_MEM_LIMITContainer memory limit
<SERVICE>_MEM_RESERVATIONReserved memory
<SERVICE>_INTERNAL_LIMIT_MBInternal limit for services that manage their own heap, where the schema declares one
<SERVICE>_ENABLEDWhether an optional service is enabled on this host class

If the sum of reservations exceeds the budget, all reservations are scaled down by a single factor and the file records the calculation in a comment. Setting one of these rows by hand sets userModified, and from then on the Installer records it as hand-edited, not recalculated.

Note

On a host that is not the Linux VM these limits describe — for example running the backend outside a container on macOS — no limits are emitted at all, with the reason written into the limits block.

How do I change the VM user used for <USER> substitution?

Set it in the migration step, or call the migration endpoint. Several path values are stored as templates containing <USER> and are expanded with this value when the .env is rendered:

bash
curl -X POST http://localhost:8000/api/migrate/vm-user \
  -H "Authorization: Bearer <access-token>" \
  -H "Content-Type: application/json" \
  -d '{"vmUser":"ubuntu"}'

The value is stored encrypted in the Installer's system secrets. Path values that still contain <USER> are refused as unresolved by the Jenkins import preview.

Is there a supported way to set values the interface does not show?

No — the interface renders every configuration row, including secrets and computed ones. You can filter by key, and the value editor is the same surface the API uses.

What is genuinely not possible is adding a new code. The template publishes the set of valid codes; PATCH /api/configs/values returns a not-found error for a code that does not exist, and re-downloading the template soft-deletes any row the template no longer lists.

What do "Required" and "Required if …" mean on a configuration row?

A required row blocks deployment while it is empty. A row with a requiredIf dependency is required only when the row it names already has a value. Both are enforced before a deploy or an apply, and the failure message lists the codes:

text
Missing required configurations: PLATFORM_URL, CLICKHOUSE_PASSWORD

Can I change the port the Installer itself listens on?

Yes — change NEST_PORT in the Installer's own .env and recreate the container. The port is published and used symmetrically, so the mapping follows the variable:

In the .env next to the Installer's docker-compose.yml:

text
NEST_PORT=9000

Then recreate the Installer's backend service. Note that the container healthcheck, the interface and any link you saved all move with it.

Note

This is the Installer's port, not a DataMind OS service port. DataMind OS ports belong to the service_ports group of the deployment configuration.

How do I change the lifetime of Installer sessions?

Edit JWT_ACCESS_EXPIRY or JWT_REFRESH_EXPIRY in the Installer's own .env and recreate the backend. The values are read when the process starts, and the durations drive both the signed token lifetime and the expiry of the browser cookies.

bash
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=24h

Existing tokens keep their original expiry; new tokens use the new value.

What happens if I change the Installer's encryption key?

Every stored secret and generated value becomes unreadable. The key encrypts secret configuration values, generated values and the system secrets table with AES-256-GCM. After a key change the Installer cannot decrypt them, and validation reports DECRYPT_ERROR with the message that the stored secret could not be decrypted and must be re-entered.

There is no re-key command. The recovery is to restore the original encr.key, or to re-enter the affected values one by one.

How do I change the Installer's database password?

Do not edit pg.pass alone. That file is the password: the PostgreSQL container generates it on first start and the backend reads it at boot. Changing the file without changing the role's password in the database leaves the Installer unable to connect.

If you must rotate it, change the role password inside PostgreSQL first, then write the same value to delamain_secrets/pg.pass and recreate the backend.

How do I import values from an existing Jenkins installation?

Use the Jenkins import — preview first, then import. The Installer reads /var/lib/jenkins/config.xml from the read-only host mount, extracts matching codes, and adds the standard migration path defaults.

bash
curl -s http://localhost:8000/api/configs/import/jenkins/preview \
  -H "Authorization: Bearer <access-token>"

The preview writes nothing. Then apply it:

bash
curl -X POST http://localhost:8000/api/configs/import/jenkins \
  -H "Authorization: Bearer <access-token>"

Import skips rows that are constant-sourced, rows the Installer manages, and rows you have already modified. Every imported value is marked as modified, so it freezes that row. If config.xml is missing, only the path defaults are applied and the response says so.

Why does a configuration value show as "stale"?

Because a container it targets was created before that value was last changed. The Installer compares the row's update time against the container's creation time. The badge tooltip names the services involved.

Fix it with Apply changes, which recreates exactly the stale services and verifies each dependency tier as it goes. A plain restart will not clear the badge.

Can I re-seed the configuration from the template?

Yes, but treat it as a recovery action. Re-downloading the environment template rewrites the schema and re-seeds rows, preserving the values of anything you have modified, and soft-deleting rows the new template no longer declares. The download endpoint is administrator-only:

bash
curl -X POST http://localhost:8000/api/registry/download-env-template \
  -H "Authorization: Bearer <access-token>"
Warning

Re-seeding can retire configuration codes if the published schema has changed. Review the deployment status and the stale-services list afterwards, then apply changes to bring services onto the new set. Take a backup first — see Data and backups.

For the complete variable reference with every group and code, see Environment variables. The rotation procedure in full is in Rotating values, and Secrets covers the encrypted system secrets. For how the service groups and the memory computation are described, see Service catalog and Compose and files.