- Linux (Ubuntu/Debian) или Windows 11 с WSL2 и Docker Desktop
- Docker Engine / Docker Desktop (конкретную версию сверьте с документацией вашей ОС)
- драйвер NVIDIA и утилита nvidia-smi на хосте
- NVIDIA Container Toolkit (пакет nvidia-container-toolkit)
- образ локальной LLM — тег и имя сверьте с документацией образа
- 20+ ГБ свободного места под образы и веса моделей
Термины на одну фразу: GPU — видеокарта, VRAM — её видеопамять, CUDA — слой, через который программа считает на GPU NVIDIA, образ — упакованный архив с приложением, контейнер — запущенный экземпляр образа, проброс портов — правило вида -p <порт-хоста>:<порт-контейнера>.

nvidia-smi показывает таблицу с именем GPUdf -hss -ltnpdocker login (если образ приватный)docker --version
docker info --format '{{.ServerVersion}} {{.Driver}}'
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv
nvidia-ctk --version
Что сделать: выполнить четыре команды выше и сохранить их вывод.
Зачем: это база для сравнения. Когда контейнер упадёт, вы будете точно знать, сколько VRAM было свободно и какой драйвер стоит, а не гадать.
Пример: nvidia-smi --query-gpu=... вернёт строку вида NVIDIA GeForce RTX 4090, 24564 MiB, 550.xx — то есть 24 ГБ VRAM. Если команда отвечает command not found, драйвера нет, и ни одна строка ниже не заработает.
Как проверить: docker info | grep -i runtime должен перечислить runc и (после установки toolkit) nvidia. Пустой список или только runc — toolkit не подключён, идите в Шаг 1.
Сколько VRAM нужно под конкретный размер модели и квантизацию — разобрано в гайде «Сколько RAM и VRAM нужно для локальной ИИ-модели»; сверьтесь с ним перед выбором тега образа.
Что сделать: зарегистрировать NVIDIA-рантайм в Docker и прогнать тестовый контейнер.
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker info | grep -i nvidia
docker run --rm --gpus all ubuntu nvidia-smi
Зачем: Docker по умолчанию не знает про видеокарту, он умеет только runc. nvidia-ctk дописывает в конфиг демона рантайм nvidia, который при старте контейнера прокидывает внутрь устройства GPU и библиотеки драйвера. Флаг --gpus all говорит: «дай этому контейнеру все видеокарты».
Пример: успешный запуск — внутри контейнера печатается та же таблица, что и на хосте:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI ... Driver Version: 550.xx CUDA Version: 12.x |
| 0 NVIDIA GeForce RTX 4090 ... 24564MiB / 24564MiB |
+-----------------------------------------------------------------------------+
Как проверить: вывод docker run --rm --gpus all ubuntu nvidia-smi содержит имя вашей GPU. Если вместо этого приходит could not select device driver "" with capabilities: [[gpu]] — рантайм не подключён, разбор в разделе проблем.
Ограничить одной картой на многоголовой машине: --gpus '"device=0"' — строка в кавычках, иначе Docker не разберёт синтаксис.

Что сделать: собрать команду docker run из шести групп флагов.
| Флаг | Что делает | Зачем в LLM |
|---|---|---|
| `--gpus all` | прокидывает GPU | без него модель уйдёт в CPU и будет отвечать минутами |
| `--shm-size=8g` | увеличивает `/dev/shm` | по умолчанию Docker даёт мало разделяемой памяти, и загрузчик весов падает на её нехватке (точное значение смотрите в документации своей версии Docker) |
| `--memory` / `--memory-swap` | лимит RAM контейнера | чтобы «прожорливый» процесс не убил хост целиком |
| `-p 127.0.0.1:8080:8080` | публикует порт | снаружи видно только с этой машины |
| `-v <путь>:/root/.cache` | монтирует кэш | веса не скачиваются заново после `docker rm` |
| `-e <ПЕРЕМЕННАЯ>=<значение>` | env-настройки | имена переменных сверьте с документацией образа |
docker run -d \
--name llm \
--gpus all \
--shm-size=8g \
--memory=32g --memory-swap=32g \
-p 127.0.0.1:8080:8080 \
-v "$HOME/.cache/llm:/root/.cache" \
<llm-образ>:<тег>
docker ps --filter name=llm
docker logs -f llm
nvidia-smi
Зачем: каждый флаг закрывает свой класс ошибок. -d — в фоне, --name — чтобы обращаться по имени, -v — чтобы кэш весов пережил пересоздание контейнера.
Пример: docker ps показывает Up 30 seconds и 0.0.0.0:8080->8080/tcp (или 127.0.0.1:8080->8080/tcp при привязке к localhost). nvidia-smi в колонке процессов показывает процесс внутри контейнера — значит, модель реально на GPU, а не на CPU.
Как проверить: docker logs llm заканчивается строкой вида listening on 0.0.0.0:8080 (формулировка зависит от образа). Если в логах loading model висит и не двигается, а nvidia-smi показывает 0 MiB занятой VRAM — модель грузится в RAM, проверьте --gpus.

