Free account = 1 chapter of every course unlocked
No credit card · Google sign-in in 30 seconds · 25 free chapters, one per course
Start free →
All Courses/Local AI Deployment: From Laptop to Production
A tower PC with visible graphics cards sitting under a desk

Local AI Deployment: From Laptop to Production

Real production code for deploying LLMs locally. Quantization, KV cache, vLLM, multi-GPU, edge devices, OpenAI-compatible servers. Full GitHub repo included.

16 chaptersabout 18 hoursFirst chapter free with a free accountFull access: Pro $8.99/month or Lifetime $149 once

Who this is for

  • Engineers who already ran a model with Ollama or LM Studio and now need it to serve an application rather than a chat window.
  • Backend and platform developers asked to keep inference inside the building for privacy, contractual or cost reasons.
  • Homelab and workstation owners who want to understand why their setup is slow instead of buying a larger GPU.
  • Anyone about to spend money on hardware who would rather do the memory arithmetic first.
  • Not a good fit if you want a one-page install guide, or if you are looking for research on model architecture rather than operations.

What you need first

  • ·Comfortable in a terminal: shell, SSH, environment variables, reading a process log.
  • ·Enough Python to read a script, install packages in a virtual environment, and call an HTTP API.
  • ·Basic Docker familiarity helps but is taught where it is needed.
  • ·A machine with a GPU, an Apple Silicon Mac, or a rented cloud GPU. Several chapters run on CPU only, but the optimization material needs a GPU to be meaningful.
  • ·No CUDA programming, no machine learning theory and no prior model training experience required.

Running a model is not the same as deploying one

Running a model and operating one are different jobs. A single command gives you a chat prompt on your own machine, and that experience is genuinely good. It is also why the next step catches people out. What that command started is a single-process, single-user, best-effort program with no admission control, no memory budget and no story for what happens when the host reboots at 3am.

The problem becomes easier to reason about when you see local deployment as three decisions stacked on one another, because each constrains the others.

Weight format and precision. Safetensors in BF16, a GGUF file at some quantization level, AWQ or GPTQ integer weights, or MLX-converted weights for Apple hardware. This choice is usually made casually, by downloading whatever the model card links to first, and it silently rules out most of your runtime options before you have started.

The inference runtime. llama.cpp, vLLM, SGLang, TensorRT-LLM, ExLlamaV2, MLX. Each reads a different subset of formats and each is tuned for a different shape of workload.

The serving layer. The HTTP surface, request queueing, batching policy, authentication, rate limits, timeouts, metrics, and the supervisor that brings the process back.

Two properties make this harder than deploying an ordinary web service.

The first is that memory does not scale the way you are used to. A stateless API server absorbs one more concurrent request with a small, bounded slice of memory. An LLM server absorbs it with however much its key/value cache requires, and that figure grows with the length of the conversation. Capacity planning stops being a CPU question and becomes an explicit allocation problem.

The second is that latency is not one number. Time to first token is dominated by the prefill pass, which is compute-bound and scales with prompt length. The gap between subsequent tokens is dominated by streaming weights out of memory once per step, which is bandwidth-bound and largely independent of how long the prompt was. Changes that improve one often damage the other: larger batches raise total throughput and per-request latency simultaneously. Optimize against a single averaged figure and you will degrade the experience while the dashboard reports an improvement.

There is a third property that gets less attention. The interesting failure mode is frequently not an error at all. A silently truncated context, a mismatched chat template or an over-aggressive quantization raises no exception. It produces slightly worse answers, and nothing in the logs mentions it.

The memory arithmetic: weights, quantization and the KV cache

Almost every capacity question about local inference reduces to two numbers that people conflate: the memory the weights occupy, which is fixed, and the memory the conversations occupy, which is not.

Weights are the easy half

Parameter count multiplied by bytes per parameter gives the floor. BF16 is two bytes per parameter, eight-bit integer formats are roughly one, and four-bit formats are roughly half a byte before overhead. Real quantized files come in above the nominal figure because modern schemes are mixed precision. A GGUF K-quant keeps attention and embedding tensors at higher precision than the feed-forward blocks, and the suffix in names such as Q4_K_M or Q5_K_S encodes which tensors received the extra bits. Read the file size on disk rather than trusting the multiplication.

The KV cache is what actually breaks you

