A self-hosted, Kubernetes-native post-training API for fine-tuning LLMs.
Sunil Arora
SWE@Google, droot@
Part 1
Part 2
Part 3
Small task-specific models are beating frontier models on narrow tasks.
The hard part now is running the loop: GPUs, scheduling, weight sync, job lifecycles.
Sample from the current policy, grade the samples, update the weights, hand them to the sampler. Repeat for a few hundred steps.
The researcher's code stays on top, OpenRL runs underneath, and the Tinker API is the contract between them.
Plain Python with the Tinker SDK. The environment, the reward, the loss function is domain specific.
A control plane that takes requests and places workers. A data plane where trainers and samplers run.
Full fine-tuning needs the whole device, so co-located workers take turns. A timeslicer grants the turn; llm-d's snapshot agent parks the state in between.
Reproducing LoRA Without Regret on GSM8K RL: full fine-tuning against LoRA rank 32 and rank 1, two seeds each, six runs training at once on one OpenRL cluster.
Mean reward, last 10 of 40 steps
Seed-to-seed spread is 0.013 to 0.016. Rank 1 lands inside it.
All six trained at the same time. The four LoRA runs shared one trainer and one sampler as adapters, and the two full fine-tuning seeds were packed two to a claim. No run had a GPU to itself.
Qwen3-8B on H100, 40 steps of 64 episodes. Line: mean of two seeds, 5-step rolling mean. Band: the spread between the seeds. Data: docs/experiments/lora-without-regret on branch experiment/lora-without-regret. Paper: thinkingmachines.ai/blog/lora
Three jobs on two shared H100s instead of six dedicated GPUs. Same learning curves, more than twice the trainer duty cycle.
Time-sliced, three jobs on two H100s
On six dedicated GPUs: 15.6% duty cycle and 2.10 GPU-hours. The same two GPUs running the jobs one after another: 54 minutes.
Context switches cost 0.5 to 1.6 s at the median; queue waits per phase run 1 to 15 s. All three jobs matched their dedicated-GPU learning curves.
Jobs: Text-to-SQL RL on Qwen3-1.7B, math RL on Qwen2.5-7B, dialogue SFT on Gemma 4 E2B. Figure from llm-d.ai/blog/increase-researcher-velocity-rl-llm-d-time-slicing
Qwen3-8B on GSM8K math RL. The share of parameters that change in a step falls from 12% at step 1 to under 3% by step 25, while reward climbs. So the trainer ships only the changed parameters, and the sampler patches them in place.
Delta weight sync, Qwen3-8B
A full checkpoint is 16 GB and took 35 s to load on the sampler. Deltas travel over shared NFS on GKE, so trainer and sampler need no RDMA link and can be placed independently.
The trainer already holds the previous weights in host memory for time-slicing; that is what the delta is computed against.
Figure from the note “Delta Weights in RL” (Jul 2026). Per-step density for the same model is in docs/experiments/lora-without-regret/delta_density.csv on branch experiment/lora-without-regret.
Three commands, and you have RL as a service on your own cluster. Then point the Tinker SDK at it.
Create a GKE cluster on a recent release for DRA, with Filestore as the shared filesystem.
$ gcloud container clusters create ml \ --location us-central1 --release-channel rapid \ --addons GcpFilestoreCsiDriver
Add accelerator pools. The labels opt a node in and say which roles it takes; repeat with openrl.io/sampler=true.
$ gcloud container node-pools create trainers --cluster ml \ --accelerator type=nvidia-l4,count=2 \ --node-labels openrl.io/enabled=true,openrl.io/trainer=true
Install OpenRL.
$ kubectl apply -f https://github.com/gke-labs/open-rl/releases/latest/download/openrl-distributed-shared.yaml
Point the Tinker SDK at it. The loop from Part 2 runs unchanged.
import tinker # your cluster, not Tinker's cloud svc = tinker.ServiceClient( base_url="http://openrl.my-cluster:8000") trainer = await svc.create_lora_training_client_async( base_model="Qwen/Qwen3-4B-Instruct-2507", rank=16) # then sample, score, forward_backward, optim_step
Or run a recipe, or hand your coding agent the tinker-cookbook and ask for a loop.
What runs today, and what is next in the six months ahead.
nowRuns our own fine-tuning workloads. Installs from one manifest.
nextDay-0 and day-2 operating guides. A versioned API with an upgrade path.
nowOne GPU per trainer or sampler worker.
nextSamplers first, since vLLM already does tensor parallelism. Then trainers. Then the Megatron engine.
nowNVIDIA L4 and H100 through Kubernetes DRA.
nextA TPU proof of concept, then an MVP.
nowDense Qwen and Gemma to about 10B, LoRA and full fine-tuning.
nextDense models to 30B. MoE for training and sampling.
nowkubectl over Workloads, claims and pods.
nextA CLI agents can consume, a dashboard, per-step metrics in a standard format, cancelling a job.
nowText-to-SQL RL, SFT notebooks, autoresearch, the tinker-cookbook as it is.
nextA Legal Agent Benchmark recipe, a finance domain, paper reproductions, the checkpoints API.
The full plan, by focus area and initiative, is in ROADMAP.md. To change it, open an issue with the roadmap label.
We have applied to donate OpenRL to the CNCF, for a vendor-neutral home.
The roadmap and the issues are public. File one, or pick one up.
examples/sft/pig-latin · examples/text-to-sql · examples/autoresearch · docs/setup/gke-setup.md
Every capability we care about is already in it, at low probability.
RL raises the probability of what you reward.