
Running AI models, coding agents, and vector databases on bare metal leads to dependency hell, silent driver mismatches, and messy system clutter. Containerizing your AI workflows with Docker separates your host environment from volatile Python packages, CUDA libraries, and API keys. The key is decoupling serving runtimes from model weights, configuring GPU passthrough correctly with NVIDIA Container Toolkit, and enforcing strict resource limits to avoid silent out-of-memory (OOM) kernel kills.
| Deployment Metric | Bare Metal / Virtualenv | Docker Containerized Stack |
|---|---|---|
| Environment Isolation | No (Shares host CUDA/Python packages) | Yes (Complete filesystem & dependency sandbox) |
| GPU Passthrough Setup | Manual CUDA pathing per project | NVIDIA Container Toolkit (--gpus all) |
| Model Weight Storage | Mixed into project directory | External Host Volume / S3 Mount |
| Deployment Reproducibility | Low (Host OS version drift) | High (Immutable Dockerfile layer build) |
| OOM Crash Prevention | Unpredictable (Host RAM exhaustion) | Enforced (limits.memory & shared memory buffers) |
Traditional web applications are lightweight and stateless. AI workloads are the exact opposite: they depend on multi-gigabyte binary blobs, heavy C++ extensions, and tight GPU kernel integrations. If you approach an AI container like a standard Node.js or Flask app, you end up with 30GB Docker images and agonizingly slow cold-start deployment times.
Docker GPU passthrough is not full hardware virtualization like QEMU or KVM. Instead, the Linux kernel manages device files while the NVIDIA Container Toolkit injects the host GPU driver libraries into the container namespace at runtime. Your host OS supplies the underlying nvidia.ko kernel driver, while the container supplies the CUDA toolkit and application binary. This requires one fundamental rule: the host driver version determines the maximum CUDA version available inside your container.
Containers cannot see host graphics cards by default. To enable hardware acceleration for local LLMs, vLLM, or PyTorch, you must install the NVIDIA Container Toolkit on your Linux host. Do not attempt to install NVIDIA GPU drivers inside your Dockerfile; drivers belong exclusively on the host system.
Configure the container runtime by executing the toolkit utility to update your Docker daemon configuration:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart dockerOnce restarted, verify GPU access inside an isolated container with a single command:
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smiIf the nvidia-smi output displays your GPU hardware and driver version, containerized GPU passthrough is functional. You can isolate specific cards in multi-GPU machines using the device flag, such as docker run --gpus '"device=0,1"'.
Building small, fast-starting containers requires layer optimization and multi-stage builds. Always start from runtime base images rather than devel images unless your workflow requires active compilation during container startup. Multi-stage builds separate build tools from runtime execution, reducing image footprint by up to 70 percent.
# Stage 1: Build virtual environment
FROM python:3.11-slim AS builder
WORKDIR /app
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Stage 2: Runtime environment
FROM python:3.11-slim AS runner
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
ENV PYTHONUNBUFFERED=1
RUN useradd -m -u 1000 aiuser
USER aiuser
COPY --chown=aiuser:aiuser . /app
EXPOSE 8000
CMD ["python", "app.py"]Never bake large model weights into your Docker image layer. When weights are stored inside the container image, every code change invalidates the cache and forces a multi-gigabyte re-upload to your registry. Store model weights in external volume mounts or download them to host caches via environment variables like HF_HOME=/cache/hf.
The host kernel OOM killer is the most common reason AI containers crash silently under load. Machine learning frameworks like PyTorch and vLLM utilize shared memory for inter-process communications and tensor parallelism. Docker default configurations limit container shared memory (/dev/shm) to 64MB, causing DataLoader worker crashes or NCCL failures during high-throughput inference.
Fix shared memory starvation by passing --shm-size=8g or using --ipc=host during docker run commands. Additionally, separate your host RAM allocation budget from your GPU VRAM allocation. Host RAM must account for expanding context length KV-caches, tokenization buffers, and pre-fetched data streams.
Production AI features rarely exist in isolation. A standard vibe coding workflow or local RAG pipeline involves an LLM engine (Ollama or vLLM), a vector database (Qdrant or ChromaDB), and an application server. Docker Compose v2 lets you orchestrate the full stack cleanly with hardware reservations.
services:
ollama:
image: ollama/ollama:latest
container_name: ollama-engine
ports:
- "11434:11434"
volumes:
- ollama_storage:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
limits:
memory: 16g
cpus: "4.0"
restart: unless-stopped
qdrant:
image: qdrant/qdrant:latest
container_name: vector-db
ports:
- "6333:6333"
volumes:
- qdrant_storage:/qdrant/storage
restart: unless-stopped
volumes:
ollama_storage:
qdrant_storage:AI agent containers frequently execute generated code, read files, or interact with external tool endpoints. Securing these environments requires applying strict container boundaries:
USER 1000 inside your Dockerfile so compromised model processes cannot modify host mount privileges.--read-only flag with tmpfs /tmp:size=1g to prevent arbitrary runtime code modifications.--cap-drop=ALL and re-grant only network capabilities if serving APIs.Building reliable containerized AI environments comes down to five fundamental practices:
-v /data/models:/models) so container images stay lightweight and fast to pull.--shm-size=8g to prevent PyTorch and NCCL inter-process crashes during inference.