Every token already processed leaves a key vector and a value vector behind in every layer, and those have to stay resident for as long as the sequence is alive. The size is the product of two (for key and value), the layer count, the number of key/value heads, the head dimension, bytes per element, the sequence length and the number of concurrent sequences. Three of those terms are yours to set at deploy time.

This produces the single most common local deployment surprise. A model that loads happily and answers a short question falls over on a long document, or on the fourth simultaneous user. The weights did not change. The cache did. Serving one person at two thousand tokens of context and serving eight people at thirty-two thousand tokens are genuinely different hardware requirements for the same file.

Two mitigations matter. Grouped-query attention, used by most current open-weight models, shares key/value heads across query heads and cuts cache size considerably. The reduction factor is not a mystery: it is the ratio of attention heads to key/value heads in the model's config file, so you can read it rather than estimate it. Separately, the cache itself can be quantized independently of the weights. Eight-bit or four-bit KV is its own decision with its own quality cost, usually smaller than the equivalent weight quantization, but not free.

What lower precision actually costs

Quantization damage is not uniform across capabilities. Fluent conversational output survives aggressive quantization far better than exact instruction-following, schema-constrained output, arithmetic and long-context recall. A model that still sounds articulate at three bits can quietly stop honoring a JSON schema, and if your application depends on parsing that output, the model has failed even though every sample you skim-read looks fine.

The llama.cpp project publishes perplexity comparisons across its quantization types, and those are the right reference for relative ordering. They are not a substitute for measuring on your own task, because perplexity on a general corpus is only loosely connected to whether your extraction prompt still returns parseable output. Writing a small task-specific check before you commit to a quantization level is the cheapest insurance in this entire subject.

Choosing a runtime, and why the popular answer is often wrong

There is no best runtime. There is a best match between the shape of your workload and the hardware in front of you.

RuntimeWeight formatsSuitsMain constraint
llama.cpp / OllamaGGUFone or a few users, mixed or CPU-only hardware, Macsthroughput falls away under real concurrency
vLLMsafetensors, AWQ, GPTQ, FP8many concurrent users on datacenter or recent consumer NVIDIACUDA and ROCm are the mature paths, heavier startup, preallocates VRAM
SGLangsimilar coverage to vLLMagent and multi-turn workloads with heavily shared prompt prefixessame platform constraints
TensorRT-LLMcompiled enginesmaximum NVIDIA throughput when the model set is stableper-model, per-GPU compile step
ExLlamaV2 / TabbyAPIEXL2, GPTQconsumer GPUs where VRAM is tightnarrower model coverage
MLXMLX-convertedApple SiliconApple hardware only

Two ideas explain most of the throughput gap between the top and bottom of that table.

Continuous batching lets the server admit a new request into the running batch the moment any sequence finishes, instead of holding a fixed batch open until its slowest member completes. Because generation lengths vary enormously between requests, this alone changes the economics of multi-user serving.

Paged attention, introduced with vLLM by the Berkeley group that published the PagedAttention paper, stores the KV cache in fixed-size blocks rather than one contiguous reservation per sequence. Memory is no longer wasted on the difference between the context length you reserved and the context length the conversation actually used, and blocks can be shared between sequences with identical prefixes.

The decision tree is shorter than the debate suggests. One user, or a handful, on whatever hardware happens to be available: the llama.cpp family, with Ollama on top if you want model management handled for you. A team or an application with a genuine load pattern on NVIDIA: vLLM, accepting the operational weight that comes with it. A Mac: MLX or llama.cpp with Metal, planning around the fact that prompt processing rather than generation is the weak point. A fixed model where you control the whole stack and need the last increment of latency: TensorRT-LLM, and not before.

One warning about numbers found online. Throughput figures for local inference mean almost nothing without the batch size, prompt length, generation length, quantization, context limit, driver version and power limit attached, and they shift between releases of the same runtime. The only figure that describes your deployment is the one you produce on your hardware with your own prompt distribution. Treat published benchmarks as a hint about ordering and never as a capacity plan.

Failure modes you will meet in the first month

The problems that take a local deployment down are rarely the ones people prepare for.

