Naar de inhoud
NLEN
Illustratie: Lokale modellen in Docker: containers zonder gedoe

Lokale modellen in Docker: containers zonder gedoe

Door Ivo Donker — samengesteld met AI-ondersteuning (Claude & Gemini)

In de route van lokaal taalmodellen draaien bevinden we ons hier bij de stap installeren en isoleren, direct volgend op de hardwarekeuze en voorafgaand aan het koppelen van documenten of het inrichten van een netwerkserver. Wie een taalmodel direct op het basissysteem installeert, loopt vroeg of laat tegen conflicterende CUDA-versies, vervuilde Python-omgevingen of onbedoelde netwerkblootstelling aan. Door runtime-engines zoals Ollama of vLLM in Docker-containers te draaien, blijft het basissysteem schoon, voorspelbaar en reproduceerbaar.

Voordat we containers opstarten, is het essentieel om te controleren of de fysieke componenten toereikend zijn voor de beoogde werklast. Raadpleeg het overzicht over welke hardware nodig is om LLM's lokaal te draaien om te verifiëren of het beschikbare videogeheugen voldoende bandbreedte en capaciteit biedt voor de gewenste modelparameters. In deze handleiding bouwen we een stabiele containeromgeving op die zonder wrijving communiceert met de onderliggende grafische kaart en leggen we uit hoe netwerk-, geheugen- en opslaginstellingen geoptimaliseerd moeten worden.

Hardware-ondergrens en testconfiguratie

Voor het draaien van gecontaineriseerde modellen hanteren we een heldere hardwarematige ondergrens. Containers introduceren nagenoeg geen rekenoverhead voor de matrixberekeningen zelf, maar vereisen wel voldoende stabiel hostgeheugen om vastlopers tijdens de initialisatie van containers en geheugentoewijzing te voorkomen. De onderstaande tabel toont de minimale configuratie en de aanbevolen praktijkopstelling waarop de instructies in deze gids zijn gevalideerd (getest per augustus 2026 met Docker Engine 27.x en NVIDIA Container Toolkit 1.16.x).

Component Minimale ondergrens Aanbevolen testopstelling
Besturingssysteem Linux x86_64 (Ubuntu 22.04 LTS) Ubuntu 24.04 LTS of Debian 12
Werkgeheugen (RAM) 16 GB DDR4 32 GB DDR5
Grafische kaart (VRAM) NVIDIA GPU met 8 GB VRAM NVIDIA RTX 4080/3090 (16–24 GB VRAM)
Opslagruimte 50 GB vrije SSD-ruimte 250 GB NVMe SSD (PCIe 4.0)
Getest model Llama-3.1-8B-Instruct (Q4_K_M) Mistral-Nemo-12B of Qwen2.5-14B (Q4/Q8)

Draait het systeem op een bare-metal Linux-distributie, raadpleeg dan eerst de specifieke stappen over lokale LLM's draaien op Linux om de juiste eigen NVIDIA-stuurprogramma's op kernelniveau te borgen voordat Docker wordt ingericht. Zonder werkende drivers op de host kan geen enkele container communiceren met de grafische versneller.

Waarom containerisatie voor lokale AI?

Het rechtstreeks compileren van inferentie-software zoals llama.cpp of het opzetten van virtuele Python-omgevingen voor geavanceerde servers leidt in de praktijk vaak tot afhankelijkheidsconflicten. Een automatische update van de systeem-CUDA-toolkit, een wijziging in glibc of een botsing tussen PyTorch-versies kan een voorheen werkende installatie onklaar maken. Docker lost dit fundamenteel op door de complete runtime inclusief bibliotheken, CUDA-koppelingen en API-endpoints in een onveranderlijke laag in te kapselen.

Een tweede belangrijk voordeel is reproduceerbaarheid en modulaire opbouw. Door containerisatie kan men binnen enkele seconden wisselen tussen verschillende inferentie-engines (zoals Ollama voor dagelijks gebruik en vLLM voor batchverwerking met hoge doorvoer) zonder dat configuratiebestanden elkaar in de weg zitten. Bovendien kunnen aanvullende diensten — zoals vector-databases, proxy-servers en webinterfaces — op een geïsoleerd intern netwerk draaien zonder dat poorten onbedoeld openstaan voor andere apparaten.

GPU-doorvoer met NVIDIA Container Toolkit

Standaard hebben Docker-containers geen rechtstreekse toegang tot de hardwarematige GPU-kernen van het hostsysteem. De container ziet alleen de geëmuleerde CPU en het toegewezen RAM-geheugen. Om de grafische kaart beschikbaar te maken in containers, levert NVIDIA de Container Toolkit (voorheen bekend als nvidia-docker2). Deze toolkit fungeert als een runtime-hook tussen Docker en het stuurprogramma van de host, waardoor CUDA-aanroepen direct worden doorgesluisd naar de hardware zonder noemenswaardig snelheidsverlies.

