Home Blog Resume Contact Ask AI About Me
Home/Blog/How to Serve Multiple LoRA Adapters on One vL…
How toLLM EngineeringvLLMLoRALLM serving

How to Serve Multiple LoRA Adapters on One vLLM Server

8 min readBy Miloš Mitrović

Teams that fine-tune one base model into a dozen task-specific variants hit the same wall: standing up a separate GPU server for every adapter burns money and leaves hardware idle. vLLM avoids that by loading many LoRA adapters on top of a single base model in one process, then routing each request to the right adapter by name. The short version: launch with --enable-lora, register adapters with --lora-modules, and set the model field of each API call to the adapter's name. That one server can then answer as any of your fine-tunes.

Key takeaways

  • Short answer: run vllm serve <base-model> --enable-lora --lora-modules name=path, then request a given adapter by putting its name in the model field of a normal OpenAI-style call.
  • A LoRA adapter is a small pair of low-rank weight matrices that PEFT trains alongside a frozen base model, so N adapters cost roughly one base model in VRAM plus a few megabytes each.
  • max_loras defaults to 1 and caps how many distinct adapters run in a single batch; raise it to serve more concurrently.
  • max_lora_rank defaults to 16 and must be at least the highest rank among your adapters, or the server refuses to load them.
  • Adding or removing adapters at runtime needs VLLM_ALLOW_RUNTIME_LORA_UPDATING=True, which the docs flag as risky to expose in production.

What You Need Before You Start

Three things, and the first is non-negotiable. A working single-model vLLM install is the foundation, so if you have not stood one up yet, start with the basics of serving an open model with vLLM for high throughput and come back.

  • vLLM installed (pip install vllm) on a CUDA GPU with enough VRAM to hold the base model plus KV cache.
  • A base model on Hugging Face, for example meta-llama/Llama-3.2-3B-Instruct.
  • One or more LoRA adapters trained against that exact base checkpoint. If you still need to produce them, the workflow to fine-tune a model with LoRA and QLoRA on a single GPU outputs the adapter directories you will load here. An adapter trained on a different base will fail to load.

How Do You Serve Multiple LoRA Adapters on One vLLM Server?

Follow these six steps and one server will answer as any of your adapters. Everything you need to finish the task lives in this section.

  1. Install vLLM and confirm the GPU is visible.
    pip install vllm
    python -c "import torch; print(torch.cuda.is_available())"
  2. Collect your adapter directories. Each holds an adapter_config.json and the weight file, and each must have been trained against the base model you are about to serve.
  3. Start the server with LoRA enabled and register the adapters. The --enable-lora flag turns on adapter support and each --lora-modules entry maps a request name to a path, as shown in the vLLM LoRA serving guide.
    vllm serve meta-llama/Llama-3.2-3B-Instruct \
        --enable-lora \
        --lora-modules sql-lora=/models/sql-lora math-lora=/models/math-lora \
        --max-loras 4 \
        --max-lora-rank 32 \
        --max-cpu-loras 8
  4. Match the caps to your adapters. Set --max-loras to the number of adapters you expect in one batch and --max-lora-rank to the largest rank any adapter uses. Leave --max-cpu-loras higher than --max-loras so extra adapters can wait in CPU memory instead of being rejected.
  5. Confirm the adapters registered. They appear in the model list next to the base model.
    curl http://localhost:8000/v1/models
  6. Send a request to a specific adapter by name. Put the adapter name, not the base model name, in the model field.
    curl http://localhost:8000/v1/completions \
        -H "Content-Type: application/json" \
        -d '{
            "model": "sql-lora",
            "prompt": "List every customer in San Francisco:",
            "max_tokens": 64,
            "temperature": 0
        }'
    Chat completions work the same way: point model at math-lora and call /v1/chat/completions. Omit the adapter name and the base model answers.

What Does Each LoRA Flag Control?

Four flags decide how many adapters you can hold and how large they can be. The defaults below come from the vLLM LoRAConfig reference, and the two rank and count knobs are the ones that cause most first-run failures.

FlagDefaultWhat it does
--enable-loraoffTurns on LoRA support for the engine. Nothing else works without it.
--lora-modulesnoneRegisters one or more name=path adapters at startup.
--max-loras1Maximum distinct adapters in a single batch. Raise it to serve several at once.
--max-lora-rank16Largest LoRA rank the server allocates for. Must cover your highest-rank adapter.
--max-cpu-lorasnoneHow many adapters to hold in CPU memory. Must be greater than or equal to --max-loras.