Memory exhaustion under concurrency. The model loaded, so the weights fit. Then several long conversations arrive together and the cache does not. Runtimes handle this differently: vLLM reserves a fraction of the device up front and preempts or queues requests when blocks run out, while llama.cpp-based servers are more likely to fail the allocation outright. Neither behavior is wrong, but you need to know which one you have before it happens in front of users.

Cold starts and model thrash. Loading tens of gigabytes of weights from disk is slow. If your server unloads the model between requests, or if you route across several models on one GPU, the eviction cost dominates everything else. Ollama's keep-alive setting exists precisely for this, and leaving it at the default is a common cause of "it was fast yesterday".

Silent context overflow. When a conversation exceeds the configured context window, something has to go, and the default is often middle truncation. That is exactly where the retrieved documents live in most RAG applications, and sometimes where the system prompt ends up after a long exchange. The response is coherent and wrong.

Chat template mismatch. Every instruction-tuned model expects a specific arrangement of role markers and special tokens. Serve a model through a runtime that applies a different template and you get output that is subtly off: ignored system prompts, invented turns, refusal loops. It looks like a bad model. It is a bad wrapper.

Streaming that dies at the proxy. Server-sent events pass through reverse proxies badly by default. Response buffering in nginx will hold the whole generation until it completes, which converts a nicely streaming interface into a long stare at a blank box, and aggressive idle timeouts will cut long generations off entirely.

Orphaned generations. A client disconnecting does not necessarily stop the work. Without cancellation wired through, an abandoned browser tab can keep a slot occupied for the full generation length, which is a quiet capacity leak under load.

Reproducibility that is not. Even at temperature zero, output can differ between runs because floating-point reduction order changes with batch composition, and because model tags get republished. Pin digests, not tags, if you want the thing you validated to be the thing you serve.

Thermal and power limits. Laptops, small-form-factor builds and dense multi-GPU rigs all throttle. Sustained generation is a very different thermal load from a burst, and a rig that looks quick in a short test may not hold that rate over a long job.

The metrics worth exporting from day one are time to first token, inter-token latency, queue depth, KV cache utilization, prompt and completion token counts per request, and the rate at which requests are rejected or preempted. Average response time on its own tells you nothing useful about an LLM server.

Multi-GPU, Apple Silicon and edge: three genuinely different problems

Once a model stops fitting on one accelerator, or once the accelerator is not an NVIDIA card, the shape of the problem changes.

More than one GPU

There are three distinct things people mean by multi-GPU, and they are not interchangeable.

Layer offload, which llama.cpp and Ollama do by default, splits the model's layers across devices and runs them in sequence. This is what makes an oversized model fit at all, but a single request only ever occupies one device at a time, so it buys capacity rather than speed.

Pipeline parallelism formalizes that split and keeps every stage busy by having several requests in flight at different stages. It raises throughput and does nothing for single-request latency.

Tensor parallelism splits individual matrices across devices so all of them work on the same token simultaneously. It is the option that genuinely reduces latency, and it is the one with real constraints: it needs high interconnect bandwidth, so a rig with cards on narrow PCIe lanes may see gains disappear into communication overhead, and the parallel degree has to divide the model's attention head count cleanly.

Mixed VRAM sizes complicate everything, since most splitting schemes assume uniform devices and end up bounded by the smallest card. Power is its own trap: several GPUs drawing transient peaks simultaneously can trip a supply rated comfortably above their nominal combined draw.

Apple Silicon

Unified memory changes the calculation entirely. There is no separate VRAM pool, so a large fraction of system memory is addressable by the GPU, and models that would need a datacenter card on a PC fit on a well-specified Mac. The limiting factor moves to memory bandwidth for generation and to compute for prefill, which is why Macs feel comparatively strong on long conversational generation and comparatively weak when you paste in a large document. MLX targets the hardware directly; llama.cpp with Metal is the more portable option. The wired memory limit is adjustable, and knowing that is often the difference between a model loading and not.

Edge and embedded

At the edge, the constraint set inverts. Power envelope, thermal headroom, and the absence of swap matter more than raw throughput. Storage is frequently flash with finite write endurance, so logging habits from server work become a hardware problem. Updates may have to work offline. Most importantly, the model selection changes: a small model with a narrow task definition and constrained structured output will beat a general chat model that barely fits, because the failure you cannot afford at the edge is the one nobody is present to notice.

Whether you are ready, and what to study alongside it

