Tiny-LLM: Build a Mini vLLM on Apple Silicon

TL;DR
30 sec7 min coredeep dive optional

Tiny-LLM is a hands-on course where you build an LLM serving engine from raw array operations on Apple Silicon: KV cache, continuous batching, and paged attention, ending with a coding agent running on the engine you wrote.

  • Four weeks: Qwen3 from matrix multiplies, a faster single request, a mini vLLM, then a coding agent.
  • You implement operators in Python, C++, or Metal; MLX stays as the correctness oracle and baseline.
  • No CUDA rig required: one shared-memory Apple Silicon machine runs the whole path.
  • The book is free and the repo carries tests, a reference solution, and a tracked roadmap.

Most engineers meet LLM inference through configuration: pick a serving engine, tune batch size, read the docs. Tiny-LLM, published by Chi at skyzh/tiny-llm with the book at skyzh.github.io/tiny-llm, is the other road: implement the serving path yourself until continuous batching and paged attention are things you have written, not things you have heard about.

Tiny-LLM course repository on GitHub describing the four week path from matrix multiplication to a mini vLLM serving engine

The framing the author gives is exact: a serving-side counterpart to CMU’s Needle project, the course where machine-learning students build a deep-learning framework from array operations. The premise transfers cleanly. You can read about KV-cache eviction a hundred times, or you can implement it once and watch decode memory stop growing. The course is at 4,716 stars, was pushed today, and carries a public audit trail of what is implemented, tested, and reviewed.

What you build, week by week

The curriculum is sequenced so each mechanism arrives exactly when the running model needs it, which is what separates it from reference implementations you read but never extend:

WeekThemeWhat lands in your engine
1From matmul to textA Qwen3 model from array ops: attention, RoPE, grouped-query attention, RMSNorm, the MLP, sampling, the autoregressive loop
2A faster single requestKV and capacity caches, packed 4-bit quantized matvec, SIMD matmul prefill, cumulative normalization kernels
3Build a mini vLLMContinuous batching, chunked admission, paged KV as the canonical layout, decode attention and FlashAttention reading pages directly
4Build a coding agentA bounded, validated agent loop over a small workspace, with checkpointing, compaction, and steering

Week 1 deserves emphasis because it is the filter. If writing attention from matrix primitives sounds like a good week, the course fits. If it sounds like suffering, the foundations reading in our inference foundations roundup is the gentler entry, and Tiny-LLM will still be here after it.

Week 3 is the payoff: a serving engine you own

The course’s center of gravity is the week where your single-request engine becomes a server. Continuous batching arrives with chunked admission, so new requests join a running batch without waiting for it to drain. Paged KV storage becomes the canonical layout, which is the same design decision vLLM made production-famous. Decode attention and the FlashAttention variant then operate straight over those pages, which frees the scheduler from materializing dense history tensors between steps.

These are not toy approximations. The README connects each exercise to the costs that dominate real serving, memory traffic, kernel occupancy, KV-cache growth, and request scheduling, and after implementing them you can read any engine changelog and recognize which of those costs a feature is attacking. After week 3, the configuration knobs of production engines stop being folklore, because you have measured what each one trades, and conversations about serving performance become conversations about mechanisms rather than settings. Our serving engines overview covers vLLM and the field from the operator side; this course is what makes that article feel obvious.

Why MLX and Qwen3-4B make sense

The platform choice is the quiet strength of the design. Apple Silicon gives the course a single unified memory space plus direct Metal kernel access, on hardware many engineers already own, removing the CUDA workstation prerequisite that keeps most people from systems-level inference work. MLX supplies the array layer and an extension runtime, but its high-level neural-network layers are off limits for exercises: each operator a chapter introduces becomes yours to write, in Python, C++, or Metal, while MLX stays on the sidelines as the correctness oracle and the performance baseline your code is chasing.

Qwen3-4B is the right size for the exercise twice over. At 4B parameters it strains memory bandwidth, attention time, and cache capacity enough to behave like production, while staying small enough for fast local iteration. Its architecture also mirrors what production serving actually handles: 4-bit packed weights, BF16 activations, grouped-query attention, and QK normalization are the working vocabulary of modern inference optimization.

The course contract: tests, reference, and a public audit trail

Free courses usually fail on maintenance, not content, so the engineering hygiene here matters as much as the chapters. Setup is two commands, and a verification run confirms the environment before you invest a weekend:

pdm install -v
pdm run check-installation
pdm run test-refsol -- -- -k week_1

Three structural choices then carry the rest. Your implementations live in the tiny_llm package, and tiny_llm_ref holds a reference solution the tests grade against, so you always have a correct answer to diff your thinking with. The roadmap table tracks four columns per lesson, code, tests, rendered documentation, and an editorial audit pass, and it is updated rather than aspirational. And the benchmark appendix turns your gap to the optimized MLX baseline into a number you can track across weeks.

Read that roadmap before starting a chapter, because the course is honest about its own edges. Weeks 1 through 3 are the mature core; week 4 publishes incrementally, and a lesson marked as unreviewed documentation is a different commitment than an audited one. Few open courses expose their readiness state this plainly, and it changes how you plan your time around them.

That same table is where you check before building a weekend plan around a specific day, and the reference package doubles as study material when a kernel resists your first attempt.

Who should take Tiny-LLM

Three profiles get full value. Systems engineers who want to move into ML infrastructure, because the course is genuinely about memory hierarchies and scheduling with LLMs as the workload. Backend engineers running inference in production who want their tuning decisions grounded in mechanism. And students finishing deep-learning coursework who want a capstone harder than fine-tuning a model someone else serves.

The prerequisites are honest: comfortable Python, willingness to read C++ or Metal when performance chapters ask for it, and enough linear algebra that attention from matrix primitives is a refresher rather than new material. A CUDA GPU is explicitly not needed, which removes the most common hardware excuse.

Who should skip it is equally clear. If your goal is serving models this week rather than understanding serving, install a production engine and read our operator-side coverage instead. If you want model training rather than inference systems, a from-scratch training course serves that goal directly. Tiny-LLM is for the reader who wants the serving layer demystified at the source level, and it does not pretend to be anything else.

Do you need an expensive GPU to finish the course?

No, and that is a deliberate design decision rather than a limitation. The course runs on Apple Silicon because one machine with unified memory gives you the full stack: model weights, KV cache, and kernels all in one address space, with Metal available when an exercise wants a real GPU kernel. The trade is honest, though. You will not get CUDA-cluster lessons here, no tensor-parallel sharding across eight GPUs, no NCCL, because the hardware story is a laptop. For most learners that is the right trade: the mechanisms that transfer, batching, paging, cache pressure, kernel occupancy, are all visible on one machine, and the datacenter-scale topics build directly on them. Engineers who later need multi-GPU serving typically find that the single-node mental model from a course like this is the prerequisite, not the competitor, and interviewers for inference roles increasingly probe exactly this foundation, which makes the course a credible preparation path whether or not a job change is on the calendar.

What do you implement yourself versus reuse?

The rule is stated plainly: every operator a chapter introduces is one you write yourself, in Python, C++, or Metal, and the optimized MLX call is reserved for the oracle that grades you. The array layer, memory management, and the reference oracle stay from MLX; everything the course is about, attention variants, normalization kernels, quantized matvec, the caches, the scheduler, is yours. This split is what makes the course teach systems engineering rather than API fluency. It also means your solutions will be slower than the MLX baseline, and that gap is part of the pedagogy: the benchmark appendix quantifies how far your implementation sits from the optimized one, and closing that distance is where the performance intuition develops. If at some point you want to cheat and call the MLX op, the tests will not stop you, but you will have converted the course back into reading.

Is the week 4 agent material finished?

Partially, and the repo says so explicitly, which is more than most projects manage. Week 4 builds a bounded, validated coding-agent loop over a small workspace, and new days reach learners only after a review pass, with the first nine currently published. Those days already cover the interesting systems problems of agent serving: inspecting state, approved edits, a single validation command, effect receipts, a checkpoint-and-resume boundary, receipt-backed context compaction, an inspect-and-steer pause, deterministic evaluation of observable outcomes, and tokenizer and KV-prefix reuse for isolated steered branches. The remaining days are tracked in the roadmap. Two practical implications: check the table before planning a week around a specific day, and treat the agent week as applied serving engineering rather than an agents course, because the subject is the loop’s infrastructure, not prompt strategy.

