CLI
The kuben binary is the server, the operator’s tool, and a client of any Kuben server (below). Every subcommand reads the same configuration: built-in defaults, then /etc/kuben/config.toml, then ./kuben.toml, then KUBEN_* environment variables. --config <file> layers a file between the defaults and the environment.
kuben [--config <file>] <command> [options]kuben --version | -Vkuben <command> --help--config is also KUBEN_CONFIG. kuben --version prints kuben <version>, for example kuben 2.0.0-alpha.1.
Runs Kuben: the REST API, the embedded console and the controllers.
kuben serve [--roles api,controller] [--dev]| Option | Meaning |
|---|---|
--roles |
Comma-separated roles for this process: all, api, controller, activator. Also KUBEN_ROLES; without either, server.roles from the configuration (default all). activator is reserved for scale-to-zero: this build logs a warning and ignores it. |
--dev |
Development mode: pretty debug logs and an insecure cookie so the console works over plain HTTP. It still needs PostgreSQL through KUBEN_DATABASE__URL. Never use in production. |
On the first start with an empty database, serve creates the initial admin. Inside Kubernetes the generated password goes into the Secret kuben-initial-admin; outside it is printed once to the terminal, or, without a terminal (systemd), written to initial-admin-password in Kuben’s state directory (/var/lib/kuben on a server set up by kuben setup), readable only by its user. It never goes to the log. KUBEN_BOOTSTRAP__ADMIN_PASSWORD sets it instead.
Startup order: configuration → database migrations (under a lock) → CRDs server-side-applied → informers list and sync → /readyz turns 200 → leader election (when enabled) → controllers. Shutdown is the reverse: readiness off → drain in-flight requests → cancel subsystems (the leader releases its Lease) → flush the audit log.
migrate
Section titled “migrate”Applies pending database migrations and exits. serve does this on every start too; migrate is for running it as a separate step in a pipeline.
kuben migratedoctor
Section titled “doctor”Checks the prerequisites and prints what is missing and what is optional: database reachability, cluster connection and RBAC, Gateway API CRDs, cert-manager (with Gateway support), metrics-server.
kuben doctorkuben doctor --cluster checks only the cluster and needs no database: run it with your kubeconfig before installing Kuben into a cluster you bring. For each feature it says whether the cluster has what it needs, and what to install when not: public routes (Gateway API CRDs and an accepted GatewayClass; Envoy Gateway when there is none), HTTPS (cert-manager with Gateway API support and a Ready ClusterIssuer), volumes (a default StorageClass), isolation (a CNI that enforces NetworkPolicies) and autoscaling (metrics-server). It also checks, for whoever runs it, the permissions Kuben’s ClusterRole grants. A missing feature is a warning: only that feature is off. Kuben never installs or upgrades CRDs in a cluster it did not set up.
Exit code is non-zero when a required check fails. doctor warns (database role) when Kuben connects as a superuser or a BYPASSRLS role, because row-level security is then bypassed. On a server set up by kuben setup, run it as the service user so peer authentication lets it into the database:
sudo -u kuben kuben doctorreset-admin
Section titled “reset-admin”Sets a new password for the initial admin account (admin_email in the bootstrap configuration).
kuben reset-admin [--password <new>]Without --password (or KUBEN_ADMIN_PASSWORD), a random password is generated and printed once. When the account does not exist, it is created. Inside the chart:
kubectl -n kuben-system exec deploy/kuben -- /kuben reset-adminResetting the password revokes the account’s sessions.
setup-token
Section titled “setup-token”On a first start outside a cluster with no KUBEN_BOOTSTRAP__ADMIN_PASSWORD, no admin is seeded: the console shows /setup, where the first admin account and the organization are created. On any address but loopback that page needs a setup token, so nobody who finds the port first can claim the installation, and it takes the password only over HTTPS or from the server itself (an SSH tunnel), unless security.insecure_setup is set. serve prints the link with the token to its terminal on that first start; this command prints a fresh one whenever you need it:
kuben setup-token No admin account yet. Finish the setup in your browser:
http://localhost:3000/setup#token=8f2c…d1
Run `ssh -L 3000:127.0.0.1:3000 <you>@203.0.113.7` on your computer first; the admin password never travels over plain HTTP. For an HTTPS console: kuben setup --domain <domain> --acme-email <email>. On a network you trust: kuben setup --allow-http-setup. The link is valid for 30 minutes; print a new one with `kuben setup-token`.With an https:// public URL the link points there instead, with the tunnel as the way in until DNS and the certificate are ready. The token follows #, so browsers never send it to a server or write it to a proxy log.
The token lives in setup-token in Kuben’s state directory (/var/lib/kuben on a server set up by kuben setup), readable by Kuben’s user only, and is deleted once the account exists. After that, /setup answers 404 and the page redirects to the sign-in.
agent-token
Section titled “agent-token”Issues a bootstrap token for the cluster agent of a cluster, for an agent you run yourself. The token is printed once and valid for one enrollment; only its hash is kept. The agents in Kuben’s own cluster (the chart’s, and the one kuben setup installs) enroll without it.
kuben agent-token [--cluster primary] [--org <slug>] [--ttl-minutes 30]| Option | Meaning |
|---|---|
--cluster <name> |
the cluster the agent runs in; created when it does not exist yet. Default primary |
--org <slug> |
the organization; default bootstrap.org_slug |
--ttl-minutes <n> |
how long the token stays valid. Default 30 |
The output names the file of the hub’s CA certificate and the kuben-agent command line. Give the agent the token through a file or standard input (kuben-agent --token-file or --token-stdin), never as an argument.
Makes the Linux server it runs on a Kuben server. The one-line installer runs it for you as root; it is also what you re-run to upgrade or repair, because every step checks before it acts. A run that finds everything in place changes nothing, and the service restarts only when its binary, unit or configuration changed.
sudo kuben setup [--plan] [--domain <domain>] [--acme-email <email>] [--acme-staging] [--datastore sqlite|etcd] [--port 3000] [--kubeconfig <file>] [--no-k3s] [--bind-local] [--allow-http-setup] [--yes]--plan shows what a run would do, from the same checks, and changes nothing.
Every run is recorded in the install journal, /var/lib/kuben/install/journal.json: each step with its result (changed, unchanged, failed), and each resource setup touched with its owner. A resource setup created belongs to Kuben; one that was there first (a PostgreSQL database or role, a k3s, a firewall rule, a directory) stays preexisting for good. A run that stopped halfway is named on the next run, which checks every step again. kuben serve copies the journal into the database for Doctor and support.
| Step | What happens |
|---|---|
| Preflight | root, systemd, OS, architecture and memory (a warning under 2 GB); refuses inside a container unless --kubeconfig names a cluster |
| Binary | copies itself to /usr/local/bin/kuben when run from elsewhere |
| User | the system user kuben, /var/lib/kuben (the kubeconfig copy and the setup token) and /etc/kuben |
| PostgreSQL | installs it from the distribution’s packages when it is missing (apt, dnf, zypper), starts it and creates the role and database kuben. Kuben connects over the Unix socket with peer authentication, so there is no password. A config that already names another PostgreSQL is kept as is |
| Cluster | uses --kubeconfig, an installed k3s, or the kubeconfig of the current user; otherwise installs k3s v1.36.4+k3s1 with its installer for that release, checked against a pinned SHA-256, with reserved CPU and memory and eviction thresholds, SQLite or (--datastore etcd) embedded etcd, and waits for the node to be Ready (--no-k3s fails instead). A copy of the kubeconfig, readable by kuben only, goes to /var/lib/kuben/kubeconfig |
| Platform | on this server’s k3s only (never on a --kubeconfig cluster): Traefik as the Gateway API provider, the Gateway API v1.5.1 CRDs (checksum-verified) and cert-manager v1.21.2 with Gateway API support when missing, the namespace kuben-system, and with --acme-email the ClusterIssuer letsencrypt (HTTP-01 through Kuben’s Gateway). What exists is used, never upgraded |
| Config | /etc/kuben/config.toml (port, public URL, database postgres:///kuben?host=/run/postgresql&user=kuben, kubeconfig, and [security]: the Gateway’s pod network 10.42.0.0/16 as the only trusted proxy when the console gets an HTTPS address, insecure_setup with --allow-http-setup); an existing file is kept, and a file setup wrote gains missing sections once |
| Port | refuses a port another process holds and names it |
| Service | kuben.service (enabled, Restart=always), then waits for /livez and /readyz. A kuben.service that setup did not write is never replaced: setup stops and says so |
| Agent | on this server’s k3s: the cluster agent in kuben-system (the chart’s manifest), enrolled automatically from what Kuben publishes once the first admin (and so the organization) exists; [agent] in the config makes Kuben listen on port 7443, and with ufw or firewalld active that port is opened for the pod network 10.42.0.0/16 only |
| KubenConfig | after the service started: gateway class traefik with Traefik’s ports, the issuer, and --domain as the base domain. Fields someone else set are kept, with a warning |
| Console | with --domain and --acme-email on this server’s k3s: the console at https://kuben.<domain> through Kuben’s Gateway (namespace kuben-console: a route, and a Service whose endpoint is this server’s port), with a certificate like any app’s; public_url is that address |
| Firewall | opens the port in ufw or firewalld when one is active |
| Link | the setup page with its token on a first install (over HTTPS, or through an SSH tunnel), the console address afterwards |
| Option | Meaning |
|---|---|
--port <n> |
console and API port (KUBEN_PORT). Default 3000, or the next free port when another process holds it; an install keeps its port, and --port moves it |
--kubeconfig <file> |
manage this cluster instead of installing k3s (KUBEN_KUBECONFIG) |
--no-k3s |
never install k3s |
--bind-local |
listen on 127.0.0.1 only; reach the console through an SSH tunnel |
--allow-http-setup |
let the first admin be created over plain HTTP from another machine, on a network you trust (security.insecure_setup) |
--yes |
go ahead on warnings without asking |
--plan |
print what a run would do; change nothing |
--domain <domain> |
base domain for app hostnames (KUBEN_DOMAIN); point *.<domain> at the server |
--acme-email <email> |
email for Let’s Encrypt (KUBEN_ACME_EMAIL); apps get HTTPS |
--acme-staging |
Let’s Encrypt’s staging server, for tests |
--datastore sqlite|etcd |
how an installed k3s keeps its state; etcd for a server that will grow |
status
Section titled “status”Without an app: the service, the admin account, the cluster and the console address of this server. With one, see status of an app.
kuben statusuninstall
Section titled “uninstall”Removes kuben.service. The PostgreSQL database kuben, /var/lib/kuben, /etc/kuben and the binary stay, so a later kuben setup finds everything again.
sudo kuben uninstall [--purge [--keep-apps]] [--yes]--purge also removes what kuben setup created, according to its journal: /var/lib/kuben, the configuration, the binary, the system user, the database and role kuben, the firewall rule, and k3s. What was there before setup first ran stays: a database or role kuben you created yourself, a k3s you installed, a rule you opened. PostgreSQL itself stays installed, and an external database is never touched. A server set up before the journal existed keeps the earlier rule: the database and role are dropped when they are this server’s, and k3s goes only when setup installed it.
--keep-apps (with --purge) removes Kuben but keeps the apps running. The cluster stays, even a k3s that setup installed. So do the Gateway’s namespace, the Traefik settings, the ClusterIssuer, and the apps’ namespaces, Secrets and volumes. At the end, the command lists what is left. See Support bundles and leaving Kuben.
backup
Section titled “backup”Backs up the database with pg_dump into a new directory kuben-<time> under backup.dir. The directory holds the dump and a manifest with versions, the schema version, a checksum and the key fingerprints. --include-keyring also copies the secret keyring into the backup. Older backups beyond --keep are removed. See Backup and restore.
kuben backup [--out /backups] [--include-keyring] [--keep 7]restore
Section titled “restore”Restores a backup into an empty database. --check only verifies that the backup is intact and restorable.
kuben restore --from /backups/kuben-20260917T020000Z [--check]upgrade-check
Section titled “upgrade-check”Checks, without changing anything, whether this binary may upgrade the installation. Run it before the new version replaces the old one. See Upgrading.
kuben upgrade-check [--major]dns01-issuer
Section titled “dns01-issuer”Creates or updates a cert-manager ClusterIssuer that answers ACME DNS-01 challenges through Cloudflare, for wildcard and apex certificates. The token goes into a Secret next to cert-manager. See Custom domains.
kuben dns01-issuer --email ops@example.com --token-file ./cloudflare-token [--name letsencrypt-dns] [--namespace cert-manager] [--staging] [--dry-run]| Option | Meaning |
|---|---|
--email <address> |
the ACME account’s email address |
--token-file <file> |
a file holding a Cloudflare API token with Zone:DNS:Edit on the zones |
--name <name> |
the ClusterIssuer to create or update. Default letsencrypt-dns |
--namespace <ns> |
the namespace cert-manager runs in; the token’s Secret goes there. Default cert-manager |
--staging |
Let’s Encrypt’s staging server (untrusted certificates) |
--dry-run |
print the objects instead of applying them, with the token redacted |
support-bundle
Section titled “support-bundle”Writes a local support bundle containing:
- versions;
- allowlisted configuration;
doctoroutput;- database counts;
- the cluster state;
- with
--logs, Kuben’s own logs.
Nothing is uploaded. --preview shows what the bundle would hold and writes nothing.
kuben support-bundle [--preview] [--logs [--log-lines 500]] [--out DIR] [--keep 3]--out defaults to support in the state directory; --keep is how many bundles stay there.
version
Section titled “version”kuben version [--bundle [--json]]kuben --versionkuben version prints the version and the platform, kuben 2.0.0-alpha.1 (linux x86_64); kuben --version (or -V) only kuben 2.0.0-alpha.1. --bundle adds what a release installs besides Kuben, pinned with its digests: k3s, the Gateway API CRDs, cert-manager and PostgreSQL. --bundle --json prints the lock file itself (bundle.lock.json).
Client commands
Section titled “Client commands”These talk to a Kuben server over its API, from your computer or a CI job, with an API token (console → Account → API tokens). A token scoped to a project and a role can do what that role can there, nothing else.
An app is named project/environment/app; with a default project and environment (--project, --environment, or saved by login), app or environment/app is enough. Every client command takes --context, --project and --environment (also KUBEN_CONTEXT, KUBEN_PROJECT, KUBEN_ENVIRONMENT).
kuben login https://kuben.example.com [--token <token>] [--name <context>] [--project shop] [--environment prod]Checks the token against the server and saves it as a context in ~/.config/kuben/contexts.json ($XDG_CONFIG_HOME is honoured; KUBEN_CONTEXT_FILE names another file), readable by you only; the newest login is the current context. Without --token (or KUBEN_TOKEN) the token is read from standard input, without echo on a terminal. A plain http:// address other than localhost gets a warning: the token would cross the network unencrypted.
In CI, set KUBEN_URL and KUBEN_TOKEN instead: no file is read or written.
kuben apps [--json]Every app you can see (only the default project and environment, when set), with its state, image and address.
deploy
Section titled “deploy”kuben deploy shop/prod/web --image ghcr.io/acme/web:1.4.2 [--no-wait] [--timeout 600] [--idempotency-key <key>]A tag is resolved to its digest at the registry first; the deployment carries the digest. It is accepted only on top of the revision the command saw, so a deploy never silently replaces a newer one. The command then follows the run until it succeeds (exit 0) or ends otherwise (exit 1); --no-wait returns once it is accepted. Retrying with the same --idempotency-key returns the same run instead of a second one.
status of an app
Section titled “status of an app”kuben status shop/prod/web [--json]The app’s state, image, address, current revision, pods, and its Doctor: every check between a visitor and the app, with what to do about each that is not fine.
kuben logs shop/prod/web [-f] [--previous] [--tail 100] [--process worker]The recent lines of the app’s pods ([pod] in front when there are several); --previous those of the container before its last restart. -f keeps printing new lines, from new pods of a rollout too, until you stop it or the server ends the stream after an hour.
rollback
Section titled “rollback”kuben rollback shop/prod/web [--to <revision>]A new revision with the image and configuration of an earlier one, by default the one before the current. Domains and volumes stay as they are.
builds
Section titled “builds”kuben builds shop/prod/web [--json] [--limit 20]kuben builds logs shop/prod/web <build> [-f]kuben builds trigger shop/prod/webkuben builds cancel shop/prod/web <build>For apps built from Git (see Builds):
kuben buildslists the newest builds (20 by default,--limitup to 100): id, phase (with the failure or why it is blocked), the stage it is at, commit, branch and when it was created.--jsonprints the builds as the API returns them, stages included.builds logsprints a build’s log: from the build pod while it runs, the kept end of it afterwards.-f(--follow) keeps printing new lines, each with its stage in front ([build] …), until the build’s log ends; an error that ended it goes to stderr.builds triggerbuilds the head of the app’s branch now, like Build now in the console (a source sync), and prints the sync and the newest build.builds cancelstops a build: a queued one is cancelled at once, a running one says it is stopping.
A build is named by its id (a UUID), as kuben builds lists it. Reading needs the viewer role; trigger and cancel need developer.
Exit codes and logs
Section titled “Exit codes and logs”| Code | Meaning |
|---|---|
0 |
success |
1 |
the command failed; the reason is on stderr |
2 |
the command line does not parse: an unknown command or flag, or a bad flag value |
kuben deploy exits 1 when the deployment ends in any state but success. serve exits 1 when a required subsystem fails to start, for example when KUBEN_KUBE__REQUIRED=true and no cluster is reachable. Commands that log write to stderr, as JSON by default (KUBEN_TELEMETRY__LOG_FORMAT=pretty for a terminal).