A reasonable self-check before spending time here. Can you explain, roughly, why a seven-billion-parameter model at four bits needs less memory than the same model at sixteen bits, and why that difference is not the whole story? Have you already hit an out-of-memory error you could not explain? Do you have a use case where sending text to a hosted API is genuinely a problem, whether for contractual, regulatory, latency or cost reasons? Two yes answers is enough. Three means the material will pay for itself quickly.

If none of those apply, a hosted API is very likely the right answer for now, and there is no shame in it. Local deployment earns its keep when data cannot leave, when per-token pricing has become the dominant cost line, when the workload is embarrassingly steady rather than spiky, or when you need the system to keep working without a network. It costs you the operational burden that the API provider was absorbing on your behalf.

The material is also useful in reverse. Understanding prefill against decode, batching and cache pressure makes hosted API behavior legible: why a long system prompt is expensive on every call, why prompt caching exists, why streaming changes perceived latency more than actual latency.

Three adjacent skills compound with this one. Evaluation comes first, because without a task-specific test you cannot tell whether a quantization level, a runtime change or a model upgrade helped or hurt, and every optimization decision in this field is a trade. Retrieval comes second, since most useful local deployments end up feeding private documents into the context and the retrieval quality dominates the model choice more often than people expect. Security comes third: the moment a local model is reachable over the network and holds a tool, you have inherited a different threat model.

The course carries a public GitHub repository, so the configuration files, serving scripts and quantization workflows are readable before you commit to anything, and reusable afterwards.

Common questions

Do I need an NVIDIA GPU to take this course?

No, though what you can practice depends on what you have. The llama.cpp, quantization, Ollama, server and RAG material runs on CPU or Apple Silicon. The vLLM and multi-GPU chapters assume a CUDA or ROCm device, and renting a cloud GPU by the hour is a reasonable way to work through them without buying anything.

How much VRAM do I actually need for a 70B model?

Start with arithmetic rather than a recommendation. Seventy billion parameters at roughly half a byte each, which is what four-bit quantization gives you, is about thirty-five gigabytes before a single conversation exists. Then add the KV cache, which depends on your context length and how many people are talking at once, and add runtime overhead. That is why the answer for one user at short context and for a team at long context differ so widely for the same file.

Is Ollama enough, or do I need vLLM?

Ollama is enough until concurrency becomes real. It manages models well, runs almost anywhere and exposes an OpenAI-compatible endpoint. What it does not do is match vLLM on aggregate throughput when several users are generating simultaneously, because continuous batching and paged attention are exactly the problems vLLM was built for. If your load is one person at a time, moving to vLLM buys you operational complexity and very little else.

Will a quantized model be noticeably worse than the full-precision one?

For conversational use, usually not at four bits and above. For tasks that need exact behavior, such as producing valid JSON, following long multi-step instructions or recalling detail from a long context, degradation shows up earlier and is easy to miss because the prose still reads well. The honest approach is to build a small check for your specific task and run it at each quantization level rather than relying on a general recommendation.

Can one machine serve local AI to a whole team?

Yes, and the constraint is almost always the KV cache rather than the weights. Sizing the deployment means deciding a maximum context length, a maximum number of concurrent sequences and a policy for what happens when both limits are reached. The course covers admission control, queueing and the OpenAI-compatible server layer that lets existing tools point at your box without code changes.

Does the course come with working code?

It has a public GitHub repository containing the deployment configurations, serving scripts, quantization workflows and the capstone project, so you can inspect the material before buying and reuse it in your own environment afterwards.

Related reading

Full syllabus

1

Why Local AI Wins

Free preview
Read free →
2

Hardware Foundations

3

Model Anatomy

4

Ollama Deep Dive

5

llama.cpp From the Ground Up

6

Quantization Theory

7

Hands-On Quantization

8

KV Cache and Attention

9

Inference Optimization

10

vLLM in Production

11

Apple Silicon + MLX

12

Multi-GPU Deployment

13

Edge Deployment

14

OpenAI-Compatible Server

15

Local RAG + Agents

16

Capstone: Production Deployment

Unlock all 16 chapters

Plus 24 other courses — 545 more chapters included.

Every course, every future course, the Python Lab and eight downloadable kits, nothing to renew. Or subscribe: Pro $8.99/month

Free Tools & Calculators