Skip to main content

Before You Start

Work through Requirements first. The installer validates most of it and refuses to run rather than failing halfway, but a missing certificate or an unopened firewall rule still costs you the slot. Everything below runs from your control machine — your laptop, a jump host, or a CI runner. It is not the target.

The Value Gate

Before Ansible is invoked at all, the installer refuses to run a deploy while any of the following is true:
There is no flag to skip the gate. It exists because each of these produces a failure that is either silent or destroys its own evidence.

Setup

1

Verify the release, before you unpack it

ROOTKey delivers a release as a single archive, and four files reach you by two different routes. The archive comes through the delivery channel; SHA256SUMS, SHA256SUMS.sig and cosign.pub come from Trust and Verification. That separation is the point: a checksum that travelled with the file it checksums proves only that the two arrived together.Put the archive and those three files in one directory, with the verifier, and run it:
It prints the archive’s own SHA-256 for you to compare against the value we sent you separately, checks the signature against cosign.pub, and checks every file from inside the archive without unpacking it. It ends with Verified. Safe to unpack. or it tells you what failed and says not to install.
Run it before tar. Unpacking writes paths chosen by whoever built the archive, and until the signature checks out you do not know that was us.If you run the verifier with the archive alone and none of the three files beside it, it fails — deliberately. It used to report success in exactly the state where it had verified nothing.
Only then unpack:
The bundle carries the Helm charts, the Ansible roles, the installer, and — for an air-gapped install — every container image plus the k3s and Helm binaries.install.sh does not verify anything for you. This is a deliberate step, and nothing later in the install will fail if you skip it.
2

Run the preflight, before touching anything

The preflight is read-only. It changes nothing on the target and is deliberately not blocked by the value gate, so you can run it before anything is filled in.
It reports PASS, WARN or FAIL for CPU, RAM, disk, operating system, and the required ports. Resolve every FAIL before continuing. A host with fewer than eight vCPUs fails this check.
3

Fill in the inventory

Copy the example and edit your copy. The real file is git-ignored, so it never travels back into the repository.
For Scenario C, one host with SSH details. For Scenario B, ansible_connection=local and the kubeconfig passed on the command line.Then set the platform hostname in the same file, under [rootkey:vars]:
rootkey_public_host must be the same hostname you set on the platform Ingress in the values file, and must match the host in PUBLIC_FILE_BASE_URL. The installer compares all three and refuses to deploy if they disagree — because a mismatch produces a working application where every file returns 404, with nothing naming the cause.
4

Fill in the vault

Copy the example vault and fill in every value. See Configuration Reference for what each key is and where it comes from.
Store the vault password in a file the installer can read, and keep that file out of version control:
The gate type-checks only the keys the application parses — control-plane.deployment_id must be a UUID, and every *Url key must be an absolute URL. A CHANGE_ME left on BUCKET_NAME or an HMAC secret passes the gate and fails later, far from its cause. Fill in everything.
5

Create your values file

Copy the example and edit your copy:
Three things must be set:
  1. Every image repository and tag — the registry ROOTKey gave you and the released commit SHA of each service. The gate rejects the shipped placeholders.
  2. The platform hostname, on the platform-frontend Ingress.
  3. PUBLIC_FILE_BASE_URL, under global.envOverrides:
Do not append the bucket name to that URL. The MinIO Ingress prepends it from the vault, so the bucket name lives in exactly one place. Two copies drift, and the failure when they do is a 404 on every file.
6

Load the TLS certificate into the cluster

The installer consumes a Secret; it does not create one. Load your certificate for the platform hostname:
The Secret name must match ingress.tls[].secretName in your values file and rootkey_tls_secret_name in the inventory. rootkey-tls is the default both expect.The ordering, precisely. The namespace is created by the secrets stage (stage 3), and the certificate is not consumed until the Ingress is created in the deploy stage (stage 7). So you have two correct options:
A Secret in the wrong namespace is invisible to the Ingress, and the failure looks like a certificate problem rather than a placement one.
7

Run the installation

For an air-gapped host, add --airgap true to load images from the bundle instead of pulling them.
The --values flag is not optional in practice: without it the installer falls back to the shipped example, which is full of placeholders, and the gate refuses.The run executes twelve stages in order — preflight, Kubernetes runtime, secrets, database, chart staging, in-cluster stateful services, the Data Plane itself, the plan seed, the contract seed, host hardening, the post-install checklist, and the healthcheck that writes the signed install manifest.
For a first install, running it in phases is much easier to diagnose than one long run. Use --tags to stop after each stage: --tags preflight, then --tags k3s, then --tags secrets, and so on. For an upgrade, run the whole thing — two of the stages are idempotent seeds that must not be forgotten.
8

Watch for the lines that matter

Three messages tell you whether the parts that fail silently actually worked.contract-seed — on a fresh install you want contratos semeados. On a re-install you want skip: já há contratos, which means the guard held. If you see skip: o fixture não veio no bundle, the platform will install cleanly and return 500 on the first file upload, with no cause in the gateway log.The bucket job — bucket ready: <name> (anonymous download enabled). If it says anonymous read NOT granted, stored files will return AccessDenied to every browser.The MinIO precondition — it runs before any Helm command in the stateful stage. If it fails, it names which of the three values is missing and nothing has been installed yet.

What Runs, In Order

Stages 11 and 12 are not optional extras, and stage 11 is the one to read. Everything before it reports whether a step succeeded. The checklist asks a different question, and it is the question you care about: every workload can be Running, Helm can report success, and the deployment can still be unusable.A clean run ends with Nothing checked here is missing, followed by a count of items that need confirming by hand — things no check inside the cluster can see for you. Read those.Re-run the checklist on its own at any time with --tags verify. It changes nothing.

The hardening stage, and the net underneath it

Stage 10 reconfigures the firewall of the host you are connected to. That is the one stage that can, in principle, end your own SSH session — and on a remote host with no console, a firewall change that goes wrong is the difference between a failed install and a machine nobody can reach. It is not left to chance. Before the firewall is touched, the installer writes a small script to the target and arms a systemd timer against it. If the hardening pass completes and your connection survives, the timer is disarmed and you never know it was there. If it does not, the timer fires after 15 minutes and puts your access back. What it inserts is one rule, for one address — the address you are connected from, on the port you are connected on:
It re-admits you, not everyone. An earlier version disabled the firewall instead, which would have left the host open to the whole network as its idea of a safety net. If the switch fires it also writes /var/log/rootkey/FIREWALL-DEADMAN-FIRED, so a host that let you back in says so rather than looking like nothing happened.
Two settings are worth knowing before you run it:
If you reach the host through a jump host, a VPN with a changing egress address, or a NAT whose address is not the one the installer sees, put every address that needs SSH into harden_ssh_allow_from before you start. The switch protects the connection the installer is using. It knows nothing about the one you will use tomorrow.

Re-running the Installation

Every stage is idempotent, and an upgrade is the same command as an install.
  • Helm uses upgrade --install. On a release that is already deployed, --atomic is on, so a failure rolls back rather than leaving the platform half-changed. On a first install it is deliberately off: a rollback there would delete the pods, logs and events that explain what went wrong.
  • PostgreSQL is rotation-safe. It reads the passwords from your vault and applies them; it never generates its own, so a re-run cannot orphan your credentials.
  • The seeds check before they write. The contract seed skips with a message when contracts already exist rather than duplicating them.

→ Next: Configuration Reference · Verification