De installatie verloopt via de officiële package repository van de Linux-distributie. Na het toevoegen van de ondertekeningssleutel configureert het commando de Docker-daemon automatisch:

# Voeg de NVIDIA Container Toolkit repository toe
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# Installeer het pakket en herstart Docker
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Om te verifiëren dat de GPU-doorvoer vlekkeloos functioneert voordat er zware containers worden gebouwd, draaien we een snelle testcontainer aan de hand van de officiële CUDA-image:

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

Toont de terminal de vertrouwde tabel met de GPU-naam, het VRAM-gebruik en de driverversie, dan is de bridge tussen de host-kernel en Docker operationeel. Geeft de container een foutmelding zoals could not select device driver, dan is de Docker-service na configuratie niet herstart of ontbreekt de kernelmodule op het hostsysteem.

Containeropslag en persistente volumes

Taalmodellen variëren in bestandsgrootte van 4 GB tot ruim 40 GB per gewichtenbestand. Een veelgemaakte fout is het downloaden van modellen binnen de tijdelijke schrijf-laag van een container. Zodra de container wordt vernieuwd of vervangen door een nieuwere image-versie, verdwijnen alle binnengehaalde modellen en moet de volledige dataset opnieuw worden gedownload.

Om dataverlies te voorkomen, koppelen we altijd een extern hostvolume aan de interne opslaglocatie van de engine. Voor Ollama is dat standaard /root/.ollama. Zorg er tevens voor dat het bestandssysteem op de host geformatteerd is als ext4 of XFS; netwerkshares via NFS of SMB introduceren te veel latency en kunnen leiden tot I/O-fouten tijdens het inladen van de modelgewichten in het videogeheugen.

Begrip van modelstructuren en bestandsgroottes is hierbij essentieel. In het artikel over kwantisatie uitgelegd voor lokale hardware wordt in detail uiteengezet waarom een 4-bits of 8-bits variant aanzienlijk minder opslagruimte en videogeheugen vergt dan ongecomprimeerde FP16-modellen, waardoor meerdere modellen probleemloos op dezelfde SSD passen.

Docker Compose: Ollama en Open WebUI samenvoegen

In plaats van losse docker run-commando's met lange vlaggen te gebruiken, bundelen we de diensten in een overzichtelijk docker-compose.yml-bestand. Hierin definiëren we zowel de rekenengine (Ollama) als een gebruiksvriendelijke interface (Open WebUI) op een intern afgeschermd bridge-netwerk.

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama-core
    restart: unless-stopped
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - ./ollama_data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    volumes:
      - ./webui_data:/app/backend/data
    depends_on:
      - ollama

Start de omgeving op de achtergrond met een enkel commando:

docker compose up -d

Vervolgens kunnen we binnen de draaiende container een model binnenhalen via de commandolijn:

docker exec -it ollama-core ollama run llama3.1:8b

Door de poorttoewijzing 127.0.0.1:3000 is de interface uitsluitend bereikbaar vanaf de lokale machine. Open de browser en navigeer naar http://localhost:3000 om direct interactief aan de slag te gaan.

Geavanceerd resourcebeheer: VRAM, Shared Memory en OOM-preventie

Wanneer zware modellen binnen containers draaien, kan een plotselinge piek in contextlengte of een gelijktijdige aanvraag leiden tot geheugentekorten. Als het fysieke RAM of VRAM volloopt, grijpt de Linux Out-Of-Memory (OOM) killer in, wat resulteert in het abrupt afsluiten van de Docker-container.

Voor inferentie-engines die gebruikmaken van shared memory (zoals vLLM of multi-process pipelines met PyTorch) is de standaard Docker shm-size van 64 MB zwaar ontoereikend. Wanneer de interne processen data uitwisselen via gedeeld geheugen, crasht de engine onmiddellijk met een bus error. Pas daarom altijd de shm_size aan in het compose-bestand:

    shm_size: '16gb'
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536

Daarnaast is het raadzaam om via omgevingsvariabelen het gedrag van de engine te begrenzen. Bij vLLM voorkomt de parameter --gpu-memory-utilization 0.90 dat de container 100% van het VRAM reserveert, waardoor er ruimte overblijft voor de CUDA-overhead en eventuele display-servers op de host.

Netwerkisolatie en poortbeveiliging

Een van de grootste risico's bij het lokaal hosten van taalmodellen is het onbedoeld blootstellen van onbeveiligde API-endpoints. Engines zoals Ollama, llama.cpp server en vLLM hebben standaard geen ingebouwde authenticatie, encryptie of rate-limiting op hun poorten. Iedereen op hetzelfde lokale netwerk die het IP-adres scant, kan willekeurige prompts sturen of modellen inladen.

