4. Configure Gateway Routing¶
Illustrative mutation fragments
The management mutations on this page are non-standalone fragments. Before adapting them, follow this site's Example Quality Contract and complete Quickstart lifecycle. Do not treat HTTP success or management readback alone as runtime proof.
Converged guide overview · Previous: 3. Launch Engine Workers · Next: 5. Verify Requests and Routing
Before you start¶
Require direct inference from every selected worker and the saved RUN environment.
Focus of this page: Start the dedicated Gateway and configure one RR or strict Exact rule.
Topology and execution locations¶
flowchart TD
C["Client workload"] --> G["Dedicated Gateway<br/>selected VIP and PORT"]
G --> A["Worker 1<br/>WORKER1:BACKEND_PORT"]
G --> B["Worker 2<br/>WORKER2:BACKEND_PORT"]
G --> D["Worker 3<br/>WORKER3:BACKEND_PORT"]
O["Operator<br/>model and image selection"] -.->|"configuration and readback"| G
O -.->|"direct inference and engine receipts"| A
style G fill:#e1f5fe,stroke:#0288d1,color:#1f2937
style A fill:#e8f5e9,stroke:#43a047,color:#1f2937
style B fill:#e8f5e9,stroke:#43a047,color:#1f2937
style D fill:#e8f5e9,stroke:#43a047,color:#1f2937
This diagram shows the converged three-worker guide. Choose one engine and one model per run: workers 1/2/3 are replicas of that choice, not vLLM, SGLang and TensorRT-LLM mixed together. Select actual host addresses in the model-selection step; documentation addresses are examples. Each converged worker performs both prefill and decode.
| Where | What happens here |
|---|---|
| Operator terminal | Select the version tuple, private run directory, model snapshot and profiles. |
| Gateway host / namespace | Start the dedicated Gateway; apply and read back only the selected service. |
| Each worker host | Launch the chosen engine and prove direct inference before Gateway traffic. |
| Client | Send the same request through the Gateway and retain its HTTP/SSE observations. |
| Independent evidence | Correlate worker requests/receipts with the selected rule and request identifiers. |
Walkthrough¶
Prerequisites¶
On the controller, complete the manual's selection and working-directory setup
and keep its PACKAGE, MODEL, PROFILE, ENGINE, RUN, VIP, PORT and
CONTROL_API values in the current shell. Do not reset RUN after preparing
an Exact registry there. Replace the documentation VIP/worker addresses and
management URL in both variables and the selected JSON. Keep separate run
folders for separate services; retain the applied create body for rollback.
Start a dedicated lab Gateway¶
Gateway/controller shell only. This example starts an isolated proxy-only service on the host network, with loopback management and a local manual token. The VIP must already belong to this host and be reachable by the client. Use a new container name and unused 11111/service ports; do not recreate a shared Gateway.
Obtain an approved Gateway image implementing this guide's profile/status APIs
and the recorded engine contracts. No Gateway binary/image is supplied here.
Replace the image placeholder with its approved immutable digest; the
released v0.9.8.9-rc.1 does not implement strict profiles. Missing matching
artifacts are a blocking prerequisite, not permission to substitute latest.
export GATEWAY_IMAGE='registry.example.com/loxilb-igw@sha256:<approved-digest>'
[[ "$GATEWAY_IMAGE" =~ @sha256:[0-9a-f]{64}$ ]]
printf 'export GATEWAY_IMAGE=%q\n' "$GATEWAY_IMAGE" >> "$RUN/session.env"
docker pull "$GATEWAY_IMAGE"
docker image inspect "$GATEWAY_IMAGE" --format '{{.Id}} {{json .RepoDigests}}'
ss -lntp
ip -brief address
umask 077
install -d -m 700 "$RUN/gateway-config"
test ! -e "$RUN/manual-token"
openssl rand -hex 32 > "$RUN/manual-token"
printf 'Authorization: Bearer %s\n' "$(cat "$RUN/manual-token")" > "$CONTROL_HEADERS"
chmod 600 "$RUN/manual-token" "$CONTROL_HEADERS"
Create the token only for the first startup. On a resumed session reuse the same files; generating a different header token without updating Gateway breaks auth. The following two tabs share the same dedicated name. Run only one. They assume an image whose entrypoint is the Gateway binary, as in the source Dockerfile. The startup fields are source-checked command templates; this documentation review does not qualify a new image or host deployment.
docker run -d --name "$GATEWAY_NAME" --label igw.guide=model-engine-matrix \
--user 0:0 --network host \
-v "$RUN/gateway-config:/etc/loxilb" \
-v "$RUN/manual-token:/run/secrets/manual-token:ro" \
"$GATEWAY_IMAGE" --proxyonlymode --mgmt-profile=appliance-local \
--manualtoken --manualtokenvalue=/run/secrets/manual-token
export REGISTRY="$RUN/kvprofiles"
export TOKENIZERS="$RUN/tokenizers"
test -d "$REGISTRY"
test -d "$TOKENIZERS"
docker run -d --name "$GATEWAY_NAME" --label igw.guide=model-engine-matrix \
--user 0:0 --network host -e LLB_KV_NONE_HASH_SEED=0 \
-v "$RUN/gateway-config:/etc/loxilb" \
-v "$REGISTRY:/etc/loxilb/kvprofiles:ro" \
-v "$TOKENIZERS:/etc/loxilb/tokenizers:ro" \
-v "$RUN/manual-token:/run/secrets/manual-token:ro" \
"$GATEWAY_IMAGE" --proxyonlymode --mgmt-profile=appliance-local \
--manualtoken --manualtokenvalue=/run/secrets/manual-token
Record the selected image and startup invocation privately for rollback. Root ownership matches the Step 2 registry policy. This command does not exercise host eBPF or HA. For another deployment mode, follow the approved installation procedure instead of adding guessed flags. Remote management requires your site's authenticated TLS listener/proxy; do not use insecure TLS bypass options.
docker logs --tail 80 "$GATEWAY_NAME"
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/status/ready" > "$RUN/gateway-ready.json"
jq -e '.ready==true' "$RUN/gateway-ready.json"
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/status/capabilities" > "$RUN/gateway-capabilities.json"
Retry bounded readiness checks after inspecting startup logs if loading is still in progress. A 401 means the header and mounted token do not match; a missing route or incompatible contract blocks this setup. For Exact only, discover the selected profile and save its generation before applying a rule:
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/config/ai/model-profiles" > "$RUN/published-profiles.json"
jq -e --arg p "$PROFILE" --arg m "$MODEL" \
'[.profiles[] | select(.profileId==$p and .baseModel==$m and
(.supportedApis|index("chat"))!=null)] | length==1' "$RUN/published-profiles.json"
export PROFILE_GEN=$(jq -r --arg p "$PROFILE" \
'.profiles[]|select(.profileId==$p)|.gen' "$RUN/published-profiles.json")
For vLLM Exact additionally require the kv_exact_vllm capability's ready=true;
inspect its reason codes if the seed precondition failed. RR skips profile
discovery/attestation and does not require the evaluation registry.
Apply RR or Exact JSON¶
Copy the chosen tuple's file from matrix-configurations/; see the
Qwen/vLLM RR example.
The command below selects one of the 30 bodies using MODEL_KEY, ENGINE and
ROUTING_MODE. These are reproducible inputs, not a guarantee of success.
jq --arg v "$VIP" --argjson p "$PORT" \
--arg a "$WORKER1" --arg b "$WORKER2" --arg c "$WORKER3" \
--argjson backend "$BACKEND_PORT" \
'del(.serviceArguments.kvWarmupSec) |
.serviceArguments.externalIP=$v | .serviceArguments.host=$v |
.serviceArguments.port=$p | .endpoints[0].endpointIP=$a |
.endpoints[1].endpointIP=$b | .endpoints[2].endpointIP=$c |
.endpoints[].targetPort=$backend' \
"$PACKAGE/matrix-configurations/$MODEL_KEY-$ENGINE-$ROUTING_MODE.json" \
> "$RUN/model-engine-gateway.json"
jq . "$RUN/model-engine-gateway.json"
The command omits the legacy warmup field from the retained input snapshot, then
sets both VIP fields, all three workers and the allocated service
port from Step 1; review the resulting JSON before POST. Default
targetPort and probeport are 8000. QA also used a separate receipt collector;
its instrumented path does not establish direct-connection performance.
| Field | RR | Exact |
|---|---|---|
kvExactMode |
0 |
3 |
kvEngineType |
Selected engine | Same engine |
kvModelProfile |
Omitted | Exact ID of the selected evaluation profile |
kvExactApiMode |
Omitted | chat |
| Published profile, attestation and cache events | Not RR prerequisites | All must be verified |
Prepare management authentication in a mode-0600 header file using the prerequisites above. Check for rule conflicts before creation and readback. If the same identity already exists, follow the mode replacement procedure first.
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/config/loadbalancer/all" > "$RUN/before-create.json"
jq -e --arg m "$MODEL" --arg v "$VIP" --argjson p "$PORT" \
'[.lbAttr[]? | select(.serviceArguments.model_name==$m and
.serviceArguments.externalIP==$v and .serviceArguments.host==$v and
.serviceArguments.port==$p)] | length==0' "$RUN/before-create.json"
curl -fsS --max-time 30 --request POST --header @"$CONTROL_HEADERS" \
-H 'Content-Type: application/json' --data-binary @"$RUN/model-engine-gateway.json" \
"$CONTROL_API/config/loadbalancer" > "$RUN/create-response.json"
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/config/loadbalancer/all" > "$RUN/readback.json"
jq --arg m "$MODEL" --arg v "$VIP" --argjson p "$PORT" \
'.lbAttr[] | select(.serviceArguments.model_name==$m and
.serviceArguments.externalIP==$v and .serviceArguments.host==$v and
.serviceArguments.port==$p) | {serviceArguments,endpoints}' "$RUN/readback.json"
Inspect both the create response body and the selected identity's readback.
RR's default zero can be omitted in responses; interpret it as
(.kvExactMode // 0). Profile discovery or HTTP 200 alone cannot establish Exact.
Before continuing, require exactly one selected rule and compare the material fields against the submitted body. Readback may omit RR's default mode 0.
jq -e --slurpfile sent "$RUN/model-engine-gateway.json" \
--arg m "$MODEL" --arg v "$VIP" --argjson p "$PORT" \
'[.lbAttr[]? | select(.serviceArguments.model_name==$m and
.serviceArguments.externalIP==$v and .serviceArguments.host==$v and
.serviceArguments.port==$p)] as $rules |
($rules|length)==1 and
(($rules[0].serviceArguments.kvExactMode // 0)==($sent[0].serviceArguments.kvExactMode // 0)) and
$rules[0].serviceArguments.kvEngineType==$sent[0].serviceArguments.kvEngineType and
(($rules[0].serviceArguments.kvModelProfile // "")==($sent[0].serviceArguments.kvModelProfile // "")) and
(($rules[0].endpoints|map({endpointIP,targetPort,weight})|sort_by(.endpointIP))==
($sent[0].endpoints|map({endpointIP,targetPort,weight})|sort_by(.endpointIP)))' "$RUN/readback.json"
This command must print true; also inspect the full readback's monitoring,
streaming and event fields. Preserve any additional configuration difference.
Verify strict Exact binding¶
Exact only; skip this section for RR. The generation below is from discovery
above. If you reopened the shell, repeat that discovery to restore PROFILE_GEN.
export MODEL_URL=$(jq -rn --arg m "$MODEL" '$m|@uri')
curl -fsS --max-time 10 --header @"$CONTROL_HEADERS" \
"$CONTROL_API/config/loadbalancer/externalipaddress/$VIP/port/$PORT/protocol/tcp/kvexactstatus?model_name=$MODEL_URL" \
> "$RUN/exact-status.json"
jq -e --arg p "$PROFILE" --arg m "$MODEL" --arg engine "$ENGINE" \
--argjson generation "$PROFILE_GEN" \
'[.kvExactStatusAttr[] | select(.modelProfileId==$p and .modelName==$m and
.engineFamily==$engine and .modelProfileGen==$generation and .apiMode=="chat" and
.enforcedState=="READY" and (.reasonCodes|length)==0 and
.enforcement.goFenced==false)] | length==1' "$RUN/exact-status.json"
The check must print true and exit zero. Record the bindingDigest and the
full response. HTTP 200 or fallback serving in another state is not Exact proof;
Step 5 still requires events, cache-hit changes and independent backend receipts.
TRT can temporarily report drain_ownership_lost and PROFILE_VALIDATED during
subscriber startup. This source's default probe interval was 60 seconds;
Qwen/Llama reached READY after the next recheck. Preserve status responses and
timestamps, and recheck within a bounded window. For persistent errors, inspect
other /kv_cache_events consumers, connectivity, contracts and subscriber state.
Keep ownership checks enabled and classify ordinary serving separately from Exact.
Service fields to customize¶
| JSON field | Required example | Meaning / customization |
|---|---|---|
externalIP, host |
192.0.2.10 |
Replace both with the service VIP for this IP-hosted recipe; host is also part of deletion identity |
port, protocol |
28185, tcp |
Client-facing port/protocol |
mode, sel |
4, 0 |
HTTP fullproxy and RR base selection |
model_name |
card's MODEL |
Exact string served by all workers and used in requests |
sse_mode |
true |
Streaming-aware handling |
pd_disagg_mode |
false |
Converged pool; no P/D roles or transfer ports |
monitor, probetype |
true, tcp |
Port-level health; does not prove inference readiness |
probeport |
8000 |
Engine listener, aligned with all target ports |
probeTimeout, probeRetries |
2, 1 |
Probe timeout in seconds and probe failure criterion; not inference retries |
kvExactMode |
0 RR / 3 Exact |
Disable Exact or select converged Exact |
| Exact engine/event fields | vLLM: block 16 / ZMQ 5557; SGLang: page 16 / rank-0 ZMQ 5557; TRT: block 32 / HTTP 8000 | Use the selected matrix JSON and matching engine launch tab |
kvModelProfile, kvExactApiMode |
card's profile, chat |
Exact only; installed model/engine binding and allowed request surface |
endpointIP × 3 |
192.0.2.20–22 |
Replace with distinct reachable workers serving the same tuple |
targetPort, weight |
8000, 1 |
Engine API port and equal endpoint weights |
Keep omitted fields omitted unless the relevant configuration reference requires them. Hostname-based virtual services and heterogeneous endpoint ports need an adapted identity/monitoring procedure.
Previous: 3. Launch Engine Workers · Next: 5. Verify Requests and Routing
Check your result¶
Compare complete request JSON, installed state, profile readiness and actual routing observations.
Select a supported model/engine tuple before interpreting results. Keep direct-worker success separate from Gateway delivery, routing evidence, engine cache reuse, performance and release qualification. Historical PASS, FAIL and BLOCKED results retain their original artifact identities.
Next steps and cleanup¶
Use the canonical deployment sequence rather than combining fragments from different engine/version pages. Drain before switching or deleting owned resources; retained result pages are evidence, not another deployment recipe. Read the lab starting point if required values are missing. Use cleanup and recovery when ending the run.