Prerequisites
Settle these items once and every install path goes smoothly. They apply whichever target and installer you pick, and most of them take minutes. When you finish, go back to Choose your path.
Get the installer
Clone the public installer at the tag for your chart version:
git clone --branch v<chart_version> https://github.com/marshal-org/marshal-install.git
cd marshal-installSee Get the installer for the tag scheme and how upgrades work.
A guided install, if you want one
The installer ships a skill for Claude Code that walks through the whole install with you. It checks your cloud environment first, works through the configuration, runs the apply, and explains what a failure means.
cd marshal-install
claude # then: /install-marshalIt tells you what it is about to create before it creates it, it never disables a safety check to make an apply pass, and it keeps your licence token, registry token and database password out of anything it writes. Everything it does is on these pages, so following them by hand gets you to the same place.
Check your environment first (AWS)
The installer ships a read-only script that checks an AWS account against everything the EKS path needs. Run it before you write any tfvars, and again on the day of the install, because quotas get used and subnets get re-tagged in between.
MRPT_FILE=$(umask 077; mktemp) # a new file only you can read
read -rs MRPT; printf '%s' "$MRPT" > "$MRPT_FILE"; unset MRPT # paste the pull token
scripts/preflight-aws.sh --region eu-west-1 \
--vpc-id vpc-0123 --private-subnets subnet-a,subnet-b \
--ingress-host marshal.acme.com \
--deployment-id dep_ab12cd --pull-token-file "$MRPT_FILE" --chart-version <chart_version>
rm -f "$MRPT_FILE"Only --region is required. Anything you leave out is reported as skipped, so a partial run
is still useful. Every call is a describe, get or list: it creates nothing, changes nothing and
needs no write permission. It exits 1 if any check failed.
It checks your credentials, the pinned EKS and Postgres versions in your region, your on-demand vCPU quota and how much of it is already in use, a free Elastic IP for the NAT gateway, the VPC DNS settings, and for each subnet its AZ, free IP addresses, egress route and the tag the load balancer controller needs. The most common thing it catches is private subnets that are not actually private.
A clean run proves your environment, not your permissions: no read-only check can show that
the identity running terraform apply may create a cluster, roles and a database. For the
full AWS list, including the permission and organization-policy items, see
AWS: before you install. On GKE, work through this page by
hand.
Onboarding credentials
Your Marshal contact issues three credentials and a chart version, once per deployment, over a secure channel.
| Value | Looks like | What it’s for |
|---|---|---|
deployment_id | dep_ab12cd | Identifies your deployment. It is also the registry username. |
installation_jwt | eyJhbGci... | Your license. Marshal verifies it against its public keys and refreshes it on a schedule. |
registry_pull_token | mrpt_eyJhbGci... | Authenticates every pull from registry.marshal.codes: product images, the Helm chart, and space tool images when you run without a cache. |
chart_version | <chart_version> | The exact product version to install. Your onboarding email names it. |
Verify them before anything else:
read -rs MRPT # paste the registry_pull_token, then Enter. Nothing is echoed.
printf '%s' "$MRPT" | helm registry login registry.marshal.codes \
--username <deployment_id> --password-stdin
helm pull oci://registry.marshal.codes/marshal-charts/marshal --version <chart_version>Reading the token in keeps it out of your shell history and the process list.
If the pull succeeds, you are ready. If it fails, check the values with your Marshal contact
before you go further: every path needs working credentials. The registry does not list versions, so
chart_version always comes from your onboarding email.
On the Terraform paths the pull token is used at plan time, not only at apply. The Helm
provider logs in to the registry while it configures itself, so a missing or invalid token
stops terraform plan with an error that points at providers.tf. This holds even when you
pull the chart from a local file or your images from your own cache.
PostgreSQL 16
Marshal keeps all durable state in Postgres 16: organizations, members, spaces, credentials, manifests and the audit trail. Where it comes from depends on your path:
- single-node k3s runs Postgres in the cluster by default. Bring a managed one for durable data.
- EKS can create an RDS instance for you when the installer also builds the cluster.
- GKE and ArgoCD use a Postgres you provide, such as Cloud SQL.
A Postgres you bring is a connection string in this shape, reachable from the cluster over private networking:
postgres://<db-user>:<db-password>@<private-ip>:5432/<db-name>?sslmode=requireSee Datastore options to compare the targets.
Hostname and network access
- Decide your hostname now, for example
marshal.acme.com. The first apply or sync needs it, even before real DNS exists. Later you point it at the cluster’s load balancer, so you need control of DNS for that domain. For a trial without DNS of your own, any placeholder name such asmarshal.trial.internalworks: you open Marshal throughkubectl port-forward -n marshal svc/web 8080:80athttp://localhost:8080, which serves the console, the API and the CLI with no DNS and no route to the load balancer. - Using previews?
preview_domain(for examplepreview.acme.com) is optional. Leave it unset to skip previews. If you set it, plan a wildcard DNS record (*.preview.acme.com), because every preview URL is a subdomain of it. A trusted wildcard certificate needs DNS-01 validation. See Configuration and Troubleshooting. - Reach inside your VPC. Marshal is not exposed to the public internet by default. Use the VPN or bastion access you already have, or the bastion steps on your target’s page.
- Outbound HTTPS (443) from the cluster to
registry.marshal.codes(through your image cache where you run one) and tokeys.marshal.codesandtokens.marshal.codesfor license checks. Marshal never needs inbound access into your environment. See Networking and egress for the full list.
An image cache for production
Every space start pulls images, and registry.marshal.codes applies a rate limit per pull
token. A pull-through cache in your own environment keeps those pulls fast and local as usage
grows, so plan one for production. GKE creates one for you by default. On EKS and k3s, nodes
pull directly until you add one. See Registry options.
Nodes for Docker
Spaces with the docker package run a rootless Docker daemon, which needs user namespaces on
the nodes that run spaces: user.max_user_namespaces above 0. The usual node images ship that
way, including GKE Container-Optimized OS (with or without image streaming), GKE Ubuntu, EKS
Amazon Linux 2023, Debian 12 and Ubuntu 24.04. To check a node:
cat /proc/sys/user/max_user_namespacesWhat you should see: a number above 0, such as 63736.
Local tools
- Terraform 1.5 or newer (Terraform paths)
- Helm 3.14 or newer, and kubectl
- Your cloud’s CLI (
gcloudoraws), signed in - ArgoCD path: ArgoCD in the cluster (namespace
argocd) and theargocdCLI
Checklist
- AWS only:
preflight-aws.shrun, with no failures - Credentials verified with
helm registry loginandhelm pull - Postgres 16 reachable and its connection string at hand, unless the installer creates it
- Hostname decided
- If you use previews:
preview_domaindecided and a wildcard DNS record planned - Network access into the VPC arranged
- Image cache plan decided
- Using Docker in spaces: nodes allow user namespaces
- Tools installed
Next: pick your path.