← Back to DevBytes

Building a Docker Image for GPU-Accelerated LLM Serving

Building a Docker Image for GPU-Accelerated LLM Serving

Serving Large Language Models (LLMs) in production requires more than just loading weights into memory — it demands optimized inference runtimes, careful dependency management, and access to GPU hardware. Docker has become the de facto standard for packaging these workloads, but GPU-accelerated containers introduce unique challenges around driver compatibility, CUDA versions, and runtime configuration. This tutorial walks through building a production-ready Docker image for serving an LLM with GPU acceleration, using vLLM as the inference engine.

What It Is

A GPU-accelerated LLM serving container is a Docker image that bundles the model-serving framework, its dependencies, CUDA libraries, and optionally the model weights themselves, all configured to run on NVIDIA GPU hardware. Unlike CPU-only containers, these images must be built on top of a CUDA-enabled base image and run with the NVIDIA Container Toolkit, which exposes host GPU devices to the container runtime.

The typical stack includes:

Why It Matters

Packaging LLM serving into a Docker image delivers several concrete benefits. First, it guarantees reproducibility — the same CUDA, PyTorch, and vLLM versions that worked in staging will run identically in production. Second, it simplifies deployment across environments, from a single workstation with one GPU to a Kubernetes cluster with multiple nodes. Third, it enables clean separation between infrastructure and application code, letting platform teams manage GPU node pools while ML teams iterate on model versions independently.

Without containerization, teams frequently hit "dependency hell" when moving from development to production: mismatched CUDA versions, missing cuDNN libraries, or Python package conflicts that silently degrade inference performance. A well-built Docker image eliminates these issues by freezing the entire runtime environment.

How to Use It

1. Prerequisites

Before building the image, ensure your host machine has the NVIDIA driver installed and the NVIDIA Container Toolkit configured. Verify with:

nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

If both commands print the GPU table, your environment is ready.

2. The Dockerfile

Below is a complete Dockerfile that builds a vLLM serving image. It uses a multi-stage approach to keep the final image lean while ensuring all GPU libraries are present.

# syntax=docker/dockerfile:1.6

FROM nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04 AS base

ENV DEBIAN_FRONTEND=noninteractive \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    HF_HOME=/models

RUN apt-get update && apt-get install -y --no-install-recommends \
    python3.11 python3.11-venv python3-pip curl ca-certificates git \
    && rm -rf /var/lib/apt/lists/*

RUN python3.11 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

RUN pip install --upgrade pip wheel

# ---- Builder stage ----
FROM base AS builder

RUN pip install vllm==0.6.3 torch==2.4.0 transformers==4.45.0

# ---- Runtime stage ----
FROM base AS runtime

COPY --from=builder /opt/venv /opt/venv

RUN mkdir -p /models /app
WORKDIR /app

COPY serve.py /app/serve.py

EXPOSE 8000

ENTRYPOINT ["python", "/app/serve.py"]

3. The Serving Script

The serve.py script wraps vLLM's engine in a lightweight FastAPI application that exposes an OpenAI-compatible chat endpoint.

import os
from fastapi import FastAPI
from pydantic import BaseModel
from vllm import LLM, SamplingParams

MODEL_ID = os.environ.get("MODEL_ID", "meta-llama/Meta-Llama-3-8B-Instruct")
PORT = int(os.environ.get("PORT", "8000"))

app = FastAPI()
llm = LLM(model=MODEL_ID, tensor_parallel_size=1, gpu_memory_utilization=0.9)

class ChatRequest(BaseModel):
    prompt: str
    max_tokens: int = 512
    temperature: float = 0.7

@app.post("/v1/chat/completions")
def chat(req: ChatRequest):
    params = SamplingParams(
        max_tokens=req.max_tokens,
        temperature=req.temperature
    )
    outputs = llm.generate([req.prompt], params)
    return {"choices": [{"text": outputs[0].outputs[0].text}]}

4. Building and Running the Image

Build the image with a descriptive tag so you can track versions:

docker build -t llm-server:vllm-0.6.3-llama3-8b .

Run the container, passing the GPU and the Hugging Face token as environment variables:

docker run --gpus all \
  -p 8000:8000 \
  -e MODEL_ID=meta-llama/Meta-Llama-3-8B-Instruct \
  -e HF_TOKEN=hf_your_token_here \
  -v /mnt/models:/models \
  llm-server:vllm-0.6.3-llama3-8b

Test the endpoint with curl:

curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Explain quantum computing in one sentence.", "max_tokens": 100}'

Best Practices

Conclusion

Building a Docker image for GPU-accelerated LLM serving is a foundational skill for any team shipping generative AI to production. By starting from a CUDA base image, layering in a proven inference engine like vLLM, and following best practices around version pinning, multi-stage builds, and weight management, you create a portable, reproducible artifact that runs identically on a developer laptop and a multi-GPU cluster. The Dockerfile and serving script in this tutorial provide a solid starting point that you can adapt to different models, engines, and hardware configurations as your serving needs evolve.

— Ad —

Google AdSense will appear here after approval

← Back to all articles