yeke.io · docs
Installation
Run YEKE as a single container. Start with the Docker command below, then follow the sections on persistent settings, upgrades, Compose and Kubernetes.
Requirements
There are three requirements to get started. You can connect a Kubernetes cluster after installation.
- A machine with Docker. Docker Compose is not required; use it if you'd like (further down).
openssl— or anything else that can produce 32 random bytes.- Access to Docker Hub — the
nairotech/yekeimage is public and multi-arch (amd64 + arm64).
There's no separate web container and no reverse proxy in
front of it anymore. The UI, the API, and the agent tunnel all come from the same process, the
same port; this image is the only thing you install — HTTPS comes from the
same image too.
One command
Run the command below. You do not need to clone a repository, create a directory, or prepare a separate configuration file.
docker run -d --name yeke --restart unless-stopped -p 8080:8080 \ -v yeke-data:/var/lib/yeke \ nairotech/yeke:0.61.6
The data lives in a Docker volume named yeke-data. Docker creates it on first
run and takes its ownership from the image: it comes out owned by the user core runs as
(65532) and writable, with nothing for you to prepare. docker rm
doesn't touch the volume; upgrades and backups are below.
Pin the version tag — don't write latest: core's version also determines
the tag of the agent image that gets installed in your cluster. If the tag drifts, the agent
ends up with a protocol mismatch. Released tags are on
Docker Hub.
If you'd rather mount your own directory
To keep the data in a visible directory on the host, mount a directory instead of the
volume. You create the directory yourself, and it has to belong to the user core runs as (65532); YEKE does not run as root and cannot write into a
root-owned directory:
sudo install -d -m 700 -o 65532 -g 65532 /opt/yeke/data docker run -d --name yeke --restart unless-stopped -p 8080:8080 \ -v /opt/yeke/data:/var/lib/yeke \ nairotech/yeke:0.61.6
Skipping the first line doesn't break the install quietly: core stops at
startup and writes to its log which directory needs to belong to whom, along with the
command to paste (docker logs yeke).
If you go this route, only the -v line changes in the commands elsewhere on
this page: your directory in place of yeke-data.
Create the first administrator
On first boot there are no users at all; core generates a setup token and prints it to the log:
docker logs yeke | grep -i SETUP:
Open http://localhost:8080/ (with an HTTPS install, the address is
https://<your-domain>/), paste the token, and pick a username and password.
The token is valid for 60 minutes and can be used once; if it expires,
docker restart yeke issues a new one.
The one thing to back up: the yeke-data volume
Everything the installation holds lives in that volume, in two files:
core.db (cluster inventory, users, operation history, audit trail) and
snapshot.key — the 32-byte encryption key core generates on first start.
Direct-mode kubeconfigs, your AI provider keys, your hook secrets and rollback snapshots sit
in the database encrypted with that key.
Back the two up together. A database backup without the key opens, but cannot read a
single encrypted record — and the symptom is quiet: core does not crash,
/healthz still returns 200, it just cannot reach your clusters. Measured on
2026-08-10.
If you would rather keep the key outside the data directory — so that a backup of
the whole volume doesn't carry the key with it — supply
YEKE_SNAPSHOT_KEY: see the next section.
Installing with HTTPS
The container can terminate HTTPS itself with your own certificate; you don't need a separate reverse proxy in front of it.
- A DNS record — one that points your address at the machine YEKE runs on.
- A certificate and key file — the server certificate your organization issued (plus any intermediates, full chain) and an unencrypted PEM key. YEKE does not obtain or renew the certificate; your organization provides it.
Put the certificate files in a directory and give it to the user core runs as (65532):
sudo install -d -m 755 /etc/yeke/certs sudo cp tls.crt tls.key /etc/yeke/certs/ sudo chown -R 65532:65532 /etc/yeke/certs sudo chmod 600 /etc/yeke/certs/tls.key
docker run -d --name yeke --restart unless-stopped -p 443:8443 \ -v yeke-data:/var/lib/yeke \ -v /etc/yeke/certs:/certs:ro \ -e YEKE_TLS_CERT_FILE=/certs/tls.crt \ -e YEKE_TLS_KEY_FILE=/certs/tls.key \ -e YEKE_PUBLIC_URL=https://yeke.example.com \ nairotech/yeke:0.61.6
With YEKE_TLS_CERT_FILE and YEKE_TLS_KEY_FILE set, YEKE listens for https on 8443 inside the container, published with -p 443:8443; leave both unset and the behavior is unchanged from today. Mount the certificate directory, not individual files: swap a file with mv and a single-file mount keeps serving the old one. If the directory or files aren't readable by 65532, core doesn't start; it logs which file, for which user, along with the fix (docker logs yeke). With TLS on, 8080 stays loopback-only inside the container — the healthcheck uses it — and isn't reachable even if published.
To rotate the certificate, replace the files: YEKE picks up the new one within 60 seconds, and open connections (the agent tunnel, an open terminal) don't drop. To switch over without waiting, run docker kill -s HUP yeke. If the new file is broken, YEKE keeps serving the old certificate and logs it.
https://<address>/healthz carries tls.notAfter and tls.daysLeft while TLS is on; once 30 days or fewer remain, a warning is logged once a day.
No HTTP/2. No 80→443 redirect — open the address with https://. YEKE does not obtain or renew the certificate (no ACME/Let's Encrypt automation); your organization provides it.
A certificate from a public CA needs no extra setup for the agent or Shell. With a certificate signed by your own internal CA, browsers trust it once the CA is distributed inside your organization; for how the agent inside the cluster, Shell and the CLI trust that CA without -k, see Internal CA.
If a reverse proxy or load balancer already terminates TLS in front of YEKE, you don't need this section: put YEKE behind that layer on 8080.
Every setting, out in the open
This example sets the public URL, license and an external key. Remove optional settings you do not need.
To keep the key outside, generate it once and write it to a file. These lines are optional: skip them and core generates the key inside the volume itself. If the file already exists this leaves it alone; re-running the line does not rotate your key:
sudo install -d -m 700 /etc/yeke [ -f /etc/yeke/snapshot.key ] || openssl rand -hex 32 | sudo tee /etc/yeke/snapshot.key > /dev/null sudo chmod 600 /etc/yeke/snapshot.key
docker run -d --name yeke --restart unless-stopped \ -p 443:8443 \ -v yeke-data:/var/lib/yeke \ -v /etc/yeke/certs:/certs:ro \ -v /etc/yeke/license.txt:/etc/yeke/license.txt:ro \ -e YEKE_SNAPSHOT_KEY="$(sudo cat /etc/yeke/snapshot.key)" \ -e YEKE_TLS_CERT_FILE="/certs/tls.crt" \ -e YEKE_TLS_KEY_FILE="/certs/tls.key" \ -e YEKE_PUBLIC_URL="https://yeke.example.com" \ -e YEKE_LICENSE_PATH="/etc/yeke/license.txt" \ nairotech/yeke:0.61.6
When YEKE_SNAPSHOT_KEY is set, the snapshot.key file
in the data directory is neither read nor written — the variable always wins. Don't move
an existing install onto this path: records encrypted with the key generated up to that point
will not open with a different one.
If a reverse proxy in front of YEKE provides HTTPS, remove the certificate mount
line and the two TLS lines, and write -p 8080:8080 instead of -p 443:8443.
The YEKE_PUBLIC_URL and YEKE_LICENSE_PATH lines are optional too. If you don't have a license, or
you'd rather upload it from the UI, remove the YEKE_LICENSE_PATH line and
its mount line together. Removing just one of them creates a quiet mess: Docker, trying
to mount a file that doesn't exist, creates an empty directory at that path instead; core
can't read a directory, rejects the license, and keeps running as Community — the install looks
fine, but it catches up with you later when you try to copy your actual license file to that
path.
| Variable | Required | What it does · what happens if left empty |
|---|---|---|
YEKE_SNAPSHOT_KEY | No | The 32-byte key (64 hex characters or base64) that encrypts stored credentials, AI
keys, hook secrets and rollback snapshots. If empty, core generates one and
writes it to the data directory (snapshot.key, mode 600), then reads it
from there on every later start. Supplying it is how you keep the key
outside the data directory. |
YEKE_PUBLIC_URL | If you'll add a cluster | The absolute address at which core is reached from the outside; the agent
install manifest uses this address. Left empty, core boots fine and login works; only
adding a cluster in agent mode stops, with PUBLIC_URL_NOT_CONFIGURED. Core
does not guess this address: most installs listen on 0.0.0.0:8080, and
that address is not reachable from inside the cluster. |
YEKE_TLS_CERT_FILE | For HTTPS | Path to the server certificate (plus any intermediates, full chain). See Installing with HTTPS. Left empty, YEKE listens plain http; set one without the other and it won't start. |
YEKE_TLS_KEY_FILE | For HTTPS | Path to the unencrypted PEM key file. Left empty, YEKE listens plain http; set one without the other and it won't start. |
YEKE_ALLOWED_ORIGINS | No | Extra origins accepted by the CSRF/Origin check (comma-separated). Needed only when
you put a reverse proxy (Cloudflare, nginx, Traefik) in front of core and that
layer presents an external address different from the Host core sees; not
needed with native TLS, since Host is already the external address. |
YEKE_LICENSE_PATH | No | Path to the Enterprise license file. Left empty, the install runs as Community: a cap of 3 clusters / 5 users, every core capability unlocked, never expires. Set, it becomes that boot's only license source and the license can no longer be uploaded from the UI. |
YEKE_BOOTSTRAP_TOKEN | No | A fixed setup token for the first administrator: saves you from reading the log if you're scripting the install. Left empty, core generates one at random and logs it. |
You don't need to define any YEKE_* variable that isn't in this
table. Three of them — YEKE_ALLOW_HEADER_IDENTITY, YEKE_DEV_USER,
YEKE_DEV_GROUPS — were removed, and if defined core doesn't start at all:
authentication exists precisely because those three were shut off, and silently ignoring them
would let you believe it "still works the old way."
Governance settings (Enterprise only)
Enterprise governance uses four additional settings. YEKE_POLICY_MODE selects file (default) or central. Central mode requires policy-central; an unknown mode prevents startup.
Central mode also requires YEKE_POLICY_PACKAGE_PUBLIC_KEYS: the Ed25519 verification keys for policy packages, separate from license and air-gap keys.
YEKE_EXEC_RECORDING_RETENTION_DAYS controls full terminal recording retention (default: 30 days), independently of operation retention.
YEKE_SECRET_DISPLAY controls how visible Secret values are through YEKE: besides the default full, the restricted masked/hidden modes require the secret-display flag and apply to everyone, owner included. See Governance for details, in particular the Secret display section.
What comes next
YEKE is running, but it cannot see a single cluster yet.
- Connect a cluster — agent mode or direct kubeconfig.
- Set up permissions — which Kubernetes identity each person writes under.
Upgrades and backups
An upgrade recreates the container; data and key stay in the volume, so nothing else moves.
docker pull nairotech/yeke:<new-version> docker rm -f yeke # then the same run command, with only the tag changed
- An upgrade is an outage, every single one. The database is single-writer SQLite: the new core can't start until the old one has stopped. The measured window in production is about 60 seconds. Even a release that changes one line of UI text is subject to this outage; the UI ships inside core's image.
- Migrations run at startup and core prints a
schema vNline to its log; confirm success from that line, not by guessing. - Backup — the contents of the volume, plus
/etc/yekeif you moved the key outside. Take the backup with core stopped: SQLite's-walfile is written while it runs, and a half-copied backup is quietly incomplete.
docker stop yeke docker run --rm -v yeke-data:/v -v "$PWD":/yedek alpine:3.20 tar czf /yedek/yeke-data.tgz -C /v . docker start yeke
The archive carries the file ownership (65532) with it; restoring is just the
same command in reverse, and core boots straight off the restored volume. The target has to
be an empty volume — on a new machine, Docker creates it fresh when you run this command. If
you mounted your own directory instead, skip both of these: copying the directory is
enough.
docker run --rm -v yeke-data:/v -v "$PWD":/yedek alpine:3.20 tar xzf /yedek/yeke-data.tgz -C /v
If you forgot your password
There is no in-UI recovery when the one administrator forgets their password, or their account gets locked, and nobody else can sign in. Anyone with exec access to the container can issue a new password setup token from the command line.
An operator with container access runs whichever of these three matches their setup. Core does not need to be stopped.
docker compose exec core node tools/yeke-reset-password.mjs admin
docker exec yeke node tools/yeke-reset-password.mjs admin
kubectl -n yeke-system exec deploy/yeke-core -- node tools/yeke-reset-password.mjs admin
The tool does not pick a password: it prints a one-time password setup token, valid for
60 minutes, to the terminal. The user pastes that token into /set-password
and chooses their own password. Generating a new token invalidates the previous one.
If the authenticator app is gone too, add --reset-mfa; it clears the MFA
enrolment and recovery codes along with the token, and the user can set MFA up again after
signing in.
This only works for local, active accounts. An LDAP/OIDC account's password is reset in the directory, not with this tool; a disabled account has to be re-enabled by an administrator first. The operation is written to the audit log.
Anyone who can run this already has exec access to the container's data directory and database — this does not open a new privilege. No password ever reaches the terminal; only the token does. The API rule is unchanged: the setup owner's password still cannot be reset by another user from the UI.
Compose and Kubernetes
Use Docker Compose or Kubernetes to keep the service definition in a file. Both run the same YEKE image.
Docker Compose
No need to clone a repo; this is the whole file:
services:
core:
image: nairotech/yeke:0.61.6
ports:
- "8080:8080"
environment:
YEKE_PUBLIC_URL: ${YEKE_PUBLIC_URL:-}
volumes:
- yeke-data:/var/lib/yeke
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8080/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 3s
retries: 5
start_period: 10s
restart: unless-stopped
volumes:
yeke-data:
name: yeke-dataRun docker compose up -d. The name line keeps the volume from
getting a project prefix added to its name; the backup commands above apply here too,
unchanged. On this path docker ps does show (healthy); the healthcheck
is part of this file, not the image.
For HTTPS, change ports to "443:8443" and add the certificate
volume and the two TLS environment variables from Installing with
HTTPS; the healthcheck stays as is.
Kubernetes
Running core inside a cluster is supported and a manifest set is ready: namespace,
ReadWriteOnce PVC, a single Deployment (strategy: Recreate), Service,
and a standard Ingress. There we do recommend putting the key in a Secret — the opposite
of the single-machine advice: a backup that snapshots the PVC would otherwise carry the key
with it and make the encryption pointless. The set is short but has a few fields you'll need to adapt to your setup (hostname,
StorageClass, Ingress class);
ask and we'll send
them.
You don't have to install inside the cluster it manages, and usually you don't want to: when the cluster YEKE manages goes down, the UI that would fix it needs to still be up.
Stuck on a step? Write to us.
Every command on this page was actually run while it was written. If a step still does not go as expected, reach out directly.