Установка Docker на Ubuntu 24.04: официальный репозиторий, проверка, настройка
Ставим Docker Engine из официального репозитория Docker, а не из архива Ubuntu — там пакет отстаёт на месяцы и не содержит compose-плагина. Разбираем полную последовательность: ключ, репозиторий, установка, запуск без sudo, ограничение размера логов и главную ловушку Ubuntu — Docker публикует порты в обход UFW.
Docker на Ubuntu ставится двумя разными способами, и выбор между ними определяет, с какой версией вы будете жить ближайший год. Ниже — полная последовательность через официальный репозиторий Docker, с проверкой на каждом шаге и настройками, без которых сервер через полгода упирается в переполненный диск.
Выбираем источник пакета
В архиве Ubuntu есть пакет docker.io. Он ставится одной командой и этим подкупает, но у него два минуса: версия замораживается на момент релиза дистрибутива и обновляется только с бэкпортами безопасности, а плагин docker compose в него не входит — придётся отдельно искать docker-compose-v2.
Официальный репозиторий Docker даёт свежий Docker Engine, плагины buildx и compose в комплекте и обновляется обычным apt upgrade вместе с остальной системой.
docker.io из Ubuntu |
Официальный репозиторий | |
|---|---|---|
| Свежесть версии | отстаёт, часто на год | текущий релиз |
docker compose |
отдельным пакетом | в комплекте (docker-compose-plugin) |
docker buildx |
нет | в комплекте |
| Обновления | только security-бэкпорты | полноценные |
| Подходит для прода | с оговорками | да |
Дальше — установка из официального репозитория. Все команды проверены на Ubuntu 24.04 LTS; для 22.04 они идентичны, кодовое имя релиза подставляется автоматически.
Убираем конфликтующие пакеты
Если на машине уже пробовали ставить Docker или он приехал зависимостью, старые пакеты будут конфликтовать с docker-ce. Снимаем их заранее:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove -y $pkg
done
Команда безопасна: apt-get remove для отсутствующего пакета просто ничего не делает. Образы и контейнеры при этом не удаляются — они лежат в /var/lib/docker и переживут переустановку движка.
Подключаем репозиторий Docker
Сначала — зависимости для работы с HTTPS-репозиторием и ключами:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
Кладём GPG-ключ Docker в /etc/apt/keyrings. Это современный способ: ключ привязывается к конкретному репозиторию, а не добавляется в глобальное доверенное хранилище apt-key, которое объявлено устаревшим.
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Добавляем сам репозиторий. Кодовое имя релиза (noble для 24.04, jammy для 22.04) подставляется из /etc/os-release, поэтому команду не нужно править под версию:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Если apt-get update ругается на NO_PUBKEY или неизвестный релиз — проверьте, что подставилось в файл:
cat /etc/apt/sources.list.d/docker.list
Частая причина ошибки — производная от Ubuntu система (Linux Mint, Pop!_OS), где VERSION_CODENAME возвращает собственное имя релиза, которого в репозитории Docker нет. Там нужно подставить кодовое имя базовой Ubuntu вручную. Подробнее про механику apt-репозиториев — в разборе apt update и upgrade.
Ставим Docker Engine
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
Что именно приехало:
docker-ce— сам демон (Docker Engine);docker-ce-cli— клиент, командаdocker;containerd.io— низкоуровневый рантайм контейнеров;docker-buildx-plugin— расширенная сборка образов,docker buildx;docker-compose-plugin—docker composeкак подкоманда.
Установка сама создаёт и включает systemd-юниты docker.service и containerd.service, так что после перезагрузки Docker поднимется автоматически. Проверить это можно явно:
systemctl is-enabled docker
systemctl is-active docker
Обе команды должны ответить enabled и active. Если нет — включаем сами, механика разобрана в статье про systemd-юниты и systemctl enable:
sudo systemctl enable --now docker
Проверяем установку
docker --version
docker compose version
sudo docker run --rm hello-world
Флаг --rm удаляет контейнер сразу после завершения — тестовый мусор не копится. Если в выводе появилось «Hello from Docker!», значит демон работает, образ скачался с Docker Hub и контейнер запустился.
Более развёрнутая картина — docker info: там видно версию, хранилище (Storage Driver: overlay2), количество образов и контейнеров, cgroup-драйвер.
Запускаем docker без sudo
По умолчанию сокет /var/run/docker.sock принадлежит группе docker, поэтому обычный пользователь получает permission denied. Добавляем себя в группу:
sudo usermod -aG docker $USER
Изменение членства в группах применяется при новом входе в систему. Проще всего перелогиниться по SSH; либо обновить сессию на месте:
newgrp docker
docker run --rm hello-world
Важно понимать, что это ровно эквивалентно выдаче прав root. Любой член группы docker может смонтировать корень хоста в контейнер и получить полный доступ к системе:
# наглядная демонстрация, а не рецепт к применению
docker run --rm -v /:/host alpine cat /host/etc/shadow
Поэтому на многопользовательском сервере в группу docker добавляют только тех, кому и так доверен root. Про разграничение прав — в статьях о sudo и правах доступа chmod.
Ограничиваем логи, иначе диск кончится
Это главная причина «внезапно кончилось место» на серверах с Docker. По умолчанию драйвер логов json-file пишет весь stdout контейнера в файл без ограничения размера. Болтливое приложение за несколько месяцев спокойно съедает десятки гигабайт.
Правится один раз в /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true
}
sudo systemctl restart docker
Теперь на каждый контейнер приходится максимум 30 МБ логов. Настройка применяется к контейнерам, созданным после рестарта, — уже работающие нужно пересоздать.
live-restore отдельно полезен на проде: контейнеры продолжают работать при перезапуске самого демона, то есть apt upgrade docker-ce не роняет ваши сервисы.
Проверить, сколько занимает Docker прямо сейчас:
docker system df
sudo du -sh /var/lib/docker
Почистить неиспользуемое:
docker system prune -a --volumes
Команда удаляет остановленные контейнеры, неиспользуемые образы, сети и — из-за --volumes — тома, на которые никто не ссылается. На проде запускать осознанно.
Docker публикует порты в обход UFW
Ловушка, на которой обжигаются почти все. Вы настроили UFW, закрыли всё кроме 22 и 443, запускаете контейнер:
docker run -d -p 8080:80 nginx
И порт 8080 оказывается открыт всему интернету, хотя UFW его не разрешал. Причина в том, что Docker пишет свои правила в цепочку DOCKER таблицы nat напрямую через iptables, и они срабатывают раньше правил UFW. UFW при этом честно показывает Status: active и ничего не подозревает.
Проверить, что порт реально виден снаружи:
sudo ss -tulpn | grep 8080
Если в выводе 0.0.0.0:8080 — порт слушается на всех интерфейсах.
Самое надёжное решение — не публиковать порт наружу вообще. Привязываем публикацию к localhost:
docker run -d -p 127.0.0.1:8080:80 nginx
Снаружи порт недоступен, а наружу сервис отдаёт nginx как reverse proxy — с TLS, заголовками и логами. Это и есть штатная схема для веб-приложений в контейнерах.
В docker-compose.yml то же самое пишется так:
services:
app:
image: nginx
ports:
- "127.0.0.1:8080:80"
Про сам compose-файл и его структуру — в статье про Docker Compose на Ubuntu.
Переносим данные Docker на другой диск
Образы, тома и слои живут в /var/lib/docker. Если корневой раздел небольшой, а под данные есть отдельный диск, каталог переносится через data-root.
Останавливаем демон и копируем данные с сохранением прав:
sudo systemctl stop docker docker.socket
sudo rsync -aHAX /var/lib/docker/ /mnt/data/docker/
Флаги -aHAX сохраняют жёсткие ссылки, ACL и расширенные атрибуты — для слоёв образов это критично, обычное cp -r здесь ломает overlay2. Подробнее про ключи — в разборе rsync.
Прописываем новый путь в /etc/docker/daemon.json:
{
"data-root": "/mnt/data/docker"
}
sudo systemctl start docker
docker info | grep "Docker Root Dir"
Убедившись, что образы на месте, старый каталог можно удалить. Целевой диск при этом должен монтироваться при загрузке — как прописать его в fstab, разобрано отдельно.
Быстрая установка скриптом и почему не на прод
Docker предлагает convenience-скрипт:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Он определяет дистрибутив и делает всё то же самое за один заход. Удобно для одноразовой виртуалки или стенда в CI, но на боевом сервере лучше пройти шаги вручную: скрипт может неожиданно поменять поведение между запусками, тянет --channel stable без вашего контроля и хуже сочетается с системами управления конфигурацией. Для скачивания скрипта, кстати, подойдёт и wget.
Удаляем Docker полностью
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker /var/lib/containerd
sudo rm /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.asc
purge снимает пакеты вместе с конфигами, но каталоги с образами и томами не трогает — их удаляем явно вторым шагом. Проверьте, что там нет нужных томов: docker volume ls до удаления.
Частые вопросы
Какую версию Ubuntu поддерживает официальный репозиторий Docker?
Docker поддерживает актуальные LTS-релизы и текущий промежуточный. Для 24.04 (noble) и 22.04 (jammy) пакеты собираются штатно, и приведённые выше команды работают без изменений — кодовое имя подставляется из /etc/os-release.
Если репозиторий отвечает 404, значит для вашего релиза пакетов ещё нет: такое бывает в первые недели после выхода нового Ubuntu. В этот период либо ждём, либо временно указываем предыдущее кодовое имя вручную.
Чем docker compose отличается от docker-compose?
docker-compose с дефисом — старая версия на Python, которая ставилась отдельным пакетом и сейчас не развивается. docker compose без дефиса — плагин на Go, входящий в docker-compose-plugin, это текущая поддерживаемая реализация.
Синтаксис docker-compose.yml совместим, менять файлы при переходе не нужно. Ключ version: в начале файла современный compose игнорирует и предупреждает о нём — его можно просто удалить.
Нужно ли ставить containerd отдельно?
Нет. Пакет containerd.io устанавливается вместе с docker-ce из того же репозитория и настраивается автоматически. Отдельно containerd ставят только в сценариях без Docker — например, под Kubernetes.
Конфликт возникает, если в системе остался containerd из архива Ubuntu, — его снимает команда очистки из раздела про конфликтующие пакеты.
Почему контейнер не запускается после установки на Ubuntu 24.04?
Проверьте вывод sudo journalctl -u docker -n 50 — там видна конкретная причина. Типичные варианты: не хватает места на /var, конфликт с оставшимся containerd из архива Ubuntu, либо контейнер требует привилегий, которые ограничены политикой AppArmor.
В Ubuntu 24.04 ужесточены ограничения на непривилегированные user namespaces (kernel.apparmor_restrict_unprivileged_userns=1), что задевает часть rootless-сценариев и отдельные образы. Обычный Docker Engine, запущенный от root, это ограничение не затрагивает.
Как обновлять Docker дальше?
Обычным обновлением системы — репозиторий уже подключён:
sudo apt update && sudo apt upgrade
С live-restore: true в daemon.json перезапуск демона при обновлении не останавливает работающие контейнеры. Без этой настройки контейнеры на несколько секунд встанут.
Что запомнить
- Официальный репозиторий Docker вместо
docker.ioиз архива Ubuntu: свежая версия плюсcomposeиbuildxв комплекте. - Ключ кладём в
/etc/apt/keyringsи ссылаемся на него черезsigned-by—apt-keyустарел. - Членство в группе
dockerравносильно root: выдаём только тем, кому доверен root. - Ограничение
max-size/max-fileвdaemon.jsonставим сразу — иначе логи контейнеров однажды займут весь диск. - Docker публикует порты в обход UFW. Привязывайте публикацию к
127.0.0.1и выпускайте сервис наружу через nginx.