Complete reference for the Private LLM Operator's custom resources and HTTP API.
The operator registers two CRDs in the llm.privatellms.msp API group.
Requests a private llama.cpp inference endpoint. The operator reconciles it into a Deployment, Service, Ingress, and Traefik middlewares.
API Version: llm.privatellms.msp/v1alpha1
| Field | Type | Default | Description |
|---|---|---|---|
spec.model |
string |
tinyllama |
Model identifier. One of: tinyllama, phi-2, gemma-3-1b-it, gemma-3-4b-it, gemma-3-12b-it, qwen3-4b |
spec.replicas |
int32 |
1 |
Number of llama.cpp server pods. Minimum: 0 (treated as 1) |
spec.dnsPolicy |
string |
ClusterFirst |
Pod DNS policy. Use Default only when the target cluster requires its node resolver for external model downloads. |
| Field | Type | Description |
|---|---|---|
status.phase |
string |
High-level lifecycle state: Provisioning or Ready |
status.endpoint |
string |
Public URL for the inference endpoint (e.g., https://host/llm/abc123) |
status.observedGeneration |
int64 |
Last generation processed by the controller |
status.conditions |
[]Condition |
Standard Kubernetes conditions (see below) |
| Type | Description |
|---|---|
Ready |
True when Deployment is available, endpoints are ready, and /health passes |
NAME MODEL PHASE ENDPOINT
my-llm tinyllama Ready https://host/llm/abc123
| Annotation | Set By | Description |
|---|---|---|
llm.privatellms.msp/slug |
Controller | Random URL slug for routing (auto-generated) |
apiVersion: llm.privatellms.msp/v1alpha1
kind: LLMInstance
metadata:
name: production-llm
namespace: my-team
spec:
model: gemma-3-4b-it
replicas: 3After reconciliation:
status:
phase: Ready
endpoint: https://llm.example.com/llm/aB3xK9mLp2Qz
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: Provisioned
message: LLM instance is readyMints a bearer token for an existing LLMInstance. The token is stored in a Kubernetes Secret.
API Version: llm.privatellms.msp/v1alpha1
| Field | Type | Required | Description |
|---|---|---|---|
spec.instanceName |
string |
Yes | Name of the target LLMInstance (must be in the same namespace) |
spec.description |
string |
No | Human-friendly description for this token |
| Field | Type | Description |
|---|---|---|
status.phase |
string |
Lifecycle state: Pending or Ready |
status.secretName |
string |
Name of the Secret containing the generated credentials |
status.observedGeneration |
int64 |
Last generation processed by the controller |
status.conditions |
[]Condition |
Standard Kubernetes conditions |
| Type | Reason | Description |
|---|---|---|
Ready |
Provisioned |
Token generated and Secret created |
Ready |
InstanceNotFound |
Referenced LLMInstance does not exist |
Ready |
InstanceNotReady |
LLMInstance exists but is not yet Ready |
NAME INSTANCE SECRET PHASE
my-token my-llm my-token-token Ready
The controller creates a Secret named <apitokenrequest-name>-token containing:
| Key | Description |
|---|---|
OPENAI_API_KEY |
Cryptographically random bearer token (32 bytes, base64url) |
OPENAI_API_URL |
Full endpoint URL of the LLMInstance (e.g., https://host/llm/slug) |
Secret labels:
| Label | Value |
|---|---|
app.kubernetes.io/name |
llm-token |
llm.privatellms.msp/instance |
LLMInstance name |
llm.privatellms.msp/apitokenrequest |
APITokenRequest name |
llm.privatellms.msp/slug |
Instance slug (for auth server lookup) |
apeirora.eu/llm-api-compatibility |
openai |
apiVersion: llm.privatellms.msp/v1alpha1
kind: APITokenRequest
metadata:
name: ci-token
namespace: my-team
spec:
instanceName: production-llm
description: "Token for CI pipeline integration tests"After reconciliation:
status:
phase: Ready
secretName: ci-token-token
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: Provisioned
message: Token generatedEach LLMInstance exposes the standard llama.cpp HTTP API through its endpoint. All requests require the bearer token from an APITokenRequest.
All requests must include:
Authorization: Bearer <OPENAI_API_KEY>
Traefik's ForwardAuth middleware validates the token before the request reaches the llama.cpp server.
GET <OPENAI_API_URL>/health
Returns 200 OK with status JSON when the model is loaded and ready.
POST <OPENAI_API_URL>/completion
Content-Type: application/json
{
"prompt": "Hello there!",
"stream": false
}
POST <OPENAI_API_URL>/v1/chat/completions
Content-Type: application/json
{
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is Kubernetes?"}
]
}
Tip: The OpenAI-compatible endpoint works with any OpenAI SDK client. Set the base URL to
<OPENAI_API_URL>/v1and the API key to<OPENAI_API_KEY>.
# Retrieve credentials
SECRET=$(kubectl get apitokenrequest my-token -o jsonpath='{.status.secretName}')
OPENAI_API_KEY=$(kubectl get secret "$SECRET" -o jsonpath='{.data.OPENAI_API_KEY}' | base64 -d)
OPENAI_API_URL=$(kubectl get secret "$SECRET" -o jsonpath='{.data.OPENAI_API_URL}' | base64 -d)
# Health check
curl -s "$OPENAI_API_URL/health"
# Chat completion
curl -sS "$OPENAI_API_URL/v1/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Hello!"}]
}'from openai import OpenAI
client = OpenAI(
api_key="<OPENAI_API_KEY>",
base_url="<OPENAI_API_URL>/v1",
)
response = client.chat.completions.create(
model="tinyllama",
messages=[{"role": "user", "content": "Explain Kubernetes in one sentence."}],
)
print(response.choices[0].message.content)docker run --rm -p 3000:3000 \
-e OPENAI_API_KEY="$OPENAI_API_KEY" \
-e BASE_URL="$OPENAI_API_URL" \
-e HIDE_USER_API_KEY=1 \
-e DISABLE_FAST_LINK=1 \
-e DEFAULT_MODEL='/models/tinyllama.gguf' \
-e CUSTOM_MODELS='-all,+/models/tinyllama.gguf' \
yidadaa/chatgpt-next-web:latestOpen http://localhost:3000 to chat with your private LLM.
The operator requires the following cluster-level permissions:
| API Group | Resources | Verbs |
|---|---|---|
llm.privatellms.msp |
llminstances, llminstances/status, llminstances/finalizers |
get, list, watch, create, update, patch, delete |
llm.privatellms.msp |
apitokenrequests, apitokenrequests/status, apitokenrequests/finalizers |
get, list, watch, create, update, patch, delete |
apps |
deployments |
get, list, watch, create, update, patch, delete |
| (core) | services, secrets, configmaps, endpoints, events |
get, list, watch, create, update, patch, delete |
networking.k8s.io |
ingresses |
get, list, watch, create, update, patch, delete |
traefik.io |
middlewares |
get, list, watch, create, update, patch, delete |
coordination.k8s.io |
leases |
get, list, watch, create, update, patch, delete |