Run in containers
Each release of ctrl, the E2B gateway, and the Exposure gateway also ships container images that you can run with Kubernetes or Docker. You still install node on hosts. See Install node. Use these images if you operate a container platform.
This page covers only what differs from host installation. For the meaning of configuration items, see ctrl configuration, Install ctrl, and the variable tables in Install the E2B gateway and Install the Exposure gateway. For upgrade criteria, see Reinstall and upgrade. The example manifests are in deploy/kubernetes/ and deploy/compose/.
Images
| Image | Default command | Runtime account (uid:gid) | Ports | Persistent volume mount point |
|---|---|---|---|---|
ghcr.io/felinics/kibotos-ctrl | kibotos-ctrl serve | 10001:10001 | 18092 (API), 18101 (node API), 18192 (metrics) | /var/lib/kibotos, with the state directory at ctrl/ inside it |
ghcr.io/felinics/kibotos-e2b | kibotos-e2b | 10002:10002 | 18095 (API), 18195 (metrics) | /var/lib/kibotos, with the identity directory at e2b/identity by default |
ghcr.io/felinics/kibotos-gw | kibotos-gw | 10003:10003 | 18097 (tunnel entry), 18197 (metrics) | /var/lib/kibotos, with the identity directory at gw/identity by default |
- Metrics ports open only when a metrics listener is configured. See Listener in Cluster and service metrics.
- The E2B gateway also opens an envd-only listener on
127.0.0.1:18096inside the container. The examples do not publish it: port 18095 already serves envd requests. - Tags match the release version, for example
v1.2.3andv1.2.3-rc.1. There is no moving tag such aslatest. - The binaries in the images come from the deb packages of the same version and are byte-for-byte identical.
- Each release has an asset
kibotos-images_<version>.txtthat lists the three images by digest and is covered bySHA256SUMS. Reference images by digest when deploying, so they can be checked against the downloaded release set.
Use the directory from downloading the release set to see the images for this version, and check the version the image reports:
cat kibotos-releases/v1.2.3/kibotos-images_1.2.3.txt
docker run --rm ghcr.io/felinics/kibotos-ctrl:v1.2.3 version --jsonThe source repository is not public yet, so pulling images also requires read access to the repository: log in to ghcr.io with a GitHub token that has the read:packages permission, and in Kubernetes, turn it into an imagePullSecret.
Differences from host installation
State on persistent volumes
- The PKI of ctrl and the identities of the gateways are on persistent volumes. The volume is mounted at
/var/lib/kibotos, and each service creates its own0700subdirectory in it. - Losing ctrl's volume loses this deployment's identity: all nodes and Operators must enroll again. Back it up as you back up the database.
- Do not mount a Secret or ConfigMap directory as a state or identity directory. They consist of symbolic links and carry group permissions, and the services refuse to load them.
- In Kubernetes, set
fsGroupandfsGroupChangePolicy: OnRootMismatch.fsGrouplets the service create its own directory under the volume root. The policy's default,Always, makes kubelet add group read and write to the files in the volume on every mount, and ctrl then refuses to start the next time because the private key permissions are too broad. - A Docker named volume copies the image's directory on first mount, so ownership is already correct. When you use a host directory instead, first create the service's subdirectory with a command such as
install -d -o 10001 -g 10001 -m 0700.
Configuration
- The configuration of ctrl can be mounted as a whole directory. Kubernetes mounts a Secret or ConfigMap at
/etc/kibotos/ctrl, and Docker bind-mounts a directory.config.toml,dsn_file, and the metrics signing private key file can be symbolic links, but they must ultimately point to regular files that the service account can read. - Gateway configuration comes only from environment variables, with the same variable names as the package's
config.env. For the full lists, seee2b.example.envandgw.example.env. Listen addresses default to loopback; in containers, change them to0.0.0.0. - When a required item is missing or a value is invalid, the gateway lists all problematic variables at once and exits, without printing their values.
- Metrics listeners are off by default. For ctrl, write
[telemetry]inconfig.toml. For the two gateways, setKIBOTOS_E2B_TELEMETRY_LISTENandKIBOTOS_GW_TELEMETRY_LISTEN, also bound to0.0.0.0in containers. The example manifests already turn on the listeners of both gateways. For how to collect them, see Kubernetes in Cluster and service metrics.
Only one ctrl
- Run only one ctrl at a time. In Kubernetes, use
replicas: 1andstrategy: Recreate. Do not use RollingUpdate, and do not raise the replica count. With Docker, start only one container. - A second ctrl connected to the same database only answers reads and never writes at the same time as the first. See Execution ownership. The two processes still must not share one
storage.root. - A ReadWriteOnce volume prevents a second Pod scheduled on another host from mounting it, but it does not replace Recreate.
- Do not run
initautomatically on every start, for example by putting it in an initContainer. When the volume is mounted wrongly or lost,serveshould fail to load, butinitwould generate a new deployment identity. - When the host running ctrl loses contact, the old Pod stays in
Terminating, the volume is not released, the new Pod cannot start, and no ctrl is available in the meantime. Force-delete the old Pod only after confirming that the old host has shut down.
Entry point and hostname
- Only L4 passthrough can sit in front of ctrl's two ports. Both use end-to-end mTLS. In Kubernetes, use a LoadBalancer or NodePort Service, not an Ingress.
[identity].hostresolves to this entry point. The node's[ctrl].endpoint,kibo, and the gateways all connect to ctrl with this name, because ctrl's certificate is signed only for this name.- Inside the cluster, gateways cannot connect to ctrl with the Service's in-cluster name. When cluster DNS cannot resolve
[identity].host, usehostAliasesin Kubernetes to point it to the ctrl Service's address. The Compose example gives ctrl a network alias with the same name.
- Inside the cluster, gateways cannot connect to ctrl with the Service's in-cluster name. When cluster DNS cannot resolve
- Set the artifact store
endpointto an address that both nodes and Operator workstations can reach directly. The presigned URLs that ctrl issues use the same endpoint; an in-cluster address makes node downloads fail. - Gateways must be able to resolve and reach the node data-plane hostnames, that is,
<node_id>.<host_suffix>. - Enable time synchronization on the host running ctrl. Containers use the host's clock. For the requirements, see Time synchronization.
Gateways: single instance, self-enrollment on first start
Both gateways are stateless, but each instance needs its own Operator identity. When several instances share one identity directory, each renews on its own. Because ctrl accepts only the current and previous certificates of an identity, a third instance is rejected. The example manifests provide one identity per gateway, so they run one instance of each. More instances each need their own identity, as on hosts. See Identity rules.
- The gateway's identity directory is on a persistent volume. When the volume is empty, the gateway enrolls with the one-time enrollment key that
KIBOTOS_E2B_ENROLLMENT_KEY_FILEorKIBOTOS_GW_ENROLLMENT_KEY_FILEpoints to, and only then starts serving. - A gateway that has already enrolled no longer reads this file, so a used key can stay in the Secret.
- A key is valid for 1 hour by default and at most 24 hours (
--ttl). Start the gateway within the validity period after issuing it. - Secret files mounted by Kubernetes have group read permission, so the gateway logs a "permissions too broad" warning when enrolling. You can ignore it.
- After a gateway's volume is lost, enrollment fails at startup and the gateway does not start serving. Issue a new key for the same principal, update the Secret, and restart the gateway.
- The Kubernetes examples use
strategy: Recreate: the new Pod starts only after the old Pod stops, so the same identity is never used by two processes at once.
Kubernetes
The example manifests assume the namespace kibotos and use the following files. Adjust the image versions, hostnames, domains, and storage sizes to your environment before applying them.
| File | Contents |
|---|---|
ctrl-state.yaml | ctrl's persistent volume |
ctrl-command.yaml | A Job that runs a one-off command on ctrl's volume; replace COMMAND with the command name |
ctrl.yaml | ctrl's Deployment, LoadBalancer Service, and NetworkPolicy |
e2b.yaml | The E2B gateway's persistent volume, single-replica Deployment, Service, NetworkPolicy, and Ingress |
gw.yaml | The Exposure gateway's persistent volume, single-replica Deployment, Service, and NetworkPolicy, plus cloudflared |
The NetworkPolicies of the three manifests allow only the metrics collector to connect to the telemetry port. The collector's manifest is deploy/metrics/kubernetes-vmagent.yaml. See Kubernetes in Cluster and service metrics.
The storage that vmagent writes to can be installed in the same cluster with the Helm chart deploy/helm/kibotos-metrics/. See VictoriaMetrics in the cluster.
Instance metrics storage can be installed in the same cluster with the Helm chart deploy/helm/kibotos-instance-metrics/. See Reference deployment on Kubernetes.
1. Prepare the namespace, pull credentials, and ctrl configuration
Fill in config.toml as in Install the package and write the configuration, keeping [storage].root at the default /var/lib/kibotos/ctrl. It contains database and storage credentials, so put it in a Secret. The whole Secret is mounted at /etc/kibotos/ctrl. Put the dsn_file, the metrics signing private key, and the CA files used by the configuration in the same Secret, with paths written as /etc/kibotos/ctrl/<file name>. To export ctrl's own metrics, add a [telemetry] section to the configuration with listen set to 0.0.0.0:18192. When enabling instance metrics, add one more --from-file=metrics-signing-key.pem=.... For how to generate it, see Write credentials:
set -eu
kubectl create namespace kibotos
kubectl -n kibotos create secret docker-registry ghcr \
--docker-server=ghcr.io --docker-username=deploy-bot --docker-password="$GHCR_TOKEN"
kubectl -n kibotos patch serviceaccount default -p '{"imagePullSecrets":[{"name":"ghcr"}]}'
kubectl -n kibotos create secret generic kibotos-ctrl-config --from-file=config.toml=./config.tomlPreparing the bucket and permissions is the same as for host installation. See Prepare the bucket and permissions.
2. Initialize ctrl
Create the volume first, then run init and migrate on it in order. After each Job completes, check its log, then delete it:
set -eu
kubectl apply -f deploy/kubernetes/ctrl-state.yaml
for command in init migrate; do
sed "s/COMMAND/$command/g" deploy/kubernetes/ctrl-command.yaml | kubectl apply -f -
kubectl -n kibotos wait --for=condition=complete --timeout=10m "job/kibotos-ctrl-$command"
kubectl -n kibotos logs "job/kibotos-ctrl-$command"
kubectl -n kibotos delete job "kibotos-ctrl-$command"
doneWhen already initialized, init prints unchanged and does not change any file.
3. Start ctrl and issue the first Operator enrollment key
set -eu
kubectl apply -f deploy/kubernetes/ctrl.yaml
kubectl -n kibotos rollout status deploy/kibotos-ctrl --timeout=5m
kubectl -n kibotos get service kibotos-ctrlPoint the DNS record of [identity].host to the Service's external address. Then issue the key inside the ctrl container, copy it to your machine, and delete the copy in the container:
set -eu
umask 077
kubectl -n kibotos exec deploy/kibotos-ctrl -- \
kibotos-ctrl enrollment create --operator operator-a --output /var/lib/kibotos/ctrl/operator-a.key
kubectl -n kibotos exec deploy/kibotos-ctrl -- cat /var/lib/kibotos/ctrl/operator-a.key > operator-a.key
kubectl -n kibotos exec deploy/kibotos-ctrl -- rm /var/lib/kibotos/ctrl/operator-a.keyThen use this key to enroll kibo as described in Identity and enrollment, and connect nodes as described in Install node.
4. Start the E2B gateway
First issue an enrollment key for the gateway. Non-sensitive variables go in the ConfigMap in e2b.yaml. The API keys, token keys, and namespace go in a Secret; e2b-secret.env contains only these three variables:
set -eu
umask 077
kibo enrollment create --operator kibotos-e2b --ttl 24h --output kibotos-e2b.key
kubectl -n kibotos create secret generic kibotos-e2b-enrollment --from-file=enrollment.key=kibotos-e2b.key
kubectl -n kibotos create secret generic kibotos-e2b --from-env-file=e2b-secret.env
kubectl -n kibotos create secret tls kibotos-e2b-tls --cert=sandbox.crt --key=sandbox.key
kubectl apply -f deploy/kubernetes/e2b.yaml
kubectl -n kibotos rollout status deploy/kibotos-e2b --timeout=10m
rm kibotos-e2b.keyThe Ingress has the same requirements as the reverse proxy on a host. See Configure the reverse proxy: the certificate covers api.<domain> and *.<domain>, the original Host is preserved, WebSocket is allowed, and request and response buffering are off. With ingress-nginx, add the two annotations nginx.ingress.kubernetes.io/proxy-buffering: "off" and nginx.ingress.kubernetes.io/proxy-request-buffering: "off".
5. Start the Exposure gateway
Fill in [exposure] zone in ctrl's config.toml first. The tunnel credentials file is created by the provider; put it in a Secret:
set -eu
umask 077
kibo enrollment create --operator kibotos-gw --ttl 24h --output kibotos-gw.key
kubectl -n kibotos create secret generic kibotos-gw-enrollment --from-file=enrollment.key=kibotos-gw.key
kubectl -n kibotos create secret generic kibotos-cloudflared --from-file=credentials.json=./tunnel-credentials.json
kubectl apply -f deploy/kubernetes/gw.yaml
kubectl -n kibotos rollout status deploy/kibotos-gw --timeout=10m
rm kibotos-gw.key- The cloudflared replicas all run the same named tunnel and send the whole zone to the gateway's Service. For the tunnel configuration, see Configure the tunnel.
- Traffic between the gateway and the tunnel is plain HTTP. The example NetworkPolicy allows only cloudflared Pods to connect to the gateway's service port and only the metrics collector to connect to the metrics port. The cluster must have NetworkPolicy enabled.
Docker Compose
compose.yaml runs ctrl, the E2B gateway, the Exposure gateway, and cloudflared on one Docker host. It reads the following files from the same directory:
| File | Contents | Owner |
|---|---|---|
ctrl/config.toml | The ctrl configuration. The whole directory is mounted at /etc/kibotos/ctrl; other files used by the configuration go here too | Directory and files are 10001:10001, directory 0700, files 0600 |
e2b/e2b.env | The E2B gateway's environment variables, copied from e2b.example.env and filled in | Readable only by Docker, 0600 |
e2b/enrollment.key | The E2B gateway's enrollment key | 10002:10002, 0600 |
gw/gw.env | The Exposure gateway's environment variables, copied from gw.example.env and filled in | Readable only by Docker, 0600 |
gw/enrollment.key | The Exposure gateway's enrollment key | 10003:10003, 0600 |
gw/cloudflared.yaml, gw/cloudflared-credentials.json | Tunnel configuration and credentials | 65532:65532, 0600 |
- In both environment files, set
CTRL_ENDPOINTtohttps://<[identity].host>:18092: on the Compose network, ctrl carries an alias with this name. The example usesctrl.kibotos.example; change it to the actual name. - The ingress in
gw/cloudflared.yamlpoints tohttp://gw:18097; the rest is the same as in Configure the tunnel. Setcredentials-fileto/etc/cloudflared/credentials.json. - If you do not offer Exposure, remove the
gwandcloudflaredservices. - The metrics listeners of both gateways are already on and reachable only on the Compose network. For ctrl, write
[telemetry]inctrl/config.toml. For how to collect them, see Docker Compose in Cluster and service metrics.
Taking ctrl's configuration as an example, hand the directory and file over to the service account:
set -eu
sudo install -d -o 10001 -g 10001 -m 0700 deploy/compose/ctrl
sudo install -o 10001 -g 10001 -m 0600 config.toml deploy/compose/ctrl/config.tomlInitialize ctrl, start it, and issue the first Operator enrollment key:
set -eu
cd deploy/compose
docker compose run --rm ctrl init
docker compose run --rm ctrl migrate
docker compose up -d ctrl
docker compose exec ctrl kibotos-ctrl enrollment create --operator operator-a --output /var/lib/kibotos/ctrl/operator-a.key
(umask 077; docker compose exec ctrl cat /var/lib/kibotos/ctrl/operator-a.key > operator-a.key)
docker compose exec ctrl rm /var/lib/kibotos/ctrl/operator-a.keyAfter enrolling kibo with this key, issue keys for the gateways, put them in the locations in the table above, and start the remaining services:
set -eu
cd deploy/compose
docker compose up -d
docker compose ps
docker compose logs e2b gwThe E2B gateway is published on 127.0.0.1:18095, and the HTTPS reverse proxy on the host forwards to it.
One-off commands
The kibotos-ctrl commands that host installation runs as the service account run as follows in container deployments:
| Command | When | Kubernetes | Docker Compose |
|---|---|---|---|
init | First deployment, run only once | The Job in ctrl-command.yaml | docker compose run --rm ctrl init |
migrate | After ctrl stops, with the target version's image | Same as above | docker compose run --rm ctrl migrate |
check-upgrade | The preflight check before shutdown, and the formal check after stopping | Same as above, with the target version's image | docker compose run --rm ctrl check-upgrade |
enrollment create | While ctrl is running | kubectl exec into the ctrl Pod | docker compose exec ctrl kibotos-ctrl enrollment create |
catalog apply | While ctrl is running | Same as above | Same as above |
- One-off commands all run as the service account in the image. Do not add
--user 0or switch to root: the service account cannot read files created by root. - A
check-upgradeJob run while ctrl is running must land on the same host as ctrl to mount the volume, soctrl-command.yamlsets Pod affinity. When it is scheduled elsewhere, the Job stays inContainerCreating.
Upgrades
The criteria and steps are the same as for host deployments. See Rolling upgrades within a MINOR and Cross-version upgrades.
Upgrading ctrl
The only way to replace ctrl is to stop it, then start it. During the replacement, new Workspace operations and new data-plane sessions wait for it to come back; established data-plane connections are not affected. Taking Kubernetes as an example, upgrading v1.2.3 to v1.2.4:
set -eu
target=v1.2.4
run_ctrl_command() {
sed -e "s/COMMAND/$1/g" -e "s/kibotos-ctrl:v[^ ]*/kibotos-ctrl:$target/" deploy/kubernetes/ctrl-command.yaml | kubectl apply -f -
kubectl -n kibotos wait --for=condition=complete --timeout=10m "job/kibotos-ctrl-$1" || true
kubectl -n kibotos logs "job/kibotos-ctrl-$1"
kubectl -n kibotos delete job "kibotos-ctrl-$1"
}
kubectl -n kibotos scale deploy/kibotos-ctrl --replicas=0
kubectl -n kibotos wait --for=delete pod -l app.kubernetes.io/name=kibotos-ctrl --timeout=2m
run_ctrl_command check-upgradeWhen the report has no blockers, run the migration as the report says, then switch the image and start:
set -eu
target=v1.2.4
sed -e "s/COMMAND/migrate/g" -e "s/kibotos-ctrl:v[^ ]*/kibotos-ctrl:$target/" deploy/kubernetes/ctrl-command.yaml | kubectl apply -f -
kubectl -n kibotos wait --for=condition=complete --timeout=10m job/kibotos-ctrl-migrate
kubectl -n kibotos delete job kibotos-ctrl-migrate
kubectl -n kibotos set image deploy/kibotos-ctrl ctrl="ghcr.io/felinics/kibotos-ctrl:$target"
kubectl -n kibotos scale deploy/kibotos-ctrl --replicas=1
kubectl -n kibotos rollout status deploy/kibotos-ctrl --timeout=5m- When the report has blockers, do not switch the image. Scale the replicas back to 1, and ctrl resumes service on the original version.
- Docker Compose follows the same order:
docker compose stop ctrl, rundocker compose run --rm ctrl check-upgradeandmigratewith the target version's image, change the version incompose.yaml, thendocker compose up -d ctrl. - Once the migration has run, older versions of ctrl reject the newer schema, and the only way out is to fix forward.
Upgrading the gateways
Within the same release line, the gateways can be replaced at any time after ctrl is upgraded:
- Kubernetes:
kubectl -n kibotos set image deploy/kibotos-e2b e2b=ghcr.io/felinics/kibotos-e2b:v1.2.4. - Docker Compose: change the version in
compose.yaml, thendocker compose up -d e2b.
Each gateway has only one instance, so its entry point is briefly unavailable during the replacement. When the gateway receives the stop signal, it immediately stops accepting new connections, and in-flight requests have 5 seconds to finish. Long-lived connections still open after that (command output streams, terminals, Exposure WebSockets) are closed; clients reconnect.
In a cross-version upgrade, the gateways are replaced as part of the shutdown procedure: upgrade ctrl first, then the gateways.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
ctrl log load managed PKI state: ... must not grant group or other access | The kubelet added group permissions to the files in the volume: fsGroupChangePolicy is not OnRootMismatch | Change it to OnRootMismatch, run chmod -R go= on /var/lib/kibotos/ctrl from a one-off Pod running as 10001, then restart ctrl |
ctrl log config: open /etc/kibotos/ctrl/config.toml: permission denied | The configuration file is not readable by the service account | In Kubernetes, set fsGroup and give the Secret a defaultMode with group read permission. With Docker, hand the file over to 10001 |
Gateway log holds no enrolled identity and no enrollment key file is configured | The volume is new and no key file is configured | Issue a key and configure KIBOTOS_E2B_ENROLLMENT_KEY_FILE or KIBOTOS_GW_ENROLLMENT_KEY_FILE |
Gateway log enroll: followed by permission_denied, failed_precondition, or already_exists | The key has been used, has expired, or has been revoked | Issue a new key for this gateway's principal, update the Secret, and restart the gateway |
| The gateway cannot connect to ctrl, or reports a certificate mismatch | It uses the ctrl Service's in-cluster name, or [identity].host does not resolve inside the Pod | Use [identity].host, and add hostAliases if needed |
Pod stuck in ErrImagePull with 401 or 403 | Image pull credentials are missing or lack read access | Configure an imagePullSecret as described in Images |
Install the E2B gateway
This page shows how to install the E2B gateway so that programs built on the E2B SDK can connect to Kibotos.
Reinstall and upgrade
This page shows how to reinstall a package of the same version and upgrade to a new version without stopping Workspaces. For additional upgrade steps of each version, see Upgrade from v0.1.x to v0.2.0 and Upgrade from v0.2.x to v0.3.0. For uninstalling, see Uninstall. There are two kinds of upgrades. Within a release line (between the final versions and -rc.N versions of the same MAJOR.MINOR), you replace hosts one at a time, and ctrl and nodes do not need to stop together. Upgrades across a MINOR, and those involving -alpha.N or -beta.N, replace the whole deployment during a shutdown. For a first installation, see Installation overview. For details of the node and VMM process model and the maintenance check commands, see node and VMM processes. When ctrl and the gateways run in containers, the criteria and steps are the same as on this page. For how to carry them out, see Run in containers.