Что сделать: проверить порт на хосте, затем достучаться до API.
ss -ltnp | grep 8080
curl -i http://127.0.0.1:8080/
curl -s http://127.0.0.1:8080/v1/models
docker exec -it llm sh -c 'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/'
Зачем: три уровня проверки отделяют три разные поломки. ss показывает, слушает ли порт хост. curl с хоста — работает ли проброс. curl изнутри контейнера — жив ли сам сервер.
Пример: ss -ltnp | grep 8080 печатает LISTEN 0 4096 127.0.0.1:8080 ... users:(("docker-proxy",...)). curl -i возвращает HTTP/1.1 200 OK или 404 — оба ответа означают, что связка «порт → контейнер» работает; 404 просто значит, что корневой путь не отдаёт контент.
Как проверить: успех — это одновременно: контейнер в статусе Up, nvidia-smi показывает занятую VRAM вашим процессом, и HTTP-ответ приходит. Конкретные пути API (/v1/models, /api/tags и т. п.) зависят от образа — сверьтесь с его документацией.
Наружу порт пока не открыт: 127.0.0.1 доступен только локально. Как выставить сервис в интернет через туннель или reverse proxy, не трогая роутер, — в гайде «Как открыть доступ к локальной LLM из интернета без проброса портов».
Проблема. Контейнер падает сразу после старта, GPU не виден
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].nvidia — не установлен NVIDIA Container Toolkit либо демон не перезапущен после nvidia-ctk runtime configure.nvidia-container-toolkit, затем sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker info | grep -i nvidia
Проверка: в docker info появилась секция Runtimes: nvidia runc.
Проблема. Модель не влезает в видеопамять
torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate ... MiB или CUDA error: out of memory nvidia-smi
docker rm -f llm
docker run -d --name llm --gpus '"device=0"' --shm-size=8g -p 127.0.0.1:8080:8080 <llm-образ>:<тег>
Дальше: уменьшить размер контекста в настройках образа, взять более компактную квантизацию (имена тегов — сверьтесь с документацией образа) или выставить лимит VRAM средствами самого рантайма.
Проблема. Порт опубликован, но соединения нет
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Connection refused либо Error: listen tcp 127.0.0.1:8080: bind: address already in use127.0.0.1, а не 0.0.0.0 — тогда docker-proxy до него не достучится. ss -ltnp | grep 8080
docker rm -f llm
docker run -d --name llm --gpus all -p 127.0.0.1:8081:8080 <llm-образ>:<тег>
curl -i http://127.0.0.1:8081/
Если не помогло — задайте серверу адрес прослушивания 0.0.0.0 через переменную окружения образа (её точное имя и значение сверьтесь с документацией образа) и перезапустите контейнер.
Вы подключили GPU к Docker, запустили контейнер с лимитами памяти и пробросом портов и подтвердили тремя проверками, что сервис отвечает. Следующий шаг — вынести сервис наружу через туннель или reverse proxy и настроить автозапуск контейнера (--restart unless-stopped).
Почему модель работает, но отвечает очень медленно?
Скорее всего, она считает на CPU: проверьте nvidia-smi во время генерации — если VRAM не растёт, флаг --gpus all не сработал или образ собран без поддержки CUDA (сверьтесь с документацией образа).
Что делает --shm-size и почему без него падает загрузка весов?
Он увеличивает разделяемую память контейнера. По умолчанию её мало, и многопроцессная загрузка весов упирается в этот лимит — увеличьте значение и перезапустите контейнер.
Как дать контейнеру доступ только к одной видеокарте?
Используйте --gpus '"device=0"'; номер устройства берите из nvidia-smi.
Порт занят, а какой процесс его держит — не понимаю.
Выполните ss -ltnp | grep <порт> и docker ps — первая команда покажет процесс на хосте, вторая покажет, не занимает ли порт другой контейнер; затем смените внешний порт в -p.