Higher --max-lora-rank reserves more memory whether or not you use it, so set it to your real maximum rather than padding it. If every adapter is rank 16, leave the default alone.

How Do You Add or Remove Adapters Without Restarting?

vLLM can register and drop adapters on a live server, which matters when you ship new fine-tunes without a maintenance window. Set the environment variable first, since the runtime endpoints stay disabled otherwise.

export VLLM_ALLOW_RUNTIME_LORA_UPDATING=True
# then start vllm serve as usual

With that set, POST to load a new adapter and POST again to unload it, both documented in the vLLM LoRA serving guide:

curl -X POST http://localhost:8000/v1/load_lora_adapter \
  -H "Content-Type: application/json" \
  -d '{"lora_name": "sql-lora-v2", "lora_path": "/models/sql-lora-v2"}'

curl -X POST http://localhost:8000/v1/unload_lora_adapter \
  -H "Content-Type: application/json" \
  -d '{"lora_name": "sql-lora-v2"}'
The same guide warns that enabling runtime updating in production is risky, because anyone who can reach the endpoint can load arbitrary adapter weights. Keep these endpoints behind an internal network or an authenticating proxy.

Prefer to drive it from Python instead of the HTTP API? Offline, build the engine with LLM(model=..., enable_lora=True) and pass a LoRARequest(name, unique_id, path) straight into generate:

from vllm import LLM, SamplingParams
from vllm.lora.request import LoRARequest

llm = LLM(model="meta-llama/Llama-3.2-3B-Instruct", enable_lora=True)
out = llm.generate(
    "List every customer in San Francisco:",
    SamplingParams(temperature=0, max_tokens=64),
    lora_request=LoRARequest("sql-lora", 1, "/models/sql-lora"),
)

Should You Use Multi-LoRA, Merged Weights, or Separate Servers?

Multi-LoRA is the right default for many small fine-tunes of one base, but it is not the only option. Merging an adapter back into the base gives the fastest single-variant path, and fully separate servers make sense when variants use different base models.

ApproachVRAM costBest when
Single multi-LoRA serverOne base plus a little per adapterMany adapters share one base model and traffic per adapter is uneven.
Merged weights per variantOne full model per variantOne or two variants dominate traffic and you want zero adapter overhead.
Separate full serversOne full model per serverVariants use different base models, sizes, or quantization.

For autoscaling many adapters across replicas, a serving layer such as Ray Serve's multi-LoRA deployment sits on top of vLLM and handles replica placement.

Troubleshooting Common vLLM LoRA Errors

Most first-run failures trace to three mismatches: rank, name, and memory. Here is how each shows up and what fixes it.

  • The server refuses to load an adapter and mentions rank. The adapter's rank exceeds --max-lora-rank. Read the rank from the adapter's adapter_config.json and raise the flag to match.
  • A request returns a model-not-found error. The model value does not match a registered adapter name. Call /v1/models and copy the exact string, including case.
  • You get out-of-memory errors after raising --max-loras. Each concurrent adapter adds activation memory on top of the base model. Lower --max-loras, or keep extra adapters in CPU memory with a higher --max-cpu-loras so they swap in rather than co-resident.
  • The load and unload endpoints return 404. VLLM_ALLOW_RUNTIME_LORA_UPDATING was not set before the server started. Export it and restart.
  • Loading fails with a shape or key mismatch. The adapter was trained against a different base checkpoint than the one you are serving. Retrain or serve the matching base.

What to Do Next

Once a single adapter answers correctly, harden the deployment. Watch per-request latency and throughput with vLLM's engine arguments and metrics so you know when --max-loras is starving a hot adapter. If VRAM is tight, quantize the base model to fit more KV cache, then scale out replicas behind a load balancer once one server saturates. Load-test each adapter separately, since a low-traffic adapter can still block a batch if --max-loras is set too low.

Sources

M
Miloš Mitrović
Revenue Operations & AI Automation

Have a question or a project?

Whether it is about this post or a system you want built, I'm happy to talk.

Get in touch

404

Post not found. It may have been moved or the link is incorrect.

← Back to the blog
Ask AI About Me
Clicking an assistant copies the prompt and opens it: ready to run in ChatGPT, Perplexity, and Grok; in Claude, Gemini, or Copilot press Ctrl+V (Cmd+V on Mac) to paste. Use Copy prompt for any other AI. The assistant reads my site, so it needs web access.
Summarize with AI
ChatGPT, Perplexity, and Grok open with the prompt ready to run. Claude, Gemini, and Copilot open a chat with the prompt copied; press Ctrl+V (Cmd+V on Mac) to paste. The full text is included, so it works even without web access.