Skip to Content
Installing Marshalsingle-node k3s

Install onto a single k3s node

Get Marshal running on one Linux node: fill in five values, run one apply, and open the URL. The installer brings its own Postgres and Redis on the node, so there is nothing else to set up first. It is the fastest way to evaluate Marshal, and with a managed Postgres it serves a small team well.

What you can do with it

  • Evaluate on a spare VM. Install k3s on any Linux machine, point the installer at it, and claim your owner account the same day.
  • Run a demo node on Google Cloud. Let the installer create a private GCE VM with k3s for you, or one with its own public address for a demo.
  • Serve a small team. Keep the single node and move Postgres to a managed database for durable data and backups.

What you need

  • The Prerequisites: onboarding values and local tools (Terraform 1.5 or newer, Helm 3.14 or newer, kubectl).
  • A Linux node running k3s 1.27 or newer, and a kubeconfig for it. k3s ships the Traefik ingress and the local-path storage class, which is all this path needs. When the installer creates the node for you, it uses an e2-standard-4 VM (4 vCPU, 16 GB) by default.

Install k3s and get a kubeconfig

curl -sfL https://get.k3s.io | sh - # Optional, and recommended for large tool images: # --kubelet-arg=registry-qps=0 --kubelet-arg=serialize-image-pulls=false

The admin kubeconfig on the node is /etc/rancher/k3s/k3s.yaml. Copy it to your workstation and change the server address:

scp node:/etc/rancher/k3s/k3s.yaml ~/.kube/marshal-k3s.yaml perl -pi -e 's#https://127\.0\.0\.1:6443#https://<NODE-IP>:6443#' ~/.kube/marshal-k3s.yaml export KUBECONFIG=~/.kube/marshal-k3s.yaml # or set kubeconfig_path in tfvars

Install

Get the installer

git clone --branch v<chart_version> https://github.com/marshal-org/marshal-install.git cd marshal-install/single-node-k3s

See Get the installer for the tag scheme.

Fill in terraform.tfvars

cp ../examples/single-node-k3s.tfvars.example terraform.tfvars $EDITOR terraform.tfvars

Five values are required: installation_jwt, deployment_id, registry_pull_token, chart_version and ingress_host. Everything else has a default.

No domain? Use sslip.io  with the node’s IP, written with dashes: ingress_host = "marshal.203-0-113-5.sslip.io".

Apply

terraform init terraform apply

Check the install

terraform output next_steps # verify commands and the app URL kubectl get pods -n marshal

What you should see

Every pod in the marshal namespace is Running, and terraform output app_url prints your address. HTTPS is on by default with a self-signed placeholder certificate, labeled “Marshal Platform Temp Cert”, so your browser shows a warning until you add a real certificate.

Claim your owner account

Read the setup token

kubectl get secret marshal-app-secrets -n marshal -o jsonpath='{.data.SETUP_TOKEN}' | base64 -d

The token works only until the first owner exists.

Open /setup

Go to your address with /setup at the end, for example https://marshal.acme.com/setup. Paste the token, enter your email and a password, and select Claim instance.

You are signed in as the owner. Next, follow Getting started as an owner to invite your team, then run your first task.

Datastores

By default the installer runs postgres:16-alpine on a local-path volume and redis:7-alpine on the node, and connects Marshal to both. The node is fully self-contained, and Postgres has no high availability or backups. For durable data, turn off the in-cluster pair and give the installer both connection strings:

provision_datastores = false database_url = "postgres://marshal:PW@10.0.0.5:5432/marshal?sslmode=require" redis_url = "redis://10.0.0.6:6379"

See Datastore options.

TLS

tls_mode decides how the ingress serves traffic:

tls_modeResult
self_signed (default)HTTPS with a generated placeholder certificate. Browsers warn.
inlineHTTPS with your certificate, from tls_cert_pem and tls_key_pem
secretHTTPS with a TLS Secret you manage, named by tls_secret_name
nonePlain HTTP

To use cert-manager, install it with a ClusterIssuer, set ingress_annotations (for example { "cert-manager.io/cluster-issuer" = "letsencrypt" }) and ingress_tls, and point the host’s DNS A record at the node.

Images

Nodes pull directly from registry.marshal.codes with your pull token by default. To use a mirror you run, set image_cache_registry and registry_auth_mode. See Registry options.

Let the installer create the node

Set deploy_cluster = true with project_id, zone and admin_source_ranges, and Terraform creates a private GCE VM with Cloud NAT and installs k3s on it. The new node’s API is private, so the install runs in two steps:

Create the node

terraform apply -var deploy_cluster=true -var project_id=acme-prod \ -var zone=europe-west1-b -var 'admin_source_ranges=["10.8.0.0/24"]'

Fetch its kubeconfig over IAP, then install

gcloud compute ssh marshal-k3s --zone europe-west1-b --tunnel-through-iap \ --command 'sudo cat /etc/rancher/k3s/k3s.yaml' > k3s.yaml # change https://127.0.0.1:6443 to the node's internal IP terraform apply -var deploy_cluster=false -var kubeconfig_path=./k3s.yaml

terraform output next_steps prints these exact commands after the first step. Run Terraform from inside the VPC (a bastion, VPN or IAP tunnel): the k3s API on port 6443 accepts only admin_source_ranges.

For a demo node that serves the app from its own address, set public_ip = true. Terraform reserves a static external IP and opens ports 80 and 443 to public_http_source_ranges (the whole internet by default). SSH and the k3s API stay on IAP and admin_source_ranges. terraform output k3s_external_ip prints the address for your DNS record.

Upgrades

Check out the installer tag for the new version, bump chart_version in terraform.tfvars to match, and run terraform apply. See Day-2.