Back to Blog
2026-09-06
11 min read
Download the published fit24gib pack, install llama.cpp at the build the numbers were measured on, and serve it at 86,016 tokens of text context or 73,728 with an image aboard. Every flag comes from the model card or the vramfit repo's own serve how-to, and the section at the end says what to do when your card does not reach the boundary.
You have a 24 GiB card and you want to run Gemma 4 31B on it with as much context as the card will hold. You have read, or do not care about, the argument for why a 14.92 GiB pack beats the 16.44 GiB official build on this card. You want the server up.
Assumed: a Linux box with a 24 GiB NVIDIA card, a working driver, and a terminal. Not assumed: any quantization background. The why is the explanation post, and the evidence is on the model card. This post repeats none of it.
Every number below is published, and the serve boundaries were measured on an RTX 4090 running llama.cpp b10362 on 2026-08-31. Numbers from another frame, the H100 build, the multi-image ladder, say so where they appear. Your card is a different box, and the last section is about what to do when a number does not reproduce.
Disk. The decoder is 14.92 GiB and the projector sidecar is 629 MiB. The llama.cpp build or tarball adds a few GiB. Call it 20 GiB free.
Two files, one artifact. The decoder alone serves text. Images need the sidecar too. Both download in one command below.
The license. The weights carry the Gemma 4 license note. Read it before you serve them to anyone but yourself.
The build is part of the claim. The boundaries on the card were measured at llama.cpp b10362. A newer build allocates the KV cache its own way and may land a rung higher or lower. Pin the build first, reproduce the boundary, then move if you want to.
The repo is public. No token needed.
What lands:
| File | Bytes | Size |
|---|---|---|
gemma-4-31B-it-fit24gib.gguf | 16,015,862,144 | 14.92 GiB |
gemma-4-31B-it-mmproj-q4km.gguf | 659,537,504 | 629 MiB |
Check the decoder before you trust it. The card publishes the hash:
Two ways. The first is the same build number and the same backend the card's boundaries were measured on. The second is the path the repo's rented-H100 how-to takes.
Prebuilt Vulkan tarball. The b10362 release ships a Linux Vulkan build:
Build from source with CUDA. The release ships no Linux CUDA tarball, so CUDA means a build. Pin the commit the tag points at:
Either way the version line reads version: 10362 (4801e3c56). On
the rented H100 that build took 429 seconds. CUDA is a different
runtime from Vulkan, so treat the boundaries below as the rung to
test first, not a promise. Step 5 shows how.
Note what is free before you load. The card's ladders ran with 23,629 to 23,631 MiB free on a 24,564 MiB device, under a desktop.
Then the command from the card, verbatim:
Three flags carry the claim.
-c 86016 is the measured text boundary. The next rung, 90,112,
fails to load.-ngl 99 offloads every layer. Any layer left on the CPU frees
VRAM and invalidates the comparison.-np 1 is one slot. The b10362 server defaults to four, which on
this geometry adds about 2,400 MiB of sliding-window cache and
fails loads that fit at one.Wait for model loaded in the log, then hit the health route. This
build logs all slots are idle only at trace verbosity, so do not
wait on it: a check on that line waits forever with the server
healthy.
The reply is {"status":"ok"}. If the loop returns before
model loaded appears, the server exited, and the tail of
server.log says why; see "If it does not fit" below.
A loaded server is not a server on the GPU. Confirm the offload before you trust any number below:
The first prints offloaded N/N layers to GPU, both numbers equal
and neither of them zero. The second prints nothing. offloaded 0/N
means -ngl 99 was parsed and then discarded, and the second grep
says why: the build has no backend for your card, a CPU-only archive
or a driver it cannot see. Nothing else looks wrong — the server
loads, the health route answers ok, requests come back — and every
layer runs on the CPU at a fraction of the speed, while every
boundary in this post assumes the card is doing the work. Fix step 2
before you measure anything.
The server speaks the OpenAI chat shape. One text turn:
That answers from inside the 86,016-token envelope. It does not tell you what a full envelope costs to decode: the card's boundary check decoded five tokens, and throughput at the boundary is unmeasured. The throughput the card does publish was taken at 8,192 tokens of context on the same 4090, 47.8 tokens per second for this pack against 43.3 for Google's Q4_0.
While the server is up:
At the 86,016 boundary on the 4090 the load passed with 143 MiB
free. That is what a fit bar looks like: the card is full, and the
next rung is the one that fails. If you see a few gigabytes free, one
of the three flags above is not doing what you think, most often a
default -np 4 from a wrapper script.
To confirm the boundary is real on your box, stop the server and load one rung higher:
If it fails, you have confirmed the boundary; if it loads, stop it with Ctrl-C and keep climbing a rung at a time until one fails; that rung is yours. A higher rung means your box idles with more VRAM free than the card's did. The card calls the tuple of box, build, backend, and free VRAM before load the frame, and it prints the frame beside every boundary, because the same file served 81,920 for text three days earlier in a frame with less idle VRAM. Write yours down the same way. It is the only way two boundaries compare.
Images need the sidecar and two more flags. Stop whichever server is
still holding the card, start the image server from the card, and wait
for model loaded the same way as in step 3:
-c 73728 is the measured one-image boundary. The next rung,
77,824, loads and then fails at encode time.--mtmd-batch-max-tokens 264 caps the encode batch at one
1280×720 image, which is 264 image tokens. Without it the server
packs up to 1,024 image tokens into one encode graph, two images
share a graph, and the graph asks for 328 MiB against a 150.63 MiB
one-image reserve. On 2026-09-02 that crashed the server on the
second image at both configurations. With the cap, the same ladder
filled the window to a clean context refusal.Keep about 200 MiB free beyond the load. The image encode allocates at request time, and this build crashes on that failure instead of refusing.
An image request is the same chat shape with an image_url part
carrying a base64 data URL. Write it to a file first: a base64
screenshot is bigger than the single-argument limit a Linux shell
allows, and an inline -d fails with "Argument list too long".
One 768×768 image costs 256 decoder tokens on this pack, measured at the server. The 1280×720 screenshot above is 264 image tokens, 271 once its wrapper is counted, which is why the cap is 264 and the decoder sees 271.
The card's serve ladders, measured 2026-08-31, RTX 4090, llama.cpp
b10362 Vulkan, -ngl 99 -np 1, KV cache f16, 4,096-token rungs:
| Serving shape | This pack | Google's QAT Q4_0 |
|---|---|---|
| Text only, max load | 86,016 (fails at 90,112) | 65,536 (fails at 69,632) |
| One image aboard, max load | 73,728 (encode fails at 77,824) | 49,152 (encode fails at 53,248) |
The gain is 20,480 tokens of text and 24,576 with an image, on that frame. Two of the three things that set a boundary, the build and the box, are yours.
Quality is not the subject of this post. The card's five held-out benchmarks put this pack at four ties and one win against Google's build. Read the tables there, not here.
Work down this list in order. Each item is a boundary the card already crossed.
-np. A wrapper that sets slots for you costs about
2,400 MiB of sliding-window cache on this geometry. One slot, or
nothing here holds.--mtmd-batch-max-tokens 264 is the fix, not a tuning
knob.If you have more card than this, the repo has a how-to for serving the pack on a rented H100. It is the instrument behind the card's real-GUI campaign: a CUDA build of the same b10362 tag, the same two files, and context 8,192 for single-image evaluation. The 24 GiB boundaries and the encode-batch flag belong to the 4090 frame and do not carry over.
What you have at the end is not the card's number. It is a boundary measured on your own box, with its frame written beside it, which is the only kind of number the card ever claimed.
vramfit plan can re-solve this model for
a different budget.gguf extra that reads a
pack without torch, pack comparing its packed bytes against the
recipe's prediction, and every refusal raised under one root. MIT.