Course outline▾
Week 1 · Bare-Metal Infrastructure & Advanced Containerization
- Day 1The AI Infrastructure Shift — Why the Model is Only 10% of the Production System
- Day 2Advanced Docker for AI — Multi-Stage Builds for Heavy Python Runtimes
- Day 3Hardware Acceleration in Docker — GPU Passthrough and Limits for Constrained Hardware
- Day 4Optimizing Model Weights — Quantization Basics (GGUF, AWQ) for Efficient Inference
- Day 5Local AI Runtimes — Deploying Ollama and vLLM for Bare-Metal Execution
- Day 6Containerizing AI Microservices — Building and Packaging APIs with FastAPI
- Day 7Docker Compose — Architecting a Multi-Container Database and Inference Stack Locally
Week 2 · Kubernetes Foundation for MLOps
- Day 8K3d & Lightweight Kubernetes: Running Multi-Node Clusters on 16GB of RAM
- Day 9Kubernetes Core for Machine Learning: Pods, Deployments, and Headless Services
- Day 10Persistent Volumes (PV/PVC): Managing Heavy Model Weights and Vector Stores
- Day 11StatefulSets for Vector Databases: Deploying Milvus or Qdrant in Kubernetes
- Day 12Auto-Scaling AI Inference: Using Horizontal Pod Autoscalers (HPA) for Spiky API Traffic
- Day 13Helm Charts: Packaging, Versioning, and Updating AI Deployments
- Day 14Week 2 Capstone: Deploying a Highly Available Local AI Application on K3d
Week 3 · High-Performance Networking & Security Mesh
- Day 15AI Network Bottlenecks: Transitioning from iptables to eBPF
- Day 16Cilium & eBPF: Sub-Millisecond Routing for Heavy AI Microservices
- Day 17Service Mesh 101: Installing Istio over Your Local Cluster
- Day 18Envoy Proxy as an AI Gateway: Load Balancing and Terminating LLM Prompts
- Day 19Zero-Trust mTLS: Encrypting Traffic Between Autonomous Agents
- Day 20API Rate Limiting & Egress Control: Preventing Agents from Exfiltrating Data
- Day 21Distributed Tracing: Debugging Agent Workflows with Jaeger and OpenTelemetry
Week 4 · Enterprise Agents & CI/CD Workflows
- Day 22Introduction to MLOps: Bridging AI Development with Continuous Integration
- Day 23GitOps with ArgoCD: Automated, Self-Healing AI Deployments
- Day 24Building Ephemeral AI Sandboxes: Secure Execution Environments for Agent Code
- Day 25Multi-Agent Orchestration Frameworks: Exploring Mastra, LangGraph, and CrewAI
- Day 26Tool-Calling in Production: Connecting Agents Securely to Internal Databases
- Day 27MLOps Guardrails: Evaluating Agents and Preventing Hallucinations in Real-Time
- Day 28Continuous Training Pipelines: Connecting Data Ingestion to Automated Model Retraining
- Day 29Managing Technical Debt and Infrastructure Drift in Enterprise AI
- Day 30The AI-Native Platform: Scaling from a Sandbox Environment to Global Cloud Architecture
Day 7: Docker Compose — Architecting a Multi-Container Database and Inference Stack Locally
2026-11-13 · 8 min read
A production-ready RAG platform is not one script. It needs several specialised services on a shared internal network. Docker Compose is the declarative, infrastructure-as-code way to assemble them on a single host, and it closes Week 1 by pulling together everything so far.
The topology
Three services make up the stack:
- State store: a Qdrant vector database holding the embeddings.
- Inference engine: Ollama (or vLLM) running the model.
- Application and gateway: the FastAPI or Streamlit service from Day 6 that orchestrates retrieval and generation.
The Compose file
name: ai-stack
networks:
ai-internal-net:
driver: bridge
volumes:
qdrant_storage:
ollama_storage:
services:
vector-store:
image: qdrant/qdrant:v1.9.0
networks: [ai-internal-net]
volumes:
- qdrant_storage:/qdrant/storage
healthcheck:
# A TCP probe avoids needing curl in the image. Adjust to your image version.
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/6333' || exit 1"]
interval: 10s
timeout: 5s
retries: 10
inference-engine:
image: ollama/ollama:latest # pin a tested tag in production
networks: [ai-internal-net]
volumes:
- ollama_storage:/root/.ollama
deploy:
resources:
limits:
memory: 10G # leave about 6 GB of a 16 GB host free
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 15s
timeout: 10s
retries: 10
ai-gateway:
build: ./gateway
networks: [ai-internal-net]
ports:
- "80:8000" # the only port published to the host
environment:
VECTOR_DB_HOST: vector-store
VECTOR_DB_PORT: "6333"
LLM_HOST: inference-engine
LLM_PORT: "11434"
depends_on:
vector-store:
condition: service_healthy
inference-engine:
condition: service_healthy
Start it with docker compose up -d --build, check it with docker compose ps, and stop it with docker compose down. Add -v to down only when you want the volumes deleted too.
Three principles
1. Service discovery by name. Inside a Compose network every service name is a DNS name. Use it, and never hard-code IP addresses or localhost: container IPs change on restart, and localhost points at the calling container itself.
2. Healthchecks and dependency ordering. depends_on with condition: service_healthy makes the gateway wait until both services actually pass their checks, so it never queries a database or model server that is still starting.
3. Volume decoupling. Keep persistent data in named volumes, outside the containers. You can then upgrade, restart or rebuild the stack without losing the vector index or re-downloading model weights.
Two practical notes. The memory limit under deploy.resources is honoured by current docker compose, but check it with docker stats on your setup. And the healthcheck commands depend on what each image ships, so confirm they work with docker compose ps showing healthy.
That completes Week 1: lean images, GPU access, quantized weights, dedicated runtimes, a validated API and a stack that starts in the right order. Next week moves it all onto Kubernetes, starting with a lightweight cluster on a 16 GB machine.
Run it yourself
This is the exact example from the video. Docker Compose itself reads this file, and a short check prints what it found. Nothing is started, and the gateway image is a placeholder.
name: ai-stack
services:
vector-store:
image: qdrant/qdrant:v1.9.0
volumes: [qdrant_data:/qdrant/storage]
inference-engine:
image: ollama/ollama:latest
volumes: [ollama_data:/root/.ollama]
ai-gateway:
image: ai-gateway:dev
ports: ["80:8000"]
environment:
VECTOR_DB_HOST: vector-store
LLM_HOST: inference-engine
depends_on: [vector-store, inference-engine]
volumes: {qdrant_data: {}, ollama_data: {}}
Helper file check.py, used by the run:
import json, subprocess
raw = subprocess.check_output(["docker", "compose", "config", "--format", "json"])
c = json.loads(raw)
svc = c["services"]
print("services:", len(svc))
for n, s in svc.items():
for p in s.get("ports", []):
print("published:", n, p["published"] + "->" + str(p["target"]))
g = svc["ai-gateway"]
print("db host:", g["environment"]["VECTOR_DB_HOST"])
for d in sorted(g["depends_on"]):
print("waits for:", d)
print("volumes:", ", ".join(sorted(c["volumes"])))
Run it:
python check.py
Output I got while recording the video:
services: 3
published: ai-gateway 80->8000
db host: vector-store
waits for: inference-engine
waits for: vector-store
volumes: ollama_data, qdrant_data
Coming Up Next
Day 8: K3d and lightweight Kubernetes: running multi-node clusters on 16 GB of RAM.
#DockerCompose #Microservices #RAG #SystemDesign #DevOps #VectorDatabase #AIInfrastructure #SoftwareEngineering