Installation
This guide walks through a fresh, single-host installation of Manifest Self-Hosted using the manifest-installer CLI. One Linux host runs the entire stack: the k3s control plane, the Manifest applications, and the supporting services. The same host is also where you run manifest-installer itself.
These steps cover an internet-connected install. If your target host cannot reach the internet, follow the Air-Gapped Deployments workflow instead — it uses the same CLI but adds steps to bundle and transfer the install artifacts.
VersionsInstaller releases are published on the manifest-self-hosted Releases page. Replace
<version>in the commands below with the version you've been instructed to install (e.g.2.0.0); the release tag is that version prefixed withv.
Prerequisites
Before proceeding, ensure you have:
- Met all host and network requirements
- Gathered all required credentials
1. Install the CLI
The manifest-installer binary is published to the public manifest-self-hosted Releases and is downloadable anonymously — no GitHub account or token required. Each binary ships with a matching .sha256 checksum file.
Download the binary and its checksum, verify, then place it on your PATH:
base=https://github.com/manifest-cyber/manifest-self-hosted/releases/download/v<version>
curl -fsSL -O "$base/manifest-installer-linux-amd64"
curl -fsSL -O "$base/manifest-installer-linux-amd64.sha256"
sha256sum -c manifest-installer-linux-amd64.sha256 # prints "OK" on a match
chmod +x manifest-installer-linux-amd64
sudo mv manifest-installer-linux-amd64 /usr/local/bin/manifest-installerThe binaries are not code-signed today — verify the checksum to confirm integrity.
Verify the install:
manifest-installer --versionThe CLI follows the XDG Base Directory Specification, splitting its files into three locations by purpose:
| What | Where (default) | Override |
|---|---|---|
Configuration (config.yaml, installer settings) | ~/.config/manifest/ | XDG_CONFIG_HOME |
| Downloaded packages and data | ~/.local/share/manifest/ | XDG_DATA_HOME |
| Logs, lockfile, last-deploy record | ~/.local/state/manifest/ | XDG_STATE_HOME |
Export any of the XDG_*_HOME variables before running the CLI to relocate that directory — for example, to put downloaded packages on a mounted volume, export XDG_DATA_HOME=/mnt/manifest-data.
2. Configure AWS credentials
The installer pulls platform packages from Manifest's private ECR registry (623542229617.dkr.ecr.us-east-1.amazonaws.com) in us-east-1. Configure the IAM credentials Manifest provided during onboarding using either of the following.
Option A — persist them via the installer CLI (recommended for hosts without an AWS CLI or environment configured):
manifest-installer config set aws.accessKeyID <provided-by-manifest>
read -rs SECRET_ACCESS_KEY # paste the secret access key; input is not echoed
printf %s "$SECRET_ACCESS_KEY" | manifest-installer config set aws.secretAccessKeyThe secret is piped on stdin rather than passed as an argument — when the value is omitted, config set reads it from stdin, keeping the secret out of shell history and ps output. The values are written to ~/.config/manifest/installer-config.yaml (mode 0600); do not edit the file by hand. Region is derived from the registry URL; you do not need to set it. Only long-lived IAM-user keys are accepted — for short-lived STS credentials, use the environment-variable path below.
Option B — export them in the environment (useful for one-off runs and for rotating without persisting):
export AWS_ACCESS_KEY_ID=<provided-by-manifest>
export AWS_SECRET_ACCESS_KEY=<provided-by-manifest>
export AWS_REGION=us-east-1Environment variables and any other AWS SDK credential source (~/.aws/credentials, IRSA, IMDS) take precedence over the persisted values, so an emergency credential swap does not require re-running config set.
3. Download platform packages
Pull the platform packages into ~/.local/share/manifest/versions/<x.x.x>/:
# Latest version
manifest-installer download
# Or pin a specific version
manifest-installer download --version <x.x.x>The platform's initial vulnerability data is downloaded and loaded separately, after deploy; see step 6.
FIPS 140 installs
A FIPS 140 install runs RKE2 in place of k3s and uses FIPS-built container images. Those images ship as a separate package set, so download it with --flavor fips:
manifest-installer download --flavor fips
manifest-installer download --flavor fips --version <x.x.x>--flavor defaults to default, the standard package set. Without --version, --flavor fips picks the latest release that has a FIPS package set. The host must already be running in FIPS mode before you deploy, and you answer the FIPS question in setup (step 4) to match.
A version directory holds one flavor. If you already downloaded the other flavor of the same version, download stops and asks you to remove ~/.local/share/manifest/versions/<x.x.x>/ first.
4. Set up the deployment
Run the interactive setup wizard:
manifest-installer setupYou'll be prompted for deployment-specific values such as the base domain, the administrator email, and (optionally) SMTP, OIDC, and CA certificate settings. On re-runs, the prompts default to your previous answers, so you can hit Enter to accept or type a new value to change it. The Configuration Reference describes the wizard, where your answers are stored, and how to change them later.
The first page asks about the host environment, including FIPS 140 compliant install. Answer yes only if the host is already in FIPS mode and you downloaded the FIPS package set (step 3); answer no for a standard install. deploy checks that the two agree.
If the wizard renders incorrectlyOver a serial console, a limited SSH client, or a screen reader, the full-screen interface can render garbled. Run
TERM=dumb manifest-installer setupto get the same questions as a plain line-by-line prompt flow with no screen redrawing.
Every answer — plus a set of workload credentials the installer generates for you (database passwords and similar) — is written to a single file, ~/.config/manifest/config.yaml. config.yaml is the single durable copy of these values — back it up after setup and after any later configuration change. Because sensitive values live in this file, the wizard asks you to make a deliberate encryption choice before it writes anything; see Encryption & Key Management for the choices and the consequences of key loss.
5. Deploy
Deploy the platform:
manifest-installer deploydeploy runs against the packages downloaded in step 3 and the configuration written in step 4. If either prerequisite is missing, deploy stops with a pointer to the missing step — it never silently runs download or setup on your behalf.
Before it changes anything on the host or cluster, deploy also checks that the downloaded packages match your FIPS answer. FIPS on with the standard package set, or FIPS off with the FIPS package set, stops the deploy with both values named and the manifest-installer download --version <x.x.x> --flavor <flavor> command that fetches the matching set.
deploy performs the following, in order:
- Host preparation — installs OS prerequisite packages, loads required kernel modules (
br_netfilter,overlay), applies sysctl settings, opens firewall ports (6443 for the k3s API, 31999 for the Zarf registry, 80/443 for Traefik), and applies SELinux/nftables/fapolicyd rules where applicable. - k3s installation — installs the k3s binary and systemd service (RKE2 on a FIPS 140 install). Both embed containerd as their container runtime, so no separate container engine is needed.
- In-cluster registry bootstrap — Zarf injects a temporary seed registry into the cluster, then promotes a permanent in-cluster registry. All container images for the platform come from this in-cluster registry, not from the public internet.
- Platform deployment — brings up Manifest itself: platform-level dependencies and operators, then the application workloads.
A first-run deploy typically takes 10–20 minutes, depending on image-pull speed and host performance.
deploy is safe to re-run from the top. If it fails partway through, fix the underlying issue and re-run the same command — each step is idempotent and the previously-completed work is detected as a no-op, so a re-run converges to the failed point in seconds. There are no checkpoint files to clean up and no special "resume" flag.
When the deploy completes successfully, the command exits with code 0 and the final line of output confirms the last package deployed and its version:
deployed package manifest-app, version <x.x.x>deploy does not print the platform URL. Verifying the install and reaching the web UI are covered in step 7.
6. Load vulnerability data
deploy brings up the platform without any vulnerability data. The initial data (NVD, OSV, KEV, EPSS) ships as dated vulnerability-data packages, the same ones an air-gapped install uses. Download them, then load them into the running platform, on the same host:
manifest-installer download-data
manifest-installer deploy-datadownload-data pulls a full snapshot plus the most recent 7-day refresh into ~/.local/share/manifest/data/ and records its selection. Re-running it pulls nothing already on disk. If your install has a FOCI subscription (SUBSCRIPTION_FOCI is enabled in config.yaml), run it with --include-foci instead, which also pulls the newest FOCI snapshot. Installs without a FOCI subscription run the bare command above:
manifest-installer download-data --include-focideploy-data deploys the downloaded data packages oldest-first, skipping any the cluster already has. Each package's in-cluster load can take up to 3 hours; the command waits for completion. If it fails partway, re-run it. Already-loaded packages are recorded in the cluster and skipped.
deploy-data logs each package as it starts and finishes, and nothing in between. To watch a load progress, follow the loader Job's logs from a second shell:
kubectl logs -f job/vuln-loader -n jobEach package's deploy replaces the vuln-loader Job, so this follows the package currently loading and exits when that package's pod finishes. Run it again for the next package.
Keeping the data currentAfter this first load, the platform keeps the data current on its own: it ingests updates directly from the vulnerability data sources your firewall must allow. You don't need to run
download-dataordeploy-dataagain on a connected install.
7. Verify the install
statuscommandThe
manifest-installer statushealth-report command is not yet available in this release. Until it ships, verify the install withkubectlas below.
Point kubectl at the new cluster and confirm pods are healthy:
export KUBECONFIG=$HOME/.kube/config
kubectl get nodes
kubectl get pods -AAll pods should reach Running or Completed. Confirm Traefik is listening:
kubectl -n traefik get svcThen resolve your domain to the host IP (via DNS or a temporary /etc/hosts entry) and visit the URL in a browser. Signing in for the first time and trusting the generated CA certificate are covered in Post-Install.
FIPS installs: check the RKE2 STIG settings
Starting with release 3.12.0, a FIPS install configures RKE2 to the DISA Rancher Government Solutions RKE2 STIG (Version 2, Release 7) and places a check script on the host. Run it as root on the RKE2 server:
sudo bash /usr/local/share/manifest/rke2-stig-check.shIt prints one line per STIG rule and exits non-zero if any rule fails:
PASS/FAIL: the rule's automated check, with the evidence it found. Like the STIG's own check text, it reads the running components' command lines fromps, plus/etc/rancher/rke2and the RKE2 data directory.REVIEW: rules the STIG leaves to your ISSO. The line shows what to review:- CNTR-R2-000580: the API server and etcd ports, to compare against your PPSM CAL.
- CNTR-R2-000800: literal environment values with credential-like names. Values the platform loads from Kubernetes Secrets still need ISSO review.
- CNTR-R2-001620: the RKE2 version on each node, to confirm against the Kubernetes release support dates.
- CNTR-R2-000460: image verification against the Rancher Government Carbide signing key, using Hauler.
Every deploy rewrites /etc/rancher/rke2/config.yaml, and any edits made to it directly are lost. Put site-specific RKE2 settings in a file under /etc/rancher/rke2/config.yaml.d/ instead, which the deploy does not touch. Like config.yaml, these files must be owned by root with mode 0600, in a directory with mode 0700; the check script fails CNTR-R2-000520 otherwise. RKE2 reads those files after config.yaml, and for each key the last value read wins, so a list such as kube-apiserver-arg: there replaces the STIG settings rather than adding to them. Append with a + suffix instead (kube-apiserver-arg+:) in every file that sets the key, since a later file without the + replaces everything before it, the STIG settings included. Then restart RKE2 to apply the change:
sudo systemctl restart rke2-serverNext Steps
- Complete the Post-Install steps: first login and certificate trust
- Review the Configuration Reference for the setup wizard values and day-2 configuration changes
- To update to a newer release later, see Upgrade
- If you encounter issues, see the Troubleshooting Guide
Updated 4 days ago
