Skip to content
12 min read

How many cameras fit on one box — and the ceiling that hides until you cross it

Sizing a box for a camera fleet has two answers and people only compute one. Here are measured cores per stream for the software path, the hardware ceiling that stops you at eight sessions while the GPU sits at 8% utilisation, and what crossing it looks like — which is nothing at all.

Vyacheslav Pankratov· Fullstack Developer
Cover art for “How many cameras fit on one box — and the ceiling that hides until you cross it”
On this page

TL;DR: Two different ceilings decide how many cameras fit on one box, and teams usually compute only the first. The CPU answer is measurable: on an i9-14900, one 1080p25 stream costs 0.13 cores to decode, 0.30 to decode plus encode a 360p layer, and 0.79 to re-encode in full. The GPU answer is not a curve at all — it is a hard NVENC session limit of 8 per system on a GeForce card, reached while the GPU still reports single-digit utilisation. Crossing it produces no error.

The question arrives as procurement: how many cameras will this machine hold? Somebody opens a monitoring dashboard, sees the GPU at 8%, and concludes there is room for an order of magnitude more. (If the question behind it is what the fleet costs rather than what it fits, that is a different forensic exercise.)

That reading is correct about the metric and wrong about the machine. Utilisation measures how busy the encoder is; it says nothing about how many sessions the driver will let you open, and those are different numbers with different limits. This article is both ceilings, measured, and what it looks like when you meet the invisible one.

What does one stream actually cost on the CPU?

Measured rather than estimated, because this is the number people inherit from forum posts.

Method, so you can judge it: a synthetic 1080p25 H.264 source, transcoded with ffmpeg -benchmark, reported as CPU-seconds consumed per second of video — which is the same thing as cores needed to keep up in real time. Run on 2026-08-01 inside a 4-core-capped container on an i9-14900 that was already serving production traffic — so these are conservative rather than best-case numbers, and a quiet machine would do slightly better.

Path Cores per stream
Decode only 0.13
Decode + encode one 640×360 layer 0.30
Full 1080p re-encode 0.79

Three things fall out of that table, and the third one corrected something we had believed.

The simulcast publisher is about a third the cost of re-encoding everything. Decoding once and adding one small layer is 0.30 against 0.79 — which is the whole economic argument for building a publisher rather than transcoding, and now it has a ratio instead of an adjective.

A full re-encode really is roughly one core per camera. 0.79 on a fast desktop CPU at veryfast; a slower box, a better preset or a higher input framerate all push it to 1 and past it. The rule-of-thumb survives contact with a measurement, which is not always how this goes.

Decode does not dominate the budget — it is 44% of the simulcast path and 17% of a full re-encode. We had carried the opposite assumption from an engineering note, and the benchmark disproved it. Encoding is the bigger half in both cases.

So the CPU ceiling is arithmetic: usable cores ÷ cores-per-stream, keeping real headroom because these numbers are steady-state and a fleet is not. Synthetic content, one CPU, one preset — bench yours before you buy anything.

And cores are not the only thing that runs out. The same benchmark reported peak resident memory per process: 0.22 GiB for the decode-plus-small-layer path and 1.03 GiB for a full 1080p re-encode. That second number is the one that bites, because it scales with exactly the streams you were sizing by cores: thirty full re-encodes is ~31 GiB of RAM before the operating system, the application and page cache get a look in. On a 64 GiB machine that is half the box spent on a number nobody put in the capacity plan.

Treat the answer as the minimum of the two, and remember that memory bandwidth and network are a third constraint this article does not measure — cores are necessary, not sufficient. Getting from these per-stream figures to a capacity you can trust is a test in its own right, with failure modes a ramp cannot reach: how to load-test a system shaped like this.

  • Full 1080p re-encode0.79 cores
  • Decode + one 360p layer0.30 cores
  • Decode only0.13 cores
Measured, not estimated: CPU-seconds per second of video, which is the same thing as cores needed to keep up in real time. The decoder is the smaller half of both transcode paths.

Why does the GPU look unlimited and then simply stop?

Because the limit is not capacity, it is a licence check.

NVIDIA's own NVENC Application Note splits GPUs into qualified and non-qualified. On qualified cards — the professional line — concurrent encode sessions are bounded only by real resources: encoder throughput, system memory, video memory. On non-qualified cards, which is every GeForce, the driver enforces a fixed maximum of 8 concurrent sessions, and the count is per system, not per card.

