Quickstart
The shortest path from nothing to a running app: install the CLI, sign in, get a cluster, deploy from your project directory, get the URL. Every command below is real.
Install the CLI
Install the RunOS CLI, then pull the command manifest:
curl -fsSL https://get.runos.com/cli.sh | bash
runos manifest update
runos manifest update downloads the latest command set from the API. The CLI is manifest-driven, so do this once after install and after upgrades.
Sign in
A personal access token (PAT) and every command are scoped to one account id (aid), for example rjwrn. Sign in with the browser flow:
runos login --account-id rjwrn
For CI or headless use, pass a PAT instead of the browser flow:
runos login --api-key runos_pat_<keyId>.<secret> --account-id rjwrn
--account-id is required with --api-key. Credentials are stored in ~/.runos/config.json. The RUNOS_API_KEY environment variable overrides the stored key (setting it to an empty value is a hard error, not a fallback).
Pick a default cluster so you can drop --cid from later commands:
runos config set cid ky3
Get a cluster (provision a cloud node OR install your own server)
A RunOS cluster is one or more Linux nodes running Kubernetes. You need at least one node. Create the cluster first:
runos clusters add --name my-cluster
This gives the cluster a cluster id (cid): 3 to 16 lowercase alphanumeric characters (for example ky3). Run runos clusters list to see it. You pass it to later commands as --cid. Then get a node into it one of two ways.
Option A: provision a cloud node
RunOS can provision a node on hetzner, digitalocean, or scaleway. Each needs an integration (your provider API token) added once:
runos integrations add hetzner
runos integrations list
Then provision the node and auto-configure the cluster for app deployment in one step:
runos integrations hetzner add-server \
--cid ky3 \
--integration-id <integration-id> \
--request-as-cp \
--auto-configuration configureForDeployment \
--follow
--auto-configuration configureForDeployment installs the full deployment stack (MinIO, PostgreSQL, Valkey, Harbor, plus build config) once the server joins. Use --provider-config.instance-type and --provider-config.location to size and place it (see runos integrations info).
Option B: install RunOS on your own server
Have an Ubuntu 22.04, 24.04, or 26.04 server with a public IP? Generate a join command and run it on that server:
runos nodes join-command ssh-remote --cid ky3 --request-as-cp
Run the printed command on your server. After it joins, install the deployment stack:
runos clusters configure-for-deployment --cid ky3
One node is fine to start, but a single node is not highly available. Add more nodes later with the same commands.
Confirm the node is ready:
runos nodes list --cid ky3
NAME HOSTNAME STATUS ISCP ISWORKER PUBLICIP PROVIDER
web-01 node-1 ready true true 203.0.113.10 hetzner
Write runos.yaml
runos.yaml is the desired state for your app: name, ports, resources. RunOS reads it from the current directory at deploy time. The minimum is a name, one port, and a resource class:
app: hello-runos
servicePortMappings:
- port: 8080
standardHttps: true
resourceRequirementClassId: app.sl1.beff
port must match the port your container listens on. standardHttps: true puts the app behind the cluster's HTTPS ingress with a default domain. app.sl1.beff is the standard small app resource class (grammar is service.tier.size); leave cpu, memory, and replicas unset to use the class defaults.
Add a Dockerfile
RunOS builds your image from a Dockerfile in the project directory. RunOS clusters run every container as non-root, so the contract is:
- Run as a non-root user:
USER 10001. - Listen on a port above 1024 that matches
servicePortMappings.port(here, 8080). - Keep the build reproducible (pin a base image tag, install from a lockfile).
A minimal Node example:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER 10001
EXPOSE 8080
CMD ["node", "server.js"]
For a static site, use nginxinc/nginx-unprivileged:1.27-alpine (it listens on 8080 as a non-root user) instead of nginx:alpine, which binds port 80 and needs root.
Deploy
From the project directory (the one holding runos.yaml and your Dockerfile):
runos deploy --follow
runos deploy tarballs the project, uploads it, and RunOS builds the image in-cluster. No local Docker, no git push required. --follow streams the build and rollout until done; without it, deploy prints a job id and returns immediately. Add --cid <cid> to target a cluster other than your default.
Check status and get the URL
List your apps to get the 5-character app id and status:
runos apps list --cid ky3
Watch deploy jobs:
runos jobs list --cid ky3
Once rolled out, get the live URL:
runos apps network-access <app-id> --cid ky3
That command prints the HTTPS URL serving your app. Open it and you are live.
Next steps
- Add a managed service (PostgreSQL, Valkey, MinIO, Kafka, and more) with
runos servicesand wire it into your app viarequires:. - Deploy from GitHub or GitLab by commit SHA:
runos deploy --sha <sha> --app <id>. - Run a one-off task (database migration or seed) against a deployed image:
runos run. - Drive RunOS from an AI assistant:
runos mcp bootstrapfirst, thenrunos mcp configure <claude|codex|gemini|opencode>.