SetupProvisioning

Provisioning

What the engine installs in the Infrastructure step.

The Infrastructure step is where everything is assembled on the host. The engine installs, in sequence, with progress streamed live:

  • k3s — lightweight, production-grade Kubernetes (scales to multi-node later).
  • Ingress (Traefik) + cert-manager — routing and automatic TLS via Let's Encrypt.
  • PostgreSQL and Redis in-cluster — the definitive database and the queue/pub-sub for logs.
  • The artifact Registry and the CI Runners.
  • The Deploy serverless runtime.
  • The definitive api and web images (deploy + migrations + super admin seed as in-cluster Jobs).
The Kubernetes objects carry the equantic-space-* prefix (namespace equantic-space, Deployments equantic-space-api / equantic-space-platform, database equantic-space-db).

CI runners

Pipeline jobs run as isolated pods on the cluster — one pod per job, with its own CPU/memory limits, so a heavy job never starves the platform or its neighbours. Capacity is derived from the cluster itself (always reserving headroom for the control plane): jobs beyond the ceiling wait in the queue, and adding a node to the fleet grows CI capacity automatically.

Every container in a job's pod is bounded — the job itself, its services: sidecars, and the per-job Docker daemon all carry explicit ceilings, and each scratch volume has a size cap, so a runaway job evicts itself instead of pressuring the node. The job's timeout-minutes is also stamped onto the pod: even if the platform process dies mid-job, the cluster retires the pod on its own.

Need docker build inside jobs? Set EQS_RUNNER_DIND=true: each job gets its own private Docker daemon, never shared between jobs or workspaces.

Tuning the capacity

The derivation is a good default, not a verdict: Settings → Platform → CI capacity lets you decide how much of the machine the runner may take, with every change previewing what it would buy on this cluster before you save it.

Two knobs matter most. The platform reserve is the slice of each node CI never touches — lowering it gives CI more in total, though more slots then divide the memory further, so each job can end up smaller. And "do not start below" is a floor for admission: a job's ceiling is frozen when its pod is created, so a brief dip in free memory can hand a two-hour build a limit it cannot finish under. Below the floor the node offers no slots and the job waits in the queue — late beats crippled.

The reserve also has an absolute floor — at least 2 cores and 3 GiB stay with the control plane whatever the percentage says (self-reducing on tiny hosts). A dedicated runner node with no platform on it can zero the floor via EQS_RUNNER_MIN_PLATFORM_RESERVE_CPU / EQS_RUNNER_MIN_PLATFORM_RESERVE_MEMORY_GI.

A value saved here outranks the matching EQS_RUNNER_* environment variable, and takes effect within one budget refresh (30 s) — no restart. Clear a field to fall back to the environment or the built-in default.

Logs and retention

A single step may store up to 50,000 log lines (EQS_RUNNER_MAX_LOG_LINES_PER_STEP; 0 removes the cap). Past the cap the step keeps running to its real conclusion — a loud truncation marker is written and further output is dropped, which protects the database and the live stream from a runaway set -x.

Step logs age out after 90 days (EQS_CI_LOG_RETENTION_DAYS; 0 keeps them forever). Runs, jobs, conclusions and annotations are kept — history stays browsable, only the raw transcript expires. The registry's blob garbage collector and retention runner are also on by default (each EQS_REGISTRY_*_ENABLED=false opts out): unreferenced blobs are reclaimed on their own, and a retention policy saved in the Artifacts screen simply runs.

The platform's own share

The platform sizes its own pods from the host — database, api and web declare resource requests derived at install time (and back-filled by updates), so under load the kernel arbitrates in the platform's favour instead of treating a CI job and the database as peers. Overridable per component via EQS_PG_CPU_REQUEST-style variables when you know your box better than the formula does.