Skip to main content
That script copies .env.example to .env, generates the passwords and the JWT secret that are still template values, and brings the stack up. It is idempotent — run it again after editing .env.

What comes up

Postgres must start with shared_preload_libraries=timescaledb, which the compose file sets. A data volume first initialised by plain postgres will not have it in postgresql.conf, and the hypertable migrations then fail. Start from a fresh volume, or add the setting by hand.

Before you start

1

Configure shared storage

Set TUNEPLANE_STORAGE_ROOT=/srv/tuneplane. Mount the shared working storage at the same path on the control plane and every Node. Jobs receive scoped mounts rather than the entire storage root.
2

Start the control plane

The API and workers need no GPU, NVIDIA runtime, Docker CLI, or Docker socket. They schedule work onto registered Fleets.
3

Connect execution capacity

Register a Node Fleet and enroll each compute machine using the Node installation guide. A single compute machine uses the same flow. Kubernetes and Slurm clusters each register as their own Fleet.
Everything else has a working default. These do not.
.env
Use a control-plane address reachable from compute nodes for TUNEPLANE_INGEST_URL. Loopback points to the compute machine itself.
The database and Redis hostnames are compose service names, resolvable only inside the tuneplane-internal network. Bring the whole stack up with compose; a bare docker run of the app image fails with failed to resolve host 'postgres'.

Check it worked

Then open the console. The first visit runs the onboarding that creates the first administrator.

Moving off one machine

The control plane can stay on this CPU-only machine as capacity grows. Register more Nodes, a Kubernetes cluster, or a Slurm cluster as Fleets. Moving the control plane into Kubernetes is a separate deployment choice.

Native training telemetry

The gateway Compose profile starts the gateway: one process that admits collector telemetry on port 8444 and serves Node control on port 8443, each with its own limits. Training jobs use the pinned Rust collector; no client certificate issuer is required. Startup automatically persists the gateway TLS identity and supplies its public CA. Set TUNEPLANE_NODE_GATEWAY_TARGET to the public host:8443 for automatic Node enrollment. The private key stays on the control plane. All replicas must share PostgreSQL, TUNEPLANE_SECRET_ENC_KEY and TUNEPLANE_WEB_JWT_SECRET. Build a collector for the execution hosts with scripts/build-rust-collector.py and place its complete output directory on platform-managed working storage, outside job packages and run outputs. Set these values in .env:
With automatic gateway TLS enabled and neither gateway trust-bundle override set, startup supplies the public CA and each launch distributes it to the collector. Set TUNEPLANE_TELEMETRY_TRUST_BUNDLE only for an externally managed certificate. TUNEPLANE_TELEMETRY_SPOOL_BYTES defaults to 256 MiB and TUNEPLANE_TELEMETRY_DRAIN_SECONDS to 30 seconds; set them only to override these limits. Node enrollment configures the Node control connection; it does not replace the three telemetry settings above. The target is host:port, without a URL scheme. Its hostname must match the server certificate and be reachable from execution hosts. The trust bundle is the public issuing CA chain. All gateway replicas use the same database and fixed TUNEPLANE_WEB_JWT_SECRET. Leave the target unset only when this deployment does not enable native collection; required sources then refuse to start. Provision the spool root on persistent local disk on each execution host, mode 0700, owned by the execution service account. Do not use NFS, a container writable layer, or temporary storage. The platform creates isolated launch and member subdirectories, issues scoped renewal credentials, and mounts private grant files read-only. Shared-storage file ownership must let the workload read its 0600 grant files; align service UIDs instead of making credentials public. For a Node or Slurm Fleet:
The spool survives process/container restart on its original host. Permanent host-disk loss is outside that guarantee. Shutdown drains for the configured budget; incomplete delivery retains the spool and fails required collection. Do not remove retained directories with pending records. Grant files and local spools are not part of training artifacts. Renewals expire with their execution lease, superseded attempt, revocation, or the five-minute terminal drain window. Use a collector artifact matching the execution host architecture. A mixed architecture installation needs separate compatible deployment profiles; an incorrect binary is rejected rather than installed into the training environment.

Separate storage disks

Use the optional overlay for separate physical locations. All sources and targets are identical absolute paths. The gateway mounts them read-only; training jobs receive only the slices granted by the executor. Add these values to .env. API, workers and Nodes must use the same values.
Provision actual disks/NAS mounts first, then create their subdirectories. For a new installation using the image’s UID 10001:
Keep both -f options on subsequent Compose commands. The overlay refuses to create absent bind sources. It also supports finer cache and state overrides listed in storage layout; provision each explicitly configured source before startup. Existing external mounts can keep their own paths and ownership: register them through Storage Connections and mount them read-only into API/worker at the same host paths. Do not change ownership recursively on an existing external dataset or model library. This is a new-install example. Do not switch an existing deployment’s roots until its files and stored references have been migrated. A directory existing on the host does not prove the NAS mount succeeded; use host mount dependencies and preflight checks. The overlay does not deploy S3 storage or migrate database data. Code sandboxes are optional. The default Compose deployment has no host container runtime; configure an external TUNEPLANE_SANDBOX_ENDPOINT if training needs a code execution service. An explicitly enabled first-party sandbox requires its own Docker or Podman access.