Door in de Docker-configuratie expliciet 127.0.0.1:11434:11434 te specificeren in plaats van de notatie 11434:11434, dwingen we af dat de socket alleen luistert op het loopback-adres. Verkeer vanaf andere netwerkinterfaces wordt direct door de kernel geweigerd. Wie de container wel veilig wil delen binnen een huishouden of klein kantoor, kan de richtlijnen raadplegen over AI veilig gebruiken thuis met praktische tips voor gezinnen om ongewenste toegang en privacylekken op het thuisnetwerk te voorkomen.

Meetmethoden voor inferentietijd en containerprestaties

Om te verifiëren of de containeropstelling optimaal presteert en de GPU daadwerkelijk volledig wordt benut, is een gestructureerde meetmethode noodzakelijk. We meten twee cruciale statistieken: Time To First Token (TTFT, de latentie tot het begin van het antwoord) en de continue doorvoersnelheid in tokens per seconde (tok/s).

Via een gestandaardiseerd curl-commando met tijdmeting kunnen we het API-endpoint van de container rechtstreeks bevragen:

curl http://127.0.0.1:11434/api/generate -d '{
  "model": "llama3.1:8b",
  "prompt": "Schrijf een beknopte samenvatting van 100 woorden over kwantummechanica.",
  "stream": false
}' | jq '{
  total_duration_ms: (.total_duration / 1000000),
  load_duration_ms: (.load_duration / 1000000),
  eval_count: .eval_count,
  eval_rate_tok_per_sec: (.eval_count / (.eval_duration / 1000000000))
}'

Tijdens het uitvoeren van deze meting monitoren we op de host de GPU-belasting via watch -n 0.5 nvidia-smi. Als het VRAM-gebruik tijdens inferentie nauwelijks stijgt en de CPU naar 100% schiet, draait de container per abuis in CPU-fallback modus door een ontbrekende GPU-reservering in de containerdefinitie.

Randgevallen en probleemoplossing

In de praktijk treden regelmatig specifieke fouten op bij het combineren van Docker en AI-workloads. Hieronder behandelen we de meest voorkomende randgevallen en hun directe oplossingen:

Privacy en datastromen binnen containers

Een fundamentele reden om modellen via Docker op eigen hardware te draaien, is het waarborgen van volledige gegevenssoevereiniteit. Zodra de container-images zijn binnengehaald en de gewichten lokaal op de schijf staan, functioneert de inferentiestack volledig autonoom zonder uitgaande netwerkverbindingen.

Er worden geen prompts, documentfragmenten, embeddings of gegenereerde antwoorden doorgestuurd naar externe clouddiensten. Meer achtergrond over de juridische en operationele waarborgen rond lokale gegevensverwerking is te vinden in het overzicht over privacyvriendelijk AI gebruiken. Met de onderstaande netwerkinspectie kan te allen tijde worden geverifieerd dat er geen actieve externe sockets openstaan tijdens inferentie:

# Inspecteer actieve netwerkverbindingen binnen de container
docker exec -it ollama-core ss -tulpn

Betrouwbaarheid en automatisering in productieworkflows

Zodra de lokale containerinfrastructuur stabiel operationeel is, vormt deze het ideale fundament voor complexere automatiseringen, zoals lokale software-agents die zelfstandig stappenplannen uitvoeren, tools aanroepen en databases bevragen. Voor wie wil doorgroeien naar het ontwerpen van complete agent-systemen biedt het leertraject over AI agent engineer worden in 2026 een gestructureerde aanpak voor orchestratie, geheugenbeheer en tool-gebruik.

Ondanks een vlekkeloze en geïsoleerde containeromgeving blijft het onderliggende taalmodel vatbaar voor hallucinaties, verouderde feiten of logische redeneerfouten. Het scheiden van de infrastructuurlaag en de validatielaag is daarom cruciaal voor een betrouwbaar eindresultaat. Raadpleeg de methodologie over AI-antwoorden fact-checken om geautomatiseerde validatiestappen in te bouwen voordat modeloutputs blindelings worden doorgevoerd in bedrijfsprocessen of databases.

Conclusie en onderhoudsadvies

Het draaien van lokale taalmodellen in Docker-containers elimineert configuratieconflicten op het basissysteem en resulteert in een robuuste, reproduceerbare AI-opstelling. Door het strikt scheiden van opslagvolumes, het binden van poorten aan het loopback-adres en het correct inrichten van de NVIDIA Container Toolkit blijft het systeem stabiel onder zware werklasten.

Houd bij periodiek onderhoud rekening met de schijfruimte die verouderde Docker-images innemen. Nieuwe versies van runtime-images kunnen na verloop van tijd tientallen gigabytes aan ongebruikte tussenlagen achterlaten. Voer maandelijks een opschoning uit met docker image prune om ongebruikte bouwlagen veilig te verwijderen, terwijl de gedownloade modelbestanden in de gekoppelde volumes onaangetast blijven.