The Kubernetes runtime extension allows Universe to spawn instances as Kubernetes Pods instead of local processes or Docker containers. It supports both local development clusters (minikube, Docker Desktop, kind) and full cloud providers (EKS, GKE, AKS), with automatic URL rewriting so a containerized Universe can reach a local cluster’s API server without manual kubeconfig edits. Each instance maps to one Pod. The extension manages the full Pod lifecycle — creation, readiness polling, stdin command dispatch viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/universeclouddev/Universe/llms.txt
Use this file to discover all available pages before exploring further.
kubectl exec, log streaming, and deletion on stop. Optionally, a per-instance Kubernetes Service is created alongside each Pod to provide stable in-cluster DNS resolution.
Two Operating Modes
The extension’s behaviour changes depending on whetherhostDataPath is set:
| Local Mode | Cloud Mode | |
|---|---|---|
| When to use | minikube, Docker Desktop, kind — cluster nodes share the host filesystem | EKS, GKE, AKS — cluster nodes are separate VMs |
hostDataPath | Required (e.g. /opt/universe/data) | Omit or set to null |
| Volume type | hostPath — Pod sees template files via host filesystem mount | emptyDir — Pod starts with an empty working directory |
| Template delivery | Universe copies template locally; Pod reads via hostPath | Use S3 init containers (recommended) or a shared PVC |
| S3 init containers | Not needed | Auto-generated when s3TemplateInit: true |
Setup
Place the extension JAR
runtime-k8s-<version>.jar into the ./extensions/ directory on the Universe data volume:Mount the kubeconfig file
kubectl does on your host machine.Add extra_hosts on Linux
host.docker.internal. On Linux Docker you must add it manually:127.0.0.1 or localhost API server URL in the kubeconfig to host.docker.internal, so the container can reach the host’s Kubernetes API server without any manual kubeconfig edits.Set the KUBECONFIG environment variable
Create the extension config file
./extensions/k8s/config.json. The minimal required fields are:hostDataPath:Full Configuration Schema
All fields in./extensions/k8s/config.json:
| Field | Type | Default | Description |
|---|---|---|---|
factoryName | string | "kube" | Runtime key used in instance configs ("runtime": "kube") |
namespace | string | "default" | Kubernetes namespace where Pods are created |
image | string | "azul-zulu:25-jdk-alpine" | Container image for instance Pods |
imagePullPolicy | string | "IfNotPresent" | K8s image pull policy (Always, IfNotPresent, Never) |
workingDir | string | "/app" | Working directory inside the Pod |
restartPolicy | string | "Never" | K8s Pod restart policy (Always, OnFailure, Never) |
serviceAccount | string | null | K8s ServiceAccount name assigned to each Pod |
nodeSelector | object | {} | Node selector labels (e.g. {"disktype": "ssd"}) |
tolerations | array | [] | Toleration rules for tainted nodes |
env | object | {} | Environment variables injected into every Pod |
labels | object | {} | Extra labels applied to Pod metadata |
annotations | object | {} | Extra annotations applied to Pod metadata |
volumes | array | [] | Additional K8s volumes (hostPath, emptyDir, ConfigMap, Secret, PVC) |
volumeMounts | array | [] | Additional volume mounts inside the container |
kubeConfigPath | string | null | Path inside the container to the kubeconfig file |
masterUrl | string | null | Override the Kubernetes API server URL (rarely needed) |
timeoutSeconds | int | 30 | Seconds to wait for a Pod to reach Running state |
hostDataPath | string | null | Host path mapped to the Universe data directory. Set for local mode; omit for cloud mode |
s3TemplateInit | boolean | true | Auto-generate S3 init containers in cloud mode |
s3InitImage | string | "amazon/aws-cli:latest" | Image for S3 init containers (must provide aws CLI and unzip) |
s3Bucket | string | null | Override S3 bucket. If null, reads from the S3 extension config |
s3Prefix | string | null | Override S3 key prefix. If null, uses the S3 extension’s configured prefix |
service | object | see below | Per-instance Kubernetes Service configuration |
volumes Array Items
hostPath, emptyDir, configMapName, secretName, or claimName should be set per volume item.
volumeMounts Array Items
tolerations Array Items
service Object
| Field | Default | Description |
|---|---|---|
enabled | true | Whether to create a Service alongside each Pod |
type | "ClusterIP" | Service type (ClusterIP, NodePort, LoadBalancer) |
clusterIP | "None" | "None" = headless (DNS only). null = auto-assign virtual IP |
labels | {} | Extra labels merged into Service metadata |
annotations | {} | Extra annotations (e.g. for external-dns or cloud LB config) |
ownerReference | true | Service is garbage-collected when its Pod is deleted |
cleanupOrphans | true | On startup, delete Services whose Pods no longer exist |
Per-Instance Services
By default, the K8s extension creates a headless Service (ClusterIPNone) alongside every Pod to provide stable in-cluster DNS resolution. This is how the Velocity proxy plugin connects to backend instances inside a cluster.
Headless Service (default)
Each instance gets a fully-qualified DNS name usable from any Pod in the cluster:Disabling Services
If you use Tailscale or host networking for connectivity, disable per-instance Services to reduce API server churn:NodePort for External Access
To expose instance ports on the cluster nodes’ public IPs:Cloud Mode with S3 Init Containers
In cloud mode (hostDataPath omitted or null), the cluster nodes are separate VMs. Universe cannot copy template files to them via hostPath. Instead, the extension automatically generates init containers that download templates from S3 before the main container starts.
How It Works
- The S3 storage extension stores template zips in an S3-compatible bucket.
- When an instance is created, the K8s extension reads the
templateInstallationConfigand finds any templates with"storage": "s3". - For each such template, an init container is generated. It downloads the zip from S3 and extracts it into the Pod’s working directory (
emptyDir). - The main application container starts only after all init containers complete successfully.
Setup
Configure the S3 storage extension at./extensions/s3/config.json:
endpoint field:
hostDataPath in the K8s config and do not configure s3Bucket or s3Prefix unless you need to override the S3 extension’s values. The extension reads them automatically.
The init containers use amazon/aws-cli:latest by default. To use a custom image (e.g. a private registry mirror or a smaller image with aws-cli + unzip):
Per-Instance Image Override
Override the container image for a specific instance by settingCUSTOM_IMAGE in environmentVariables. This takes precedence over the image in the extension config:
Cluster-Specific Notes
Docker Desktop Kubernetes
Docker Desktop Kubernetes
- Universe auto-rewrites
127.0.0.1:6443→host.docker.internal:6443when it detects it is running inside a container. host.docker.internalis a built-in DNS name on Docker Desktop — noextra_hostsentry is needed on Mac or Windows.- On Linux Docker, you must add
extra_hosts: ["host.docker.internal:host-gateway"]to the compose service.
minikube
minikube
127.0.0.1 → host.docker.internal rewrite is automatic.Mac/Windows (VM driver): minikube runs in a VM. The API server is on the VM network, not the host loopback. Switch to the Docker driver to avoid networking issues:minikube in the kubeconfig, add it to extra_hosts alongside the host-gateway entry:minikube ip to get the IP address.Remote Clusters (EKS, GKE, AKS)
Remote Clusters (EKS, GKE, AKS)
~/.kube/config is already a publicly routable hostname. No URL rewriting or extra_hosts configuration is needed.Requirements:- The Universe container must have outbound internet access to reach the cloud API endpoint.
- Mount
~/.kube/configand setKUBECONFIGas shown in the setup steps. - Use cloud mode (omit
hostDataPath) and configure S3 init containers for template delivery. - Ensure RBAC permissions allow Universe to create, list, and delete Pods and Services in the target namespace.
kind and k3d
kind and k3d
kind and k3d run Kubernetes inside Docker containers, which places the API server on a private Docker bridge network — not reachable via host.docker.internal.Option 1 — Linux only: Use network_mode: host in the compose service so the Universe container shares the host network stack and can reach 127.0.0.1:6443 directly:extra_hosts entry:~/.kube/config for the kind/k3d cluster context.Troubleshooting
UnknownHostException: kubernetes.default.svc
UnknownHostException: kubernetes.default.svc
KUBECONFIG is set to the mounted kubeconfig path and the file is readable inside the container.UnknownHostException: host.docker.internal
UnknownHostException: host.docker.internal
extra_hosts configuration. Add the following to your compose service and recreate the container:Connection refused to host.docker.internal:6443
Connection refused to host.docker.internal:6443
0.0.0.0:6443 on the host. For minikube, run minikube tunnel or restart with --driver=docker. For kind/k3d, see the cluster-specific notes above.Certificate errors after URL rewrite
Certificate errors after URL rewrite
127.0.0.1, not host.docker.internal. Two options:- Set
masterUrlin the K8s extension config to skip auto-detection: - Regenerate the cluster certificate to include
host.docker.internalas a Subject Alternative Name.
Pods immediately exit — Phase: Failed
Pods immediately exit — Phase: Failed
hostDataPath in ./extensions/k8s/config.json points to the correct host-side path for the Universe data directory. If the path is wrong, the hostPath volume mount will be empty and the instance command will fail to find its files.Cloud mode (S3 init containers): Check the init container logs:./extensions/s3/config.json, wrong bucket or region, template zip not uploaded.Cloud mode (emptyDir): The pod’s working directory is empty. Enable s3TemplateInit, mount a shared PVC, or bake files into the container image.Init containers fail — Unable to locate credentials
Init containers fail — Unable to locate credentials
./extensions/s3/config.json) is missing, unreadable, or does not contain accessKey and secretKey. Verify the file exists at the correct path inside the Universe container (/data/extensions/s3/config.json if your data volume is mounted at /data).Init containers fail — NoSuchKey or 404
Init containers fail — NoSuchKey or 404
templates/, a template named lobby in group minecraft must be at:s3 upload command or place the zip manually.Pods stuck in Pending or ImagePullBackOff
Pods stuck in Pending or ImagePullBackOff
- Verify the
imagevalue in the K8s config exists in a registry the cluster nodes can reach. - For private registries, configure
imagePullSecretson the namespace. - For the S3 init image (
amazon/aws-cli:latest), ensure cluster nodes have outbound internet access or configures3InitImageto point to a private mirror.
Harmless Netty warning about ByteBuffer.cleaner()
Harmless Netty warning about ByteBuffer.cleaner()
java.nio.ByteBuffer.cleaner(): unavailable or sun.misc.Unsafe unavailable is a non-fatal Netty initialization warning that appears when running inside Docker. Netty catches the exception internally and falls back to a different buffer allocator. To suppress it, rebuild your Universe Docker image with the latest JAR — the entrypoint now includes --add-exports=java.base/sun.misc=ALL-UNNAMED.~/.kube/config and your docker-compose.yml data directory. Never expose the Universe REST API publicly when it has access to a Kubernetes cluster.