Platform for self-hostable, configurable integrations and tooling
8.0K
gestaltd is a platform for self-hostable, configurable integrations and tooling. You describe your platform in one YAML file, and gestaltd turns that file into a running server with an HTTP API, admin UI, health endpoints, and optional MCP endpoint.
Alpha. Gestalt is under active development. Images are tagged with alpha versions and may introduce breaking changes. See the documentation for the latest guidance.
Image: valontechnologies/gestaltd
Default port: 8080
Default command:
/gestaltd serve --config /etc/gestaltd/config.yaml --artifacts-dir /data
Default config path: /etc/gestaltd/config.yaml
Default writable data and artifacts dir: /data
This image is not zero-config. Mount or bake a config file before starting it.
| Tag | Base | Shell | Size |
|---|---|---|---|
latest, <version> | scratch (static) | No | Smallest |
latest-alpine, <version>-alpine | Alpine 3.23 | Yes | Small |
All tags are published for linux/amd64, linux/arm64, and linux/arm/v7.
The default image is a static build: just the gestaltd binary and CA
certificates on a scratch base. There is no shell, no package manager, and
minimal attack surface.
For debugging or app compatibility, use the -alpine variant. It includes
a shell, ca-certificates, libgcc, libstdc++, and a writable /data
directory owned by nobody.
Mount a config file and writable /data volume before starting the container:
export GESTALT_ENCRYPTION_KEY="$(openssl rand -hex 32)"
docker run --rm \
-p 8080:8080 \
-e GESTALT_ENCRYPTION_KEY="${GESTALT_ENCRYPTION_KEY}" \
-v "$(pwd)/gestalt.yaml:/etc/gestaltd/config.yaml:ro" \
-v gestalt-data:/data \
valontechnologies/gestaltd:latest
Generate the encryption key once with openssl rand -hex 32 and use that value for the deployment.
Example minimal config:
apiVersion: gestaltd.config/v8
server:
public:
port: 8080
encryptionKey: ${GESTALT_ENCRYPTION_KEY}
providers:
indexeddb: main
providers:
indexeddb:
main:
source: https://github.com/valon-technologies/gestalt-providers/releases/download/indexeddb/relationaldb/v0.0.1-alpha.2/provider-release.yaml
config:
dsn: sqlite:///data/gestalt.db
apps: {}
services:
gestaltd:
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:
For deterministic production deployments, run gestaltd lock and
gestaltd sync --locked before runtime, then bake the lockfile and prepared
artifacts into a derived image:
gestaltd sync \
--locked \
--verbose \
--output-format=json \
--config deploy/config.yaml \
--artifacts-dir deploy \
--cache-dir /cache/gestaltd \
> gestaltd-sync.json
--output-format=json writes one compact metrics document to stdout on
successful sync, while source-build output and errors go to stderr. Without
--verbose or --output-format=json, successful gestaltd sync --locked
stays quiet.
--cache-dir enables a content-addressed cache for materialized prepared
artifacts keyed by locked archive identity. Cache hits restore prepared outputs
without re-downloading or reinstalling the package.
Set GESTALTD_SYNC_CACHE_REMOTE=gs://bucket/prefix with --cache-dir to back
that local cache with sparse per-artifact GCS objects. Before materializing a
locked package, gestaltd sync prefetches only that package's requested cache
object into the local cache, then uploads entries materialized during the current
sync.
Use a private cache location because cache writers can influence restored
prepared artifacts.
FROM valontechnologies/gestaltd:latest-alpine
COPY deploy/ /app/
CMD ["serve", "--locked", "--config", "/app/config.yaml"]
See the deployment documentation for the full lock/sync/serve workflow and recommended patterns.
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. This works well with
Docker secrets. See the configuration documentation
for the full config model and structured secret-ref support.
GET /health for livenessGET /ready for readinessThe admin UI is served at /admin. Gestalt uses server.admin.ui when set,
otherwise auto-discovers admin/index.html from the root providers.ui
bundle before falling back to the built-in shell. If you configure
server.management, health and admin endpoints move to the management
listener. If you also set server.admin.authorizationPolicy, Gestalt applies
browser session authentication and role checks to /admin; on split
public/management deployments, set server.management.baseUrl so login can
return the browser to the management listener's /admin route after callback.
Use the same
hostname as server.baseUrl, and keep it on https whenever
server.baseUrl is https, so the session cookie is reusable across both
listeners. See the
deployment documentation for the recommended
split-listener production pattern.
The built-in /admin shell now includes both the Prometheus metrics dashboard
and an app authorization workspace. For any app that already declares
authorizationPolicy, operators can open /admin/?tab=members&plugin=<name>
to inspect merged static/dynamic rows and manage dynamic grants. Static policy
members remain authoritative.
/dataSQLite works for local development, demos, and single-instance deployments.
Store the database on a mounted volume (e.g. /data/gestalt.db). For
horizontally scaled deployments, use Postgres or MySQL.
The default static image does not include a shell. Use the -alpine variant
for interactive debugging:
docker run --rm -it --entrypoint sh valontechnologies/gestaltd:latest-alpine
To check startup behavior:
docker run --rm valontechnologies/gestaltd:latest --help
serve --locked --config ....docker run valontechnologies/gestaltd:latest by itself fails because the
image does not auto-generate config in-container.-alpine for debugging.Content type
Image
Digest
sha256:24ed050d4…
Size
29.8 MB
Last updated
3 months ago
docker pull valontechnologies/gestaltd