Everything the setup wizard asks can come from a YAML file instead, so a new installation can be set up by a script, a container entrypoint, Ansible, Terraform or a GitOps pipeline with nobody at a browser. This is passive setup. The Helm chart uses it too (setup.mode: passive or auto; see Install with Helm).
1. How it works
- Get the installation ID — ./server --machine-id prints it and exits. In a container, run the image with --machine-id on the host (or pod) that will run Depotex: the ID belongs to that machine.
- Get the license — Request it for that ID (see Get a license).
- Write the setup file — The same sections as the wizard; secrets as ${VARIABLE} references (below).
- Start the server once with it — --setup-config setup.yaml, or SETUP_CONFIG_FILE=setup.yaml. The server checks the file, sets up, and continues in normal mode.
- Remove the file — It's read only on a fresh installation and ignored afterwards; don't leave secrets lying around.
chmod 600 setup.yaml
export CONFIG_ENCRYPTION_KEY="$(cat /run/secrets/depotex-encryption-key)"
export DATABASE_URL='postgres://depotex:…@db.internal:5432/depotex?sslmode=verify-full&pool_max_conns=25'
export ADMIN_PASSWORD="$(cat /run/secrets/depotex-admin-password)"
./server --setup-config setup.yaml
2. The setup file
An annotated example ships as setup.example.yaml. Every key:
database:
url: ${DATABASE_URL} # may be left out when DATABASE_URL is set
cache:
redis_url: "" # empty: single server without Redis; required for a cluster
storage:
backend: s3 # local | s3 | minio | azure
local:
path: /var/lib/depotex/artifacts
s3:
bucket: my-depotex-artifacts
region: eu-west-1
access_key: ${S3_ACCESS_KEY} # leave both keys out to use the server's IAM role
secret_key: ${S3_SECRET_KEY}
role_arn: "" # optional: a role to assume
external_id: "" # optional: its external ID
minio:
endpoint: minio.internal:9000
bucket: depotex
use_ssl: true
access_key: ${MINIO_ACCESS_KEY}
secret_key: ${MINIO_SECRET_KEY}
azure:
container: depotex
account: ${AZURE_STORAGE_ACCOUNT}
key: ${AZURE_STORAGE_KEY}
license:
file: license.json # relative to this file; or data: | with the JSON inline
admin:
email: admin@example.com
name: Administrator
password: ${ADMIN_PASSWORD} # at least 8 characters
| Key | Required | Meaning |
|---|---|---|
| database.url | When DATABASE_URL isn't set | PostgreSQL URL. Query options such as sslmode and pool_max_conns are kept. |
| cache.redis_url | For a cluster | Redis URL: one server, Sentinel (redis+sentinel://) or Redis Cluster (redis+cluster://); rediss variants for TLS |
| storage.backend | Yes | local, s3, minio or azure; only that backend's section is read |
| storage.local.path | With local | Directory for artifacts (a shared volume in a cluster) |
| storage.s3.* | With s3 | bucket, region; access_key and secret_key (or neither, for the IAM role); optional role_arn, external_id |
| storage.minio.* | With minio | endpoint, bucket, use_ssl, access_key, secret_key |
| storage.azure.* | With azure | container, account, key |
| license.file or license.data | Yes, one of them | The license JSON: a file (relative to the setup file) or inline |
| admin.email, admin.name, admin.password | Yes | The first super admin |
3. Rules the server enforces
- Private file. The file must not be readable by group or others (chmod 600); otherwise the server stops and prints the chmod to run.
- No unknown keys. A misspelled key is an error, so a typo can't silently drop a setting.
- Secrets from the environment. Any value may use ${NAME}; only that form is expanded, so a literal $ stays as it is. A referenced variable that isn't set stops the server with its name.
- One license source. license.file or license.data, not both.
- Fresh installations only. On an installed server the file is ignored.
- Fails loudly. If a value doesn't work (database, storage, Redis, license), the server stops with an error that says what to fix; nothing is half set up.
4. In containers
docker run --rm \
-e CONFIG_ENCRYPTION_KEY -e DATABASE_URL -e ADMIN_PASSWORD -e S3_ACCESS_KEY -e S3_SECRET_KEY \
-e SETUP_CONFIG_FILE=/config/setup.yaml \
-v "$PWD/setup:/config:ro" \
-v depotex-data:/app/data \
-p 8080:8080 \
<your-registry>/depotex-backend:<version>
Mount the setup file read-only with mode 0600, owned by the server's user (UID 65532 in the image). The /app/data volume keeps the server's bootstrap file, search index and logs: keep it across restarts. With the Helm chart you don't write the file yourself: the chart builds it from its values and the secrets (see Install with Helm).
5. After setup, without the UI
- Everything in the web app is also an API: GET /api/openapi.json describes every route, its permission and its license tier (see API Tokens → API description). Sign in once as the administrator to create an API token for automation, or register an OAuth application with a service account for machine access.
- Write the configuration you script (spaces, repositories, upstreams, directories, policies) against that API, so a new installation can be rebuilt the same way.
- Disaster recovery also works without the UI: ./server --restore-file backup.dxbak checks a backup with no server running, and --yes restores it. See Backups → Disaster recovery.
6. Command-line flags
| Flag | What it does |
|---|---|
| --setup-config <file> | Set up a fresh installation from this file (overrides SETUP_CONFIG_FILE) |
| --machine-id | Print this machine's installation ID, for a license request, and exit |
| --restore-file <file> | Check a backup archive (.dxbak) with no server running; with --yes, restore it, then exit |
| --restore-import <name> | The same for a file in the backup storage's import folder (backups/import/) |
| --restore-key-file <file> | The key of an encrypted backup: an age identity file, or the passphrase |
| --restore-sha256 <hex> | The SHA-256 the backup must have (from its .sha256 file) |
| --restore-accept-foreign | Restore a backup signed by another installation |
| --restore-force | Restore although other servers of this installation are running |
| --yes | With a restore flag: restore (without it only the checks run) |