How Much VRAM Does an LLM Actually Need?

Weights are the easy part. The context cache is what actually decides whether the model fits.

In short

A 7B model takes about 14 GB of FP16 weights, but 8K context at batch 8 needs about 30 GB of KV cache. Size the cache first, then precision, then the card.

Why OOM is almost never about the weights

The first time a 13B model hits torch.OutOfMemoryError halfway through loading, the instinct is to rent a bigger card. That instinct is usually wrong, and it is the expensive answer.

Weights load once and then never change. Everything else on the card is allocated per request and scales with how many people you serve at once. A 13B model in 4-bit leaves roughly 19 GB free on a 24 GB card — and a busy endpoint can eat all of it with context alone, while the weights sit there unchanged.

The four consumers of VRAM

Component7B FP167B 4-bitScales with
Weights~14 GB~4 GBparameters × precision
KV cache~0.5 MB / token~0.5 MB / tokenlayers × kv-heads × context × batch
Activations1–3 GB1–3 GBbatch × sequence length
CUDA context1–2 GB1–2 GBfixed per process

The KV cache formula, written out

For each token you keep two tensors (a key and a value) for every layer and every key/value head. So the cost per token is:

2 × layers × kv_heads × head_dim × bytes_per_element

For a 32-layer model with 32 KV heads of dimension 128 in FP16 that is 2 × 32 × 32 × 128 × 2 = 524,288 bytes, or 0.5 MB per token. At 8K context that is 4 GB for a single request. At batch 8 it is 32 GB — and now the card is gone before the weights are even counted.

This is why grouped-query attention changed the economics. A 70B model with 80 layers but only 8 KV heads costs 2 × 80 × 8 × 128 × 2 = 320 KB per token — less per token than the 7B example above, despite having twenty times the parameters. Parameter count tells you the weight size. It tells you nothing useful about the cache.

Recompute it for the exact model you are deploying

import json

cfg = json.load(open("config.json"))

layers    = cfg["num_hidden_layers"]
hidden    = cfg["hidden_size"]
heads     = cfg["num_attention_heads"]
# GQA models expose fewer KV heads than attention heads.
# Falling back to `heads` overestimates KV cache by the
# group factor -- common mistake when sizing Llama-3.
kv_heads  = cfg.get("num_key_value_heads", heads)
head_dim  = cfg.get("head_dim", hidden // heads)

def kv_bytes_per_token(dtype_bytes=2):
    return 2 * layers * kv_heads * head_dim * dtype_bytes

def total_gb(params_b, ctx, batch=1,
             weight_bpp=2, kv_bytes=2, overhead_gb=1.5):
    weights = params_b * 1e9 * weight_bpp / 1024**3
    kv = (kv_bytes_per_token(kv_bytes) * ctx * batch) / 1024**3
    # activations + fragmentation, roughly 15% of weights
    acts = weights * 0.15
    return weights + kv + acts + overhead_gb

print(f"KV/token: {kv_bytes_per_token()/1024:.0f} KB")
for ctx in (4096, 8192, 32768):
    for b in (1, 8):
        print(f"ctx={ctx:6} batch={b}: "
              f"{total_gb(7, ctx, b):5.1f} GB")

Order of operations when the card is too small

Which card actually fits your case

Pick the row closest to your deployment. All figures are recomputed from the formula above, not from vendor spec sheets.

7B, single user, testing

24 GB card (RTX 3090 / 4090 / L4)

  • 4-bit weights ~4 GB, leaving ~18 GB for context
  • 8K context at batch 1 costs ~4 GB — comfortable
  • No reason to pay for a datacenter card at this stage

Batch 4 at 8K context already needs ~16 GB of cache. If you plan to load-test, size for the batch you intend to serve, not for the batch you test with.

7B serving real traffic

48 GB card (L40S / A6000), or 2 × 24 GB

  • 8K context at batch 8 is ~32 GB of cache alone
  • Headroom matters: a single long request should not stall the queue
  • Two replicas on separate cards beat one large card for availability

Multi-GPU tensor parallelism splits the weights, not the cache linearly — cross-card traffic becomes the bottleneck before memory does.

70B quantized, low concurrency

2 × 48 GB or 1 × 80 GB (A100 / H100)

  • 4-bit weights ~35 GB — already past a single 24 GB card
  • GQA keeps cache small: ~0.3 MB per token
  • 80 GB fits weights plus 8K context at modest batch

70B at 4-bit degrades long-tail knowledge first. Keep an FP16 baseline to compare against, or you will misread a quality regression as a prompt problem.

Embedding / reranker only

16–24 GB, no datacenter card needed

  • Embedding models are small and have no KV cache growth from long generation
  • Throughput comes from batching, not from VRAM size
  • A CPU-only host handles low-volume indexing

Do not co-locate the embedding model with a large generation model and assume they share memory politely. They do not — reserve separate headroom.

Common questions

Can I just check the model's parameter count and multiply?

For weights, yes: parameters × bytes per parameter. For anything else, no. Context length and batch size change the answer by an order of magnitude, which is why a model that fits in a quick test can OOM in production with identical code.

Why does the same model OOM in production but not in testing?

Concurrency. Testing usually runs one request at a time; production holds several KV caches at once. Paged attention and continuous batching reduce fragmentation but do not remove the per-token cost.

Is 4-bit quantization lossless enough for production?

It is a trade, not a free lunch. 4-bit weights cost long-tail factual recall first, which is exactly what evaluation sets tend to miss. Keep an FP16 baseline and compare on your own task before shipping.

Where do these architecture numbers come from?

From the model's own config.json — layer count, attention heads, and key/value heads. Do not copy numbers between model families; GQA and MHA differ by a large factor in cache cost.

Do this before renting anything