GreenThreadDocs

LiquidCompute is the self-hosted "Render.com for GPUs" UI and control plane that sits on top of the GreenThread engine. It adds a Service abstraction, Projects, custom domains, a unified /v1/* OpenAI-aggregator endpoint, and a live Fleet view across GPUs.

It is optional — the engine alone is a complete platform if all you need is kubectl + per-Model URLs. Install LiquidCompute when you want a UI, multi-tenant Projects, or one OpenAI endpoint that fronts every Model in the cluster.

What's in the box

WorkloadRole
lcdControl-plane Go service. Owns Projects, Services, custom-domain verification, fleet aggregation, the /api/* surface the SPA talks to.
lc-proxyOpenAI-compatible aggregator. Single /v1/chat/completions, /v1/embeddings, etc. — routes by request model to the right engine Model. Also hosts the async batch dispatcher.
frontendReact/Vite SPA. Projects board, Services UI, Fleet view, Metrics.
postgresBundled by default; flip postgres.bundled.enabled=false and supply a DSN to use external Postgres (CloudNativePG, RDS, etc.).

Key concepts

Project

A namespace-shaped grouping. Every Service belongs to one Project. The Project's slug becomes part of every emitted Kubernetes object's labels (liquidcompute.io/project=<slug>).

Service

A unified abstraction over both inference Models (tier=inference) and generic CPU / GPU container workloads (tier=cpu, tier=gpu). The Service spec is small and constrained — lcd compiles it into the right Kubernetes objects (Model CR, Deployment + Service, Ingress, Certificate).

Fleet

A live view that joins the engine's GPU CRDs (per physical device, with mode + health + available VRAM) into a single dashboard. No new state — just an aggregation.

Custom domain

Bring your own app.example.com. lcd issues a TXT challenge, you publish it, lcd verifies, and the next Service apply threads the hostname into its Ingress + cert-manager Certificate.

Install

LiquidCompute assumes the engine, an ingress controller, and cert-manager are already installed. See Prerequisites and Install GreenThread first.

Pull credentials

The chart and images live on licence.greenthread.ai (see Licensing › Pulling charts and images). If you already ran helm registry login for the engine install you can skip that step — credentials are stored per-workstation, not per-chart. You'll still need an image-pull Secret in the LC namespace:

kubectl create namespace liquidcompute-system
kubectl create secret docker-registry greenthread-registry \
  -n liquidcompute-system \
  --docker-server=licence.greenthread.ai \
  --docker-username=licence \
  --docker-password=<your-licence-token>

Helm install

helm upgrade --install liquidcompute \
  oci://licence.greenthread.ai/greenthread/charts/liquidcompute \
  --namespace liquidcompute-system \
  --set 'global.imagePullSecrets[0].name=greenthread-registry' \
  --set 'images.lcd.repository=licence.greenthread.ai/greenthread/liquidcompute/lcd' \
  --set 'images.lcProxy.repository=licence.greenthread.ai/greenthread/liquidcompute/lc-proxy' \
  --set 'images.frontend.repository=licence.greenthread.ai/greenthread/liquidcompute/frontend' \
  --set 'console.host=app.example.com' \
  --set 'global.platformDomain=example.com' \
  --set 'certManager.issuer=letsencrypt-production' \
  --set 'ingress.class=nginx' \
  --set 'ingress.platformService.namespace=ingress-nginx' \
  --set 'ingress.platformService.name=ingress-nginx-controller' \
  --set 'lcd.auth.enabled=true' \
  --set 'lcd.auth.username=admin' \
  --set 'lcd.auth.password=<generate-a-password>'
ValuePurpose
console.hostHostname the SPA + /api/* are served at.
global.platformDomainWildcard suffix for auto-generated Service subdomains (e.g. whisper-prod.example.com).
certManager.issuercert-manager (Cluster)Issuer used for every emitted Certificate.
ingress.classIngressClass lcd stamps on emitted Ingress objects (nginx, traefik, etc.).
ingress.platformService.{namespace,name}The shared cluster ingress Service — lcd surfaces this in the UI as the CNAME target for custom domains.
lcd.auth.username / passwordSingle shared admin login for v1 (multi-user / SSO are tracked separately).
postgres.bundled.enabledtrue (default) bundles Postgres in-chart. Set false + postgres.dsn=... to use external.
storage.defaultStorageClassStorageClass for per-Service /workspace PVCs. Empty → cluster default.

Verify

# All pods Running
kubectl get pods -n liquidcompute-system

# Ingress has an address
kubectl get ingress -n liquidcompute-system

# lcd is reachable
kubectl -n liquidcompute-system port-forward svc/liquidcompute-lcd 8080:8080 &
curl http://localhost:8080/api/v1/fleet | jq '.gpus | length'

A fresh install on a 4-GPU dev cluster:

$ kubectl get pods -n liquidcompute-system
NAME                                      READY   STATUS    AGE
liquidcompute-frontend-58fdcfdbcd-65qlv   1/1     Running   23h
liquidcompute-frontend-58fdcfdbcd-wfk5r   1/1     Running   23h
liquidcompute-lc-proxy-8574f5d469-k7h24   1/1     Running   23h
liquidcompute-lc-proxy-8574f5d469-spfmv   1/1     Running   23h
liquidcompute-lcd-565f79b8f7-rfwkt        1/1     Running   23h
liquidcompute-lcd-565f79b8f7-zqjmr        1/1     Running   23h
liquidcompute-postgres-6cd87d78f4-5zqdt   1/1     Running   2d5h

$ kubectl get ingress -n liquidcompute-system
NAME                    CLASS   HOSTS                  ADDRESS         PORTS     AGE
liquidcompute-console   nginx   app.example.com        203.0.113.10    80, 443   20d

Using the API

Once console.host resolves, open the URL in a browser and sign in with the admin credentials. Or drive it from the command line:

Create a Project

LCD=https://app.example.com/api
curl -X POST $LCD/v1/projects \
  -H 'Content-Type: application/json' \
  -d '{"name":"Default","slug":"default"}'

Deploy a Model via Service

A Service with tier=inference is compiled into a Model CR in the matching Project namespace:

curl -X POST $LCD/v1/projects/default/services \
  -H 'Content-Type: application/json' \
  -d '{
    "name":"llama-3-8b",
    "tier":"inference",
    "kind":{"model":{
      "modelName":"meta-llama/Llama-3.1-8B-Instruct",
      "dtype":"bfloat16",
      "endpoints":["chat_completions","completions"]
    }}
  }'

Deploy a CPU service

curl -X POST $LCD/v1/projects/default/services \
  -H 'Content-Type: application/json' \
  -d '{
    "name":"hello","tier":"cpu",
    "kind":{"container":{
      "image":"hashicorp/http-echo:latest",
      "command":["/http-echo","-text=hi","-listen=:8080"],
      "ports":[{"port":8080,"name":"http"}]
    }}
  }'

Call inference through lc-proxy

lc-proxy exposes the OpenAI aggregator at the same hostname as lcd, under /v1/*. The model field in the request body is the upstream model ID (the HuggingFace name on the Model spec):

# Discover what's addressable
curl https://app.example.com/v1/models | jq '.data[].id'

# Send a chat completion
curl https://app.example.com/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model":"meta-llama/Llama-3.1-8B-Instruct",
    "messages":[{"role":"user","content":"Hello!"}]
  }'
One endpoint, many models

Unlike the engine's per-Model URLs (/<model>/v1/*), lc-proxy exposes a single /v1/* surface and routes by the model field — exactly like OpenAI's API. Apps written against OpenAI need only a base_url change. The model value matches whatever Model.spec.modelName is set to (the upstream HuggingFace ID), so swapping providers is a one-line change.

Custom domains

# 1. Register the domain → server returns a TXT challenge.
curl -X POST $LCD/v1/domains \
  -H 'Content-Type: application/json' \
  -d '{"fqdn":"chat.acme.com","projectSlug":"default","serviceName":"llama-3-8b"}'
# → { "challengeRR": "_lc-challenge.chat.acme.com", "challengeToken": "abc..." }

# 2. Publish the TXT record at your DNS provider.

# 3. Verify.
curl -X POST $LCD/v1/domains/<id>/verify

After verification, the next Service apply rewrites its Ingress to include chat.acme.com and emits a cert-manager Certificate. The customer's CNAME for chat.acme.com should point at the platform's shared ingress Service (surfaced in the UI under Domains).

Where to next

  • GreenThread engine — what LiquidCompute is built on
  • AI Console — the layer above LiquidCompute that exposes per-customer API keys + a customer-facing UI
  • Licensing — token + Secret + offline JWT cache the engine enforces