Operations
A repeatable, single-hostname install of the whole suite. You supply one hostname; Core and all five tools are served behind it, every hop over TLS, with secrets and certificates generated for you. The only thing the host needs is Docker with the Compose plugin. This page covers installing, configuring, upgrading, and scaling the stack.
Everything is served from one hostname. Each tool lives under its own URL prefix and Core owns the root:
| URL | Serves |
|---|---|
| https://HOST/ | Core — admin UI, login, SSO callbacks, /api |
| https://HOST/detanami | Detanami |
| https://HOST/datamosaic | DataMosaic |
| https://HOST/flow | Flow — UI, API, and webhooks at /flow/webhook/* |
| https://HOST/blueprint | Blueprint — the data modeler |
| https://HOST/smith | Smith — UI, API, and previews at /smith/preview/* |
| https://HOST/api-gw/<api> | Data APIs served by Smith API workers (only when enabled) |
How the pieces fit together:
The stack runs seven images: the six DBMojo ones (core, detanami, datamosaic, flow, smith, blueprint) and stock nginx:1.27-alpine. Enabling Flow workers adds redis:7-alpine.
From inside the dbmojo-deploy directory:
./install.sh --host dbmojo.example.com # Linux / macOS / WSL / Git Bash .\install.ps1 -HostName dbmojo.example.com # Windows PowerShell with Docker Desktop
The installer does the following, in order:
When it finishes, open https://dbmojo.example.com/ and sign in as described below. Because a fresh install uses a certificate from the internal CA, browsers warn until you trust certs/ca.crt or supply your own certificate — see Certificates & TLS.
The first admin account is named admin. On a first install the installer generates its password and writes it to .env, so it is always recoverable:
grep ADMIN_PASSWORD .env # Linux / macOS / WSL / Git Bash Select-String -Path .env -Pattern ADMIN_PASSWORD # PowerShell
Change the password after logging in. What the .env value does and doesn't mean:
New users, groups, and per-tool roles are then managed in Core's User Management, exactly as in a non-container install. See the Core reference.
Deployment-level settings live in one file, dbmojo-deploy/.env, created by the installer. Edit it, never commit it. Most values are generated for you; the ones you are expected to set are the hostname and, for a real deployment, the image source and any license values.
| Variable | Default | What it does |
|---|---|---|
| DBMOJO_HOST | — | The hostname users type, with no scheme or path. Set by --host. Also the session-cookie domain. |
| DBMOJO_PUBLIC_ORIGIN | derived | The full external origin (https://HOST, plus :PORT when not 443). Rewritten by the installer on every run from the host and HTTPS port — don't edit it by hand. |
| HTTPS_PORT | 443 | The published HTTPS port. |
| HTTP_PORT | 80 | The published HTTP port, which only redirects to the HTTPS origin. |
| HSTS_MAX_AGE | 31536000 | HSTS lifetime in seconds. Set 0 on a throwaway hostname so browsers don't pin it. |
| SESSION_SECRET | generated | Shared by every app; this is what makes single sign-on work. |
| INTERNAL_SECRET | generated | Shared by every app; authenticates server-to-server calls. If you set your own, Core refuses to start unless it is at least 32 characters. |
| ENCRYPTION_KEY | generated | Protects Core's connection vault. Back it up — losing it makes stored connection credentials unreadable. |
| ADMIN_PASSWORD | generated on first install | The first admin's initial password. See First login. |
| JOIN_TOKEN_DETANAMI, _DATAMOSAIC, _FLOW, _SMITH, _BLUEPRINT | blank | Per-tool tokens that let a tool register with Core. Written by --register-tools. See Registering tools. |
| FLOW_ROLE | all | all runs flows inside flow-web; web hands them to worker containers. Set by --workers. |
| FLOW_WORKERS | 2 | Number of Flow worker containers when FLOW_ROLE=web. |
| REDIS_PASSWORD | generated | Password for the Flow workers' TLS-only Redis. |
| API_WORKERS | 0 | Number of Smith API worker containers. Set by --api-workers. |
| API_POOL_SIZE | 4 | Database connections each API worker opens per API. |
| COMPOSE_PROFILES | derived | Which optional services run (workers, apiworkers). Derived by the installer from the two worker settings — don't edit by hand on Linux/macOS. |
| DBMOJO_REGISTRY | blank | Registry prefix to pull prebuilt images from. Blank builds from sibling source checkouts. See Images. |
| DBMOJO_VERSION | latest | Image tag to use. Pin a real version in production. |
| LICENSE_MODE, LICENSE_TOKEN, LICENSE_SERVICE_URL, LICENSE_PUBLIC_KEY | blank | Passed straight through to Core; blank uses Core's defaults. For an offline signed-file license, put license.json in the core-data volume and set LICENSE_PUBLIC_KEY; for a connected license set the mode, token, and service URL. See the Core reference. |
Per-service settings such as ports and base paths are set in docker-compose.yml and normally don't need to change.
| install.sh | install.ps1 | Effect |
|---|---|---|
| --host NAME | -HostName NAME | Set the hostname. Required on the first run. |
| --https-port N | -HttpsPort N | Publish HTTPS on a port other than 443; the public origin then includes it. |
| --renew-certs | -RenewCerts | Reissue the internal service certificates. See Certificates. |
| --no-start | -NoStart | Prepare .env, images, and certificates but don't start the stack. |
| --rebuild | -Rebuild | Force building images from source, e.g. after updating the code. |
| --pull | -Pull | Force pulling images; needs DBMOJO_REGISTRY set. |
| --workers N | not available | Run Flow workers. 0 turns them off. See Flow workers. |
| --api-workers N | not available | Run Smith API workers. 0 turns them off. See API workers. |
| --register-tools LIST | not available | Install tools in Core and hand each its join token. all or a comma-separated list. See Registering tools. |
The options marked not available are only in the shell installer. On Windows, run install.sh from Git Bash or WSL, or set the equivalent .env values by hand — see Windows notes.
Every hop is encrypted: browser to nginx, and nginx to each service. The installer creates and manages the certificates for the internal hops; you only need to think about the one browsers see.
| What | Where | Notes |
|---|---|---|
| Internal CA | certs/ca.crt, certs/ca.key | Valid ten years. ca.key is never mounted into a container — keep it private. |
| Service certificates | certs/<service>/tls.crt, tls.key | One per service, valid 825 days, with the service name as its SAN. |
| Edge certificate (what browsers see) | certs/edge/tls.crt, tls.key | See below. |
Put your certificate (full chain) and key at certs/edge/tls.crt and certs/edge/tls.key before running the installer. To swap one in later, replace the files and run docker compose restart nginx.
If you don't, the installer issues an edge certificate from the internal CA and browsers warn until they trust certs/ca.crt. Air-gapped installs work the same way: nothing in the install calls out to the internet.
./install.sh --renew-certs
This reissues the internal service certificates (and the edge certificate only if it was issued internally, never one you supplied), then you restart with docker compose restart. The installer warns whenever a certificate is within 30 days of expiry.
A tool is running as soon as the stack is up, but it stays hidden from Core's launcher until an admin installs it in Core (System Management → Tools). Installing issues a join token, shown once. A tool given its token calls Core when it boots, and Core then shows it as joined, when it last booted, and the URL browsers use for it.
To do the whole handoff in one step — install in Core, capture each token, save it to .env, and restart the tool so it joins:
./install.sh --register-tools all # or: --register-tools flow,smith,blueprint scripts/register-tools.sh [--rotate] [tool ...] # any time later
What a tool does when it boots: it joins once per boot (so "last seen" means "last booted", not "up right now"); it reports its public URL, never its container address; a refusal from Core is final (it logs why once and stops asking) while an outage is retried with backoff, so a tool that boots before Core still registers; and a tool with no token simply logs one line and carries on serving. Worker containers never join — only the tools themselves do.
The compose file names the DBMojo images ${DBMOJO_REGISTRY:-dbmojo}/{core,detanami,datamosaic,flow,smith,blueprint}:${DBMOJO_VERSION:-latest}. The Flow image is called flow, while its compose service is flow-web. The installer picks the image source in this order:
--pull and --rebuild force one path. Images contain code only — no secrets, certificates, or hostname — so one image set serves every customer.
On a build machine with all repositories checked out side by side and docker login done:
DBMOJO_REGISTRY=registry.example.com/dbmojo DBMOJO_VERSION=1.4.0 \ ./scripts/publish.sh --also-latest
This builds the five images for linux/amd64 and pushes them tagged with the version. Both variables are required, and latest is refused as a version — pin real versions, and add --also-latest only if you want a moving tag too. Set PLATFORM=linux/arm64 if your servers are ARM (an ARM build machine would otherwise produce images a typical server can't run). --no-push builds and tags without pushing.
On the target host you need only the dbmojo-deploy directory:
cp .env.example .env # set in .env: # DBMOJO_REGISTRY=registry.example.com/dbmojo # DBMOJO_VERSION=1.4.0 docker login registry.example.com # if the registry is private ./install.sh --host dbmojo.example.com # a registry is set, so it pulls
nginx:1.27-alpine comes from Docker Hub unless you mirror it.
On a connected machine that already has the images, build one self-contained tarball:
DBMOJO_VERSION=1.4.0 DBMOJO_REGISTRY=registry.example.com/dbmojo \ ./scripts/bundle.sh dist # -> dist/dbmojo-1.4.0.tar.gz and dist/dbmojo-1.4.0.tar.gz.sha256
It contains all six images (including nginx) plus the compose file, nginx config, scripts, and installers. The bundled .env.example already carries the matching registry and version, so the host can't end up looking for different tags. Carry the tarball and its checksum across, then on the host:
sha256sum -c dbmojo-1.4.0.tar.gz.sha256 tar xzf dbmojo-1.4.0.tar.gz && cd dbmojo-1.4.0 docker load -i images.tar.gz # prints each "Loaded image: ..." ./install.sh --host dbmojo.example.com # all images local: no pull, no build
If you enable Flow workers on an air-gapped host, redis:7-alpine must be loaded too. If you hand-roll docker save instead of using the bundle script, save everything listed by docker compose config --images.
Change DBMOJO_VERSION in .env, then run ./install.sh --pull (or docker compose pull && docker compose up -d). Volumes, secrets, and certificates are untouched. Core and every tool keep state in local SQLite and run as a single instance each, so expect a short outage while containers restart — take a backup first.
By default Flow runs every deployed flow inside the flow-web container. To run flows on separate worker containers with one central schedule:
./install.sh --workers 2 # 2 workers + a TLS-only Redis; flow-web becomes the coordinator ./install.sh --workers 0 # back to running flows inside flow-web docker compose up -d --scale flow-worker=4 # change the worker count later
It is safe on an existing install: deployed flows move to the workers on their own, and --workers 0 brings them back. Flow's users and the Deploy, Stop, and Trigger buttons work as before.
Limits: failover takes about 15–20 seconds after a worker is killed outright (about 4 seconds after a clean stop); workers must share a host because they share two volumes with flow-web; and a TCP In node in listen mode opens a port inside its worker's container that nothing publishes. As an admin, GET /flow/api/cluster shows the live workers, each flow's owner, and the schedule.
Smith can serve the Data APIs it generates from separate worker containers behind the proxy, rather than only offering a local preview or an AWS Lambda deploy:
./install.sh --api-workers 2 # 2 api-worker containers ./install.sh --api-workers 0 # turn them off docker compose up -d --scale api-worker=4 # change the count later
With workers on, Smith's Deploy dialog gains an API workers target, and a deployed API is served at https://HOST/api-gw/<api> (the API's URI slug, or its id). The Flow and API worker settings are independent — turning one on or off never changes the other.
Limits to plan for: each worker opens its own connection pool per API, so total connections to your database are workers × API_POOL_SIZE × APIs — size that against the database's connection limit. There is no rate limiting or per-caller identity yet (one key per API), so put a rate limit in front if an API is exposed beyond trusted callers. SQLite connections can't be served. A licensed Core is required for the shared-connection and approval path; a fresh Core with no license answers it with a 402.
Everyday commands, run from the dbmojo-deploy directory:
docker compose ps # service status and health docker compose logs -f core # follow one service's log docker compose restart nginx # e.g. after replacing the edge certificate docker compose stop # stop the stack, keeping all data docker compose up -d # start it again
The stack stores its state in named Docker volumes:
| Volume | Holds |
|---|---|
| shared | Session store (sessions.db) |
| core-data | Core's database, audit log, and license |
| detanami-data | Detanami's data |
| datamosaic-data, datamosaic-uploads | DataMosaic's data and uploaded files |
| flow-data, flow-files, flow-custom-nodes | Flows and their credential key, File In/Out files, and custom node files |
| smith-data, smith-generated | Smith's notebooks (including its key) and generated API code |
| blueprint-data | Blueprint's models |
Back up all of the volumes, plus .env (it holds ENCRYPTION_KEY and INTERNAL_SECRET) and the certs/ directory. Because each service keeps its state in SQLite, stop the stack (docker compose stop) before copying the volumes so the files are consistent. Volume names are prefixed with the compose project name, dbmojo (for example dbmojo_core-data).
Use a throwaway hostname and port so nothing collides with other dev servers:
./install.sh --host localhost --https-port 8443 # and in .env: HTTP_PORT=8080 HSTS_MAX_AGE=0
Setting HSTS_MAX_AGE=0 matters: browsers pin HSTS per hostname regardless of port, so a long lifetime on localhost would force HTTPS onto every other http://localhost:... server you run.
| Symptom | Cause and fix |
|---|---|
| The browser warns about the certificate | The edge certificate came from the internal CA. Trust certs/ca.crt, or supply your own at certs/edge/ — see Certificates. |
| A tool is running but missing from Core's launcher | It hasn't been installed in Core. Run scripts/register-tools.sh or install it under System Management → Tools. See Registering tools. |
| Core won't start and mentions the admin password | ADMIN_PASSWORD is set but shorter than 8 characters. Lengthen it, or blank it to have Core generate one. Check docker compose logs core. |
| The installer tries to pull or build when you expected it not to | DBMOJO_REGISTRY and DBMOJO_VERSION in .env must match the local image tags exactly. Compare docker compose config --images against docker images. |
| Images won't start on the server | Images are built for linux/amd64 by default. For an ARM server publish with PLATFORM=linux/arm64. |
| Typing http://host:8443 gives an error | Plain HTTP on the TLS port is redirected to the configured HTTPS origin; if you still see an error, check DBMOJO_PUBLIC_ORIGIN and re-run the installer. |
| /api-gw/... returns 502 or 503 | No API workers are running. Enable them with --api-workers N. |
| Every tool stops working at once | The Core license has expired — every tool depends on it. See the license values in Configuration and the Core reference. |
| Flow or Detanami crash-loop after a custom image build | Native modules (better-sqlite3, sqlite3) must compile inside the image. The Dockerfile sets npm_config_build_from_source for this; don't remove it. |