Skip to Content
DeployDocker

Docker

Quick start

export GESTALT_ENCRYPTION_KEY="$(openssl rand -hex 32)" docker run --rm \ -p 8080:8080 \ -e GESTALT_ENCRYPTION_KEY="${GESTALT_ENCRYPTION_KEY}" \ -v "$(pwd)/config.yaml:/etc/gestaltd/config.yaml:ro" \ -v gestalt-data:/data \ valontechnologies/gestaltd:latest

Mount a config file at /etc/gestaltd/config.yaml. The image is not zero-config and will fail to start without one. Generate the encryption key once with openssl rand -hex 32 and reuse that value for the deployment.

Docker Compose

services: gestalt: image: valontechnologies/gestaltd:latest ports: - "8080:8080" volumes: - ./config.yaml:/etc/gestaltd/config.yaml:ro - gestalt-data:/data environment: GESTALT_ENCRYPTION_KEY: "${GESTALT_ENCRYPTION_KEY}" volumes: gestalt-data:

Image details

PropertyValue
Imagevalontechnologies/gestaltd:latest
Platformslinux/amd64, linux/arm64, linux/arm/v7
BaseStatic (scratch with CA certificates)
Variants:latest-alpine (Alpine, has shell)
Port8080
Entrypoint/gestaltd
Default commandserve --config /etc/gestaltd/config.yaml --artifacts-dir /data
Config path/etc/gestaltd/config.yaml
Data directory/data

For the CLI client image, see Client Docker Image.

Production image

For deterministic production deployments, prepare lock state before the image is built, then bake the lockfile and prepared artifacts into a derived image:

gestaltd lock --config deploy/config.yaml gestaltd sync --locked --config deploy/config.yaml --artifacts-dir deploy

If the deployment builds local source providers during sync, you can opt into the generic Gestalt build cache during the build stage:

RUN --mount=type=cache,target=/root/.cache/gestalt-build,sharing=locked \ GESTALT_BUILD_CACHE_DIR=/root/.cache/gestalt-build \ gestaltd sync --locked --config deploy/config.yaml --artifacts-dir deploy

The cache stores build accelerators only. It is safe to delete, and unsetting GESTALT_BUILD_CACHE_DIR returns sync to the default clean build behavior.

deploy/ config.yaml gestalt.lock.json .gestaltd/providers/... .gestaltd/ui/...
FROM valontechnologies/gestaltd:latest-alpine COPY deploy/ /app/ CMD ["serve", "--locked", "--config", "/app/config.yaml"]

This produces a self-contained image that starts without fetching, resolving, or writing provider artifacts at runtime.

See Configuration for what gestalt.lock.json records and why serve --locked depends on it.

Environment variables and secrets

Gestalt expands ${VAR} placeholders in the config before YAML decoding. The image also supports the *_FILE convention: if VAR is not set but VAR_FILE is, ${VAR} resolves to the contents of that file. If neither is set, config loading fails. Use ${VAR:-} when an explicit empty default is intentional. This works well with Docker secrets:

docker run --rm \ -p 8080:8080 \ -v "$(pwd)/config.yaml:/etc/gestaltd/config.yaml:ro" \ -v /run/secrets/encryption-key:/run/secrets/encryption-key:ro \ -e GESTALT_ENCRYPTION_KEY_FILE=/run/secrets/encryption-key \ valontechnologies/gestaltd:latest

For production, use a dedicated secret manager with structured secret refs. See Secrets Providers and Configuration.

Health endpoints

GET /health returns 200 when the process is running. GET /ready returns 503 until providers and the datastore are ready, then 200.

SQLite and persistent storage

SQLite works for local development, demos, and single-instance deployments. Store the database on a mounted volume (e.g. /data/gestalt.db) to persist data across container restarts.

For horizontally scaled deployments, use a networked IndexedDB provider such as RelationalDB with Postgres, MySQL, or SQL Server, or the first-party MongoDB or DynamoDB providers. See IndexedDB.