Two consequences worth having before you spend money:

  • A second consumer GPU does not double your sessions. The cap is summed across every non-qualified card in the machine. This is the single most expensive misunderstanding available here, because the fix people reach for is another card.
  • The number has moved between SDK versions — it was 3 in Video Codec SDK 11 and is 8 in 13. So a limit you measured, or read in an article, has a shelf life. Check it against the note for the driver you actually run.

And the reason the dashboard misleads: utilisation and session count are unrelated. On our own production box, verified again on 2026-08-01, the GPU reports 8% utilisation while a live 1080p59 encode runs at 1.8 ms average latency. There is enormous throughput headroom and, unpatched, room for seven more sessions — after which throughput stops mattering entirely.

What does crossing the ceiling look like?

Nothing. That is the whole problem, and it is worth being precise about the mechanism because it explains why the failure is so hard to attribute.

When the session cap is reached, a pipeline asking for the GPU encoder does not receive an error saying "session limit reached". In our stack the GStreamer element disappears from the registry entirelygst-inspect-1.0 nvh264enc reports it missing — so the code that selects an encoder simply does not find one and falls through to the CPU path. The fallback is a feature working as designed; there is just no log line anywhere saying why it engaged.

The symptoms are therefore all second-order:

  • one camera's CPU cost jumps to the full re-encode figure while its neighbours stay near zero;
  • total host CPU climbs by roughly a core per affected stream, with no configuration change to blame;
  • the GPU utilisation graph does not move at all, because nothing new was asked of it.

The multiplier is measurable, and it is not subtle. Leaving decode on the CPU and swapping only the encoder — NVENC out, libx264 in — a 1080p60 stream cost 2.8× more CPU on the software path (2.74–2.84× across five interleaved pairs). That is the factor applied to every stream that falls back, which is how a handful of them take a box that looked comfortable an hour ago. A stack that also decodes on the GPU has further to fall than this, since here the decode was already being paid for in both arms.

Only the ratio is quoted, deliberately. That measurement ran on a laptop, where the absolute cores-per-stream figures drifted about 10% over a session as the package heated — the same workload re-measured at the end cost more than it had at the start. Interleaving the two arms cancels the drift; the ratio held to within 3.6% while both absolutes climbed. Absolutes from a thermally constrained machine are not worth printing, and the ones in the table above come from elsewhere.

The tell is the session count, not the load. nvidia-smi encodersessions lists them; count the rows. If that number equals your cap and a stream is on the CPU, you have found it. The corollary is a monitoring rule: alert on session count against the cap, never on GPU utilisation, because utilisation will look healthy at the exact moment you run out.

One more sharp edge: a stream that has already fallen back stays fallen back. In our system a camera latched to the CPU encoder only returns to the GPU on a fresh start — freeing a session does not migrate it. So after you fix the cause you still have to restart the affected streams to reclaim the hardware.

Can the cap be lifted, and what does it cost?

Yes, and the honest answer includes what you take on by doing it.

The community patch keylase/nvidia-patch removes the restriction by editing the driver's encode library. We run it in production: applied 2026-07-14 on a GeForce-class card, with 12 concurrent sessions verified afterwards against the stock ceiling of 8. Re-checked on 2026-08-01, still in place and the GPU element still registering.

What it costs, in order of how likely it is to surprise you:

It bounces every GPU process the moment you apply it. The patch rewrites libnvidia-encode.so in place, so every process that has the library mapped gets a SIGBUS when its code pages change underneath and restarts. Ours self-healed in seconds, and the live output still blinked. This is a maintenance-window operation, not a quiet fix.

It is host state that lives outside your repository. Nothing in version control knows about it, and it is keyed to the exact driver version, so it is silently lost on any of: a GPU driver upgrade, a kernel upgrade (which reinstalls the NVIDIA stack), or a server move or reprovision. Each of those hands you back the original cap, with the original silent fallback, at a moment when you are looking at something else entirely.

It needs VRAM you must budget — roughly 200–400 MiB per additional 1080p session.

The mitigation is not clever, it is procedural: make "is the patch still applied" a check that runs after every host change, and treat its absence as a capacity incident rather than a curiosity. Ours is a one-line probe for whether the GPU encoder element registers at all.

So how many cameras actually fit?

Put both ceilings on the table and take the lower one. That is the entire method, and it is usually skipped because each ceiling is owned by a different person.

GPU path:  min(session_cap, VRAM ÷ ~0.3 GiB per 1080p session)
           session_cap = 8 per SYSTEM on consumer cards, unpatched
CPU path:  min(usable_cores ÷ cores_per_stream,
               usable_RAM   ÷ RSS_per_stream)
           cores: 0.30 decode + small layer · 0.79 full re-encode
           RSS:   0.22 GiB                  · 1.03 GiB

Worked through on the hardware above: a 32-thread CPU with a quarter held back for everything else leaves ~24 usable cores, which is roughly 80 simulcast-path streams or 30 full re-encodes — the latter also wanting ~31 GiB of RAM, so on a 64 GiB box memory and cores run out at about the same moment, which is luck rather than design. Meanwhile the same machine's consumer GPU stops at 8 sessions, patched to 12. The GPU is dramatically faster per stream and dramatically more limited in how many streams it will accept, and if you size on the fast half you will provision a machine that cannot run what you bought it for.

The practical shape that follows: use the GPU for the streams that need it and the CPU for the tail. Reserve hardware sessions for the outputs where latency and quality are visible, put the secondary rungs on the CPU deliberately rather than by accident, and size the CPU for the number of streams you expect to land there — including the ones that will land there when a session is lost.

The patch is not in your repository

Lifting the session cap edits the driver library on the host, keyed to one driver version. A driver upgrade, a kernel upgrade or a server move silently hands back the original ceiling — and with it the original silent fallback, at a moment when you are looking at something else. Make “does the GPU encoder still register?” a check that runs after every host change.

FAQ

We are at 8% GPU utilisation. Are we near any limit? Possibly at it. Utilisation is throughput and the cap is session count; they are independent. Count sessions with nvidia-smi encodersessions and compare against 8 on a consumer card. Being at the ceiling with a nearly idle GPU is the normal presentation, not an anomaly.

Would a second GPU double our capacity? Not on consumer cards. NVIDIA's limit is per system, summed across every non-qualified GPU, so the second card adds throughput and no sessions. A professional card is the supported way to raise the ceiling, and the patch is the unsupported one.

Is patching the driver a reasonable production decision? It is a real trade, not a free win. You gain sessions; you take on host state that no upgrade preserves, a restart of every GPU process each time you apply it, and a support position outside what the vendor sanctions. We run it, and we also run a check for whether it is still applied — the second half is what makes the first half survivable.

What should we monitor? Encode session count against the cap, and per-stream CPU. Between them, a stream that silently moved to the CPU shows up as both a session freed and a core consumed, which is a much clearer signal than either alone. Do not alert on GPU utilisation for this.

The four things worth taking away

  1. Two ceilings, and the lower one wins. Cores ÷ cores-per-stream on the CPU side; a fixed session count on the GPU side. Sizing on the wrong one is how a fast machine ends up unable to run the job.
  2. Measured cores per stream: 0.13 decode, 0.30 decode plus a small layer, 0.79 full re-encode. The simulcast path is about a third of re-encoding, and the encoder — not the decoder — is the bigger half of both.
  3. The GPU cap is 8 per system on consumer cards, and utilisation will not warn you. It is a driver policy, not a resource limit, so the graph you are watching stays healthy right up to and past the wall.
  4. Crossing it is silent by construction. The encoder element stops existing and the code falls back; nothing logs the reason. Alert on session count, and remember that a fallen-back stream stays on the CPU until it restarts.

If you are sizing a box this week, count sessions before you count cores — the ceiling that has no gradient is the one that decides the answer.


Sizing this properly for a real fleet — which streams belong on hardware, what the tail costs on CPU, and what happens on the day the patch is silently gone — is exactly the scope of an Architecture Sprint: measured on your hardware, costed, and yours to act on with or without us.

The rest of the capacity picture — what to alert on, and why utilisation is the wrong number — is on real-time video for streaming platforms.

Was this helpful?

// Build it

Running this in production?

Real-time video breaks in the places a demo never reaches — negotiation, weak networks, the transcoding bill. Tell us what breaks and where; we reply within a day with a concrete next step.