Local LLM Inference Architecture — From Silicon to Stack
A unified stack for running, routing, and observing LLMs — currently deployed on a laptop with RTX 3050 Ti, with planned expansions to K8s GPU nodes and Apple Silicon clusters.
✓ Ollama Running ✓ Observability Stack Planned: K8s GPU Planned: Mac Mini MLX
Current Deployment Status Live
✓ Running: Ollama
- Endpoint: localhost:11434
- qwen2.5-coder:1.5b — 986MB, 50 layers GPU
- qwen2.5-coder:7b — 4.7GB, CPU-only
- nomic-embed-text — 274MB embeddings
- GPU: RTX 3050 Ti, 4GB VRAM
✓ Running: Observability
- Prometheus: :9090 (metrics)
- Grafana: :3001 (dashboards)
- OTEL Collector: :4317/:4318 (traces)
- Loki: :3100 (logs)
- Tempo: :3200 (traces)
Planned: Expansion
- K8s GPU Node: Add NVIDIA GPU node
- vLLM: Tensor parallel inference
- Mac Mini: MLX cluster (TBD)
- LiteLLM: Multi-backend routing
Quick Start Commands
# Start Ollama
ollama serve &
# Start observability stack
cd ~/Documents/git/local-llm/observability && docker compose up -d
# Verify everything is running
curl http://localhost:11434/v1/models # Ollama
curl http://localhost:9090/-/healthy # Prometheus
curl http://localhost:3001/api/health # Grafana
The Complete Stack Architecture
End-to-End Data Flow
Parallelism Strategies How Models Split
Tensor Parallelism
Split each layer by width. Every node holds part of every layer. All nodes process the same token simultaneously.
All-reduce per layer Low latency Needs fast interconnect
Pipeline Parallelism
Split model by depth. Each node holds a contiguous slice of layers. Activations flow node-to-node in a pipeline.
P2P between stages Higher latency Less bandwidth
Expert Parallelism
MoE models only. Different experts live on different nodes. Router dispatches tokens. Only active experts communicate.
MoE-specific Scales well to 4 nodes Llama 4 Maverick
How Parallelism Splits a Transformer Block
KV-Cache Architecture llm-d
Hierarchical KV-Cache with Prefix-Aware Routing
MLX Mac Cluster Apple Silicon
Thunderbolt 5 Mesh — JACCL RDMA
MLX Distributed Stack
- JACCL — RDMA over Thunderbolt 5, 80 Gbps direct memory
- mlx.distributed — all_reduce, all_gather, send/recv primitives
- mlx-lm — sharded_load, stream_generate, —pipeline flag
- mlx.launch — SSH orchestration from your MacBook
Scaling Numbers
| Setup | Tok/s | TTFT | Max Model (4-bit) |
|---|---|---|---|
| 2x Mac Mini M4 16GB | ~12 | ~700ms | ~24 GB |
| 4x Mac Mini M4 16GB | ~20 | ~500ms | ~48 GB |
| 2x Mac Studio M5 Ultra | ~14 | ~740ms | ~400B MoE |
| 4x Mac Studio M5 Ultra | ~35 | ~400ms | 1T params |
Prefill/Decode Disaggregation llm-d on K8s
Separated Prefill and Decode Stages
Observability Stack Metrics · Traces · Logs
Signal Flow — From Code to Dashboards
docker-compose.yml
# Location:
~/Documents/git/local-llm/observability/
# Services:
prometheus:9090
grafana:3001
otel-collector:4317/:4318
loki:3100
tempo:3200
Grafana Credentials
# URL:
http://localhost:3001
# Username: admin
# Password: admin
# Pre-provisioned datasources:
Prometheus (default)
Tempo
Loki
OTEL (collector metrics)
Key Metrics What to Monitor
LLM Performance
| Metric | Type |
|---|---|
| champ_llm_ttft_seconds | Histogram |
| champ_llm_request_duration_seconds | Histogram |
| champ_llm_tokens_per_second | Gauge |
| champ_llm_input_tokens_total | Counter |
| champ_llm_output_tokens_total | Counter |
| champ_llm_cost_usd_total | Counter |
| champ_llm_errors_total | Counter |
Agent & Tool
| Metric | Type |
|---|---|
| champ_agent_step_duration_seconds | Histogram |
| champ_tool_call_duration_seconds | Histogram |
| champ_mcp_tool_call_duration_seconds | Histogram |
| champ_rag_query_duration_seconds | Histogram |
| champ_rag_result_count | Histogram |
| champ_hallucination_score | Histogram |
Infrastructure
| Metric | Source |
|---|---|
| DCGM_FI_DEV_GPU_UTIL | dcgm-exporter |
| DCGM_FI_DEV_FB_USED | dcgm-exporter |
| DCGM_FI_DEV_TEMP_GPU | dcgm-exporter |
| vllm:gpu_cache_usage_perc | vLLM /metrics |
| vllm:num_requests_running | vLLM /metrics |
| kv_cache_hit_rate | llm-d EPP |
Quick Reference Config Cheatsheet
Champ → LiteLLM (Single URL)
Champ sees one OpenAI-compatible endpoint. LiteLLM routes to everything.
champ.provider: openai-compatible
champ.openaiCompatible.baseUrl: http://localhost:4000
champ.openaiCompatible.model: coding
champ.openaiCompatible.apiKey: sk-champ
champ.autocomplete.model: fast
champ.indexing.embeddingProvider: ollama
LiteLLM → Model Fleet
One config file, every backend behind one port.
model_list:
- model_name: coding
litellm_params:
model: ollama/qwen2.5-coder:7b
api_base: http://localhost:11434
fallbacks: [{coding: [fast]}]
Enjoyed this post?
Get the next one in your inbox — only when I ship something worth reading.
Newsletter form not configured.
Or follow on Substack for the newsletter.
Comments via GitHub Discussions
Comments not configured. Set GISCUS env vars to enable.