What should you do after finishing the course?

The natural continuation is reading a production serving engine with implementer eyes. vLLM and SGLang make much more sense after week 3, because you have built small versions of their core abstractions and know why paged KV and continuous batching earned their place. Two directions reward the effort differently. For performance-minded engineers, the next layer is GPU-side work, where the Metal experience transfers conceptually to CUDA kernels and the optimization habits carry over directly. For infrastructure-minded engineers, the scheduler and admission ideas extend into distributed serving, where the single-node course ends and systems design begins. Either way, keep the repo: the reference solution and benchmarks make it a permanent local laboratory for testing ideas you meet later.

How does Tiny-LLM compare with reading vLLM source directly?

Both are legitimate ways to learn serving internals, and they fail differently. Reading vLLM source teaches you the real thing, with all its production scar tissue, but the codebase is large enough that the core ideas hide behind engineering scale, and you cannot conveniently change it to test understanding. Tiny-LLM inverts the trade: the engine is small enough to hold in your head, every mechanism is one you wrote, and experiments are cheap, but it is a course artifact, not a production system, and deliberately skips the hardening that makes real engines deployable. The efficient sequence uses both: build the mechanisms here, then read the production code to see what survival required. Engineers who only ever read large codebases usually learn the APIs; engineers who build first usually learn the physics.

What does the benchmark appendix actually teach?

Beyond grading, the benchmarks serve the pedagogical point that performance intuition comes from measurement, not folklore. Each exercise can be timed against the MLX baseline, and watching your numbers move as you replace a naive kernel with a cache or a packed operation is the moment the memory-hierarchy lessons click. Concretely, the appendix rewards a habit: record before and after for every optimization you attempt, and annotate why the change moved or did not move the number. Engineers who leave the course with that habit start tuning production engines with experiments instead of superstition, which is the difference the course is really selling. It also keeps you honest about the gap to vendor-optimized code, a gap that is educational rather than embarrassing, since the baseline is engineered by a team with far more time than a learner has. A useful closing exercise is to port your best kernel improvement back and forth between the reference and your version until the difference in numbers stops surprising you, because that stability of expectation, knowing roughly what a change should buy before running it, is exactly the intuition production tuning work is graded on.

How much time does each week really take?

The course does not publish hour estimates, so any numbers here are judgment from the material’s shape rather than documented commitments. Week 1 is the longest for most learners, because writing the model from array operations means implementing or refreshing every primitive at once, and engineers without recent kernels experience routinely underestimate it. Week 2 is more incremental, a cache here and a packed matvec there, each a focused session. Week 3 is conceptually the deepest, since batching and paged attention interact, and the scheduler exercises punish incomplete mental models. Week 4 reads as a series of bounded additions once the serving engine exists. A realistic overall figure for a working engineer is an evening or two per chapter plus debugging margin, stretched over one to two months, faster with MLX familiarity and slower without it. Plan buffers around weeks 1 and 3, and if you are pairing the course with a job, treat the chapters as the unit of progress rather than the days, since a half-finished kernel exercise tends to evaporate overnight and restart badly. Learners who keep notes per exercise, what broke, what fixed it, consistently report faster weeks, because week 2 and week 3 bugs are often week 1 misunderstandings resurfacing, and the notes make the connection findable.

Does the course cover quantization deeply enough to matter?

Deeply enough to be useful, narrowly enough to stay honest. Week 2 has you keeping W4 projection weights packed through a quantized matvec exercise, which teaches the real lesson of 4-bit serving: the win comes from never unpacking the weights into dense memory in the first place, and the cost lives in the kernel that multiplies from the packed representation. That is the mechanism behind the quantization options in every production engine, and implementing it once beats reading about it repeatedly. What the course does not attempt is a survey of quantization formats and their trade-offs across vendors, which is a moving target better followed in engine documentation. The right expectation: you finish knowing how one serious quantization scheme works at the kernel level, not which checkbox to pick in a config file.

If LLM serving currently feels like a bag of configuration options, Tiny-LLM is the cure: four weeks, one laptop, and a serving engine where every line is yours. Take week 1 slowly, do not skip the benchmark appendix, and finish before the agent week if your interest is purely serving, because weeks 1 through 3 are the complete story of an inference engine.