Як встановити Docker у Rocky Linux або AlmaLinux
Встановіть Docker Engine через dnf і виправте типові проблеми EL: Podman займає команду docker, а SELinux блокує bind mount без правильної мітки.
Встановлення Docker у Rocky Linux і AlmaLinux
Щоб встановити Docker у Rocky Linux або AlmaLinux, додайте власний репозиторій Docker для dnf, встановіть engine разом із compose plugin, а потім увімкніть сервіс. Усього потрібно виконати чотири команди. На обох дистрибутивах вони однакові, оскільки обидва є перебудованими версіями Red Hat Enterprise Linux (RHEL) і використовують однакову структуру пакетів. CentOS Stream працює так само. Усе нижче стосується обох дистрибутивів, тому якщо ви ще обираєте між ними, вирішальними чинниками є гарантії сумісності, які надає кожен проєкт, і те, чи підтримується ваш старіший CPU.
Встановлення коротке, тому більша частина цього посібника присвячена відмінностям Enterprise Linux (EL) від Ubuntu. У вашому образі команда docker може вже належати Podman. SELinux блокує файли, змонтовані через bind mount, доки вони не отримають правильну мітку. Firewalld не фільтрує опубліковані Docker порти, тому порт контейнера може бути відкритим в інтернет, хоча firewall-cmd повідомляє, що відкритих портів немає.
Не використовуйте convenience script Docker із get.docker.com. У документації Docker зазначено, що цей скрипт не рекомендований для production. Він без запиту перезаписує конфігурацію репозиторію, а безпечний повторний запуск для оновлення не гарантується. Додавання репозиторію вручну означає, що dnf upgrade працює з Docker так само, як з будь-яким іншим пакетом у системі. У такому разі engine також підпадає під дію dnf-automatic, якщо він застосовує security updates за розкладом, тому заздалегідь вирішіть, чи потрібно оновлювати Docker без участі адміністратора, чи відкласти оновлення до вікна технічного обслуговування. У будь-якому разі оновлення замінює пакетний binary, тоді як старий dockerd продовжує працювати, а needs-restarting — це команда, яка показує, які сервіси досі виконують код, щойно замінений.
Чи вже відповідає podman на команду docker?
Rocky Linux і AlmaLinux постачають podman у стандартних репозиторіях, а багато образів VPS встановлюють його автоматично. Деякі образи йдуть далі та встановлюють podman-docker, який розміщує shell-скрипт у /usr/bin/docker і викликає podman. Після цього кожна команда docker, яку ви вводите, фактично запускає podman, тому інструкція для Docker починає виводити неочікувані результати.
Перша ознака — банер. Скрипт /usr/bin/docker перевіряє наявність файла /etc/containers/nodocker. Якщо файла немає, скрипт виводить один рядок перед запуском будь-якої команди:
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.Хтось міг створити цей файл, щоб приховати банер, тому не покладайтеся лише на цю ознаку. Перевірте в базі даних пакетів, якому пакету належить цей бінарний файл:
command -v docker
rpm -qf "$(command -v docker)"Результат, що починається з podman-docker, означає, що команду обробляє podman. Результат, що починається з docker-ce-cli, означає справжній Docker. Якщо rpm -qf повідомляє, що жодному пакету не належить цей файл, його встановили вручну. У такому разі перед використанням перевірте вміст скрипту.
Podman запускає ті самі OCI-образи й може бути цілком придатним вибором. Якщо ви хочете використовувати його, на цьому можна зупинитися. Обидва інструменти є рушіями контейнерів Linux, тому, якщо рішення щодо платформи ще не прийнято, варто знати, що jails у FreeBSD ізолюють повний userland, а не запускають багаторівневі образи, отримані з registry. Якщо вам потрібен Docker Engine, спочатку видаліть конфліктні пакети. Це список, який Docker наводить для RHEL:
sudo dnf remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runcПеред підтвердженням перевірте, які пакети dnf планує видалити разом із ними. У новому образі VPS список буде коротким. На сервері, яким уже користувалися, видалення podman може також видалити cockpit-podman або інший інструмент, що залежить від нього.
Теоретично podman можна залишити разом із Docker: видаліть лише podman-docker, щоб звільнити ім’я docker, а також runc, яке замінює пакет containerd.io. У документації Docker podman вказано як конфліктний пакет, тому Docker не підтримує таку конфігурацію. Якщо під час встановлення все одно виникає конфлікт, скористайтеся наведеним вище повним списком для видалення.
Додайте репозиторій Docker за допомогою dnf config-manager
Docker публікує RPM-пакети для Enterprise Linux за адресою download.docker.com. Файл репозиторію вказує на дерево CentOS, з яким працюють Rocky Linux і AlmaLinux. Вказати репозиторій CentOS для системи Rocky спочатку здається помилкою, доки не стане зрозуміло як обидва дистрибутиви походять із CentOS після того, як Red Hat у 2020 році перетворила CentOS на Stream. Станом на серпень 2026 року Docker документує цей репозиторій для CentOS Stream 9 і CentOS Stream 10.
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoУ версії 5 dnf прибрали аргумент --add-repo, тому друга команда не працює в новіших випусках. Перевірте встановлену версію та виберіть відповідний варіант:
dnf --versionЯкщо виведено версію 5.x, використовуйте варіант із підкомандою:
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoОбидва варіанти записують той самий файл до /etc/yum.repos.d/docker-ce.repo. Неправильний варіант завершується помилкою невідомого аргументу, а не виконує неправильну дію без повідомлення, тому ви не пропустите проблему.
Цей файл репозиторію задає baseurl як шлях, що містить $releasever, а dnf розгортає цю змінну зі встановленого пакета релізу. Rocky Linux і AlmaLinux задають для неї номер основної версії, тому 9 у EL 9 і 10 у EL 10. Саме тому репозиторій CentOS коректно працює в системі Rocky. Перевірте результат розгортання перед встановленням:
sudo dnf repoinfo docker-ce-stableПрочитайте рядок Repo-baseurl. Він має закінчуватися на /9/x86_64/stable або /10/x86_64/stable. Якщо ваш випуск задає $releasever як номер точкової версії, наприклад 9.6, dnf повідомляє Status code: 404 для цієї URL-адреси під час отримання метаданих. Виправте це, відредагувавши /etc/yum.repos.d/docker-ce.repo і замінивши $releasever на сам номер основної версії.
Встановлення engine і compose plugin
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginП’ять пакетів, і кожен виконує одну функцію. docker-ce — це daemon, dockerd. docker-ce-cli — це команда docker, яку ви вводите. containerd.io — це container runtime, яким керує daemon. docker-buildx-plugin створює images. docker-compose-plugin додає docker compose як subcommand.
Ці пакети не встановлюють бінарний файл із дефісом docker-compose. Це був Compose v1, життєвий цикл якого завершився в July 2023. Усе, що викликає docker-compose із дефісом, потрібно замінити на docker compose із пробілом.
Під час першого встановлення система зупиниться, щоб імпортувати signing key Docker, і покаже його fingerprint. Ключ надходить із gpgkey=https://download.docker.com/linux/centos/gpg у repo-файлі, який ви щойно додали. Тому перед підтвердженням порівняйте fingerprint, виведений dnf, із цією URL-адресою.
Одна помилка трапляється достатньо часто, щоб розглянути її окремо. Якщо dnf повідомляє, що containerd.io потребує container-selinux, але жоден пакет його не надає, репозиторій AppStream вимкнено. Виконайте dnf repolist і переконайтеся, що appstream є в списку, оскільки саме там постачається container-selinux для EL 9 і EL 10.
Запустіть Docker і перевірте його роботу
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-worldRPM-пакети Docker після встановлення залишають daemon зупиненим і вимкненим. Тому цей крок є на сторінці Docker для CentOS, але відсутній на сторінці для Ubuntu, де deb-пакет запускає сервіс автоматично. Пропустіть enable, і Docker працюватиме до наступного перезавантаження, після чого залишиться зупиненим і зупинить разом із собою всі контейнери.
systemctl status має показати Active: active (running). Контейнер hello-world має вивести This message shows that your installation appears to be working correctly. і завершити роботу. Якщо замість цього він виводить помилку доступу до /var/run/docker.sock, ви не додали sudo. Це виправляється в наведеному нижче розділі про групу docker.
Перевірте плагін compose окремо, оскільки це інший пакет і він може бути відсутнім, навіть якщо engine працює:
docker compose versionУ разі успішної перевірки результат має виглядати як Docker Compose version v2.x.x. Відновлення роботи сервісів після перезавантаження — це окреме питання від увімкнення daemon. Політики перезапуску визначають, чи запустяться сервіси Compose після завантаження системи.
Чому bind mount повертає помилку permission denied?
Rocky Linux і AlmaLinux за замовчуванням запускають SELinux у режимі enforcing. Перевірте це за допомогою getenforce. Команда виведе Enforcing.
Контейнери Docker працюють під типом SELinux container_t. Цей тип може читати й записувати лише файли з міткою container_file_t. Каталог, створений на хості, отримує мітку від батьківського шляху. Це не container_file_t. Контейнер отримує відмову в доступі, навіть якщо з боку хоста власник, група та права доступу правильні. Відтворіть проблему трьома командами:
sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.htmlКонтейнер виведе:
cat: can't open '/usr/share/nginx/html/index.html': Permission deniedДві команди покажуть причину. ls -ldZ /srv/site виводить мітку. Для шляху під /srv це system_u:object_r:var_t:s0, а не container_file_t. Потім sudo ausearch -m avc -ts recent виводить запис аудиту ядра. Він містить avc: denied { read }, поле scontext= із назвою container_t, а також поле tcontext= із міткою, яку ви щойно побачили на каталозі. Невідповідність між цими двома полями повністю пояснює проблему.
Виправленням є суфікс в аргументі volume. Docker змінить мітку шляху автоматично:
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z у нижньому регістрі позначає вміст як shared, тому один каталог можуть використовувати кілька контейнерів. :Z у верхньому регістрі позначає його як private та unshared і прив’язує до одного контейнера. Тоді другий контейнер, який читає той самий шлях, отримає відмову в доступі. Використовуйте :z для каталогів, до яких також звертається sidecar або контейнер резервного копіювання. Використовуйте :Z для каталогу бази даних, яким володіє один контейнер.
У документації Docker є важливе попередження, яке варто повторити, оскільки зміна мітки виконується рекурсивно. Монтування системного каталогу, наприклад /home або /usr, із :Z «робить хостову машину непрацездатною, і вам може знадобитися вручну змінити мітки файлів на хостовій машині». Застосовуйте ці суфікси лише до каталогів, створених для контейнера, і ніколи не вказуйте системний шлях.
У Compose суфікс додається до того самого рядка:
services:
web:
image: nginx:alpine
volumes:
- /srv/site:/usr/share/nginx/html:ro,zЛегко помилитися через два обмеження. Прапорець --mount взагалі не може встановити мітку SELinux, тому для цього використовуйте -v. Для іменованих volume суфікс не потрібен, оскільки Docker сам позначає каталоги, які створює під /var/lib/docker/volumes.
Не вимикайте SELinux. Використовуйте sudo setenforce 0 лише як однохвилинний тест: якщо після цього контейнер працює, проблема пов’язана з міткою, а відповіддю є :z. Негайно поверніть попередній режим за допомогою sudo setenforce 1. В Enterprise Linux permission denied для bind mount може мати дві окремі причини, які всередині контейнера виглядають однаково. Одна з них — мітка SELinux. Інша — звичайні числові власник і група, для виправлення яких призначені змінні PUID і PGID: саме цю проблему вони допомагають вирішити. ls -lnZ виводить режим доступу, числового власника та мітку в одному рядку. Так можна визначити, з якою саме проблемою ви працюєте.
Чому опублікований порт доступний, коли firewalld виглядає закритим?
Firewalld — стандартний firewall у Rocky Linux і AlmaLinux. Перевірте, чи він запущений, за допомогою sudo systemctl is-active firewalld. Якщо ви ще не налаштували його на цьому сервері, спочатку відкрийте SSH і вебпорт за допомогою firewalld, оскільки описана нижче ситуація має сенс лише за наявності робочого набору правил zone для порівняння. Тепер опублікуйте порт і перевірте, що firewalld вважає відкритим:
sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-portsfirewall-cmd виводить порожній рядок. З іншої машини curl -I http://YOUR_SERVER_IP:8080/ повертає HTTP/1.1 200 OK. Порт відкритий для інтернету, але firewall не показує цього.
Причина полягає в маршруті, яким проходить пакет. Правила zone у firewalld фільтрують трафік, адресований безпосередньо цьому хосту. Опублікований порт не адресується хосту: Docker встановлює правило destination NAT (network address translation), яке змінює адресу призначення на адресу контейнера ще до того, як пакет потрапляє на вхідний шлях хоста. Тому kernel пересилає пакет, а не доставляє його локально. Потім Docker розміщує bridge-інтерфейси у firewalld zone з назвою docker, для якої target має значення ACCEPT, і додає forwarding policy з назвою docker-forwarding, що дозволяє пересилання з будь-якої zone до zone docker. Правила вашої zone не бачать цього пакета.
Найпростіше виправлення не потребує правила firewall. Прив’яжіть сторону publish на хості до loopback і розмістіть перед нею reverse proxy:
sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/Локальний curl повертає HTTP/1.1 200 OK, а такий самий запит з іншої машини більше не встановлює з’єднання. Якщо в аргументі -p не вказано адресу хоста, порт публікується на кожному інтерфейсі. Тому сприймайте вказане без адреси -p 8080:80 як рішення відкрити цей сервіс публічно.
Якщо сервіс має бути доступним з одних адрес, але недоступним з інших, Docker надає окремий chain. DOCKER-USER обробляється перед власними правилами accept у Docker, тому правило, додане туди, зберігається після перезапуску Docker і повторного створення його chain:
sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USERВізьміть назву інтерфейсу з ip route show default, а не припускайте, що це eth0, оскільки поточні образи EL використовують назви на кшталт enp1s0 або ens3. У Rocky і AlmaLinux команда iptables є compatibility layer над nftables, і через неї видно chain Docker. Правила, додані таким способом, буде втрачено після перезавантаження, якщо їх не зберегти. Коли перевірите, що вони працюють, запишіть їх у systemd unit.
Docker Engine 28.0, випущений у 2025 році, усунув суміжну вразливість: прямий routed-доступ до портів контейнерів, які ніколи не публікувалися, тепер блокується в chain DOCKER. Ця зміна не впливає на опубліковані порти, тому все описане вище залишається актуальним у поточних версіях. Варто сформувати одну операційну звичку: після будь-якого sudo firewall-cmd --reload повторно перевіряйте опублікований порт. Якщо він перестав відповідати, sudo systemctl restart docker повторно встановлює правила Docker.
Адміністратори Ubuntu стикаються з тією самою проблемою через інший інструмент. Ось чому опубліковані порти Docker ігнорують правила ufw. В обох випадках причина — шлях NAT. Змінюється лише firewall, який розташований перед ним.
Додайте користувача без прав root до групи docker
Виконувати sudo перед кожною командою docker швидко набридає. Група docker усуває цю потребу:
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldusermod -aG редагує /etc/group, але поточна оболонка вже має власний список груп. Тому зміна не застосовується, доки ви не відкриєте нову оболонку. newgrp docker запускає оболонку з доданою групою, щоб ви могли одразу перевірити результат. Нові SSH-сеанси підхоплюють цю зміну автоматично.
Чітко визначте, які права надає ця група. Членство дає доступ на запис до /var/run/docker.sock. Будь-який процес, який може взаємодіяти з цим сокетом, може попросити daemon запустити контейнер із підключеною файловою системою хоста. Одна команда демонструє наслідки:
docker run --rm -v /:/host alpine wc -l /host/etc/shadowЦя команда читає файл, доступний лише root, з облікового запису без прав sudo. В офіційній документації Docker після встановлення зазначено те саме: група docker надає привілеї, еквівалентні root. Додавайте обліковий запис до цієї групи лише в тому разі, якщо ви також надали б цьому обліковому запису sudo. Якщо ви налаштовуєте облікові записи на новому сервері, визначте це разом з іншими параметрами налаштування користувачів із мінімальними привілеями на VPS, а не постфактум.
Docker також має rootless mode, у якому daemon працює від непривілейованого користувача. Це окремий шлях встановлення. Він змінює поведінку storage drivers і портів із номерами нижче 1024. Тому плануйте його як окремий проєкт, а не як прапорець, який можна додати пізніше.
Куди рухатися далі
Тепер у вас є engine, compose plugin, сервіс, який працює після перезавантаження, і три специфічні для EL особливості, описані вище. Наступний крок — compose.yaml для кожного сервісу, а в розділі будова Compose file описано формат файлу та команди для роботи з ним. Якщо це ваш перший container host, у розділі запуск Docker на VPS розглянуто питання вибору розміру, сховища та гігієни образів, які цей посібник не охоплює.
FAQ
Чи працює репозиторій Docker для CentOS у Rocky Linux і AlmaLinux?
Так. Додайте https://download.docker.com/linux/centos/docker-ce.repo за допомогою dnf config-manager. Параметр baseurl у цьому файлі містить $releasever, а Rocky Linux і AlmaLinux підставляють у нього номер основної версії. Тому система EL 9 використовує дерево CentOS 9, а система EL 10 — дерево CentOS 10. Перевірте підстановку за допомогою sudo dnf repoinfo docker-ce-stable і прочитайте рядок Repo-baseurl. Помилка Status code: 404 під час отримання dnf метаданих означає, що змінна розгорнулася до номера проміжного випуску. Відредагуйте /etc/yum.repos.d/docker-ce.repo, указавши лише номер основної версії.
Чи можна встановити Docker і podman на одному сервері?
У документації Docker пакети podman і runc зазначені як конфліктні. Перед встановленням Docker Engine їх потрібно видалити. Безпосередній конфлікт створює пакет podman-docker. Він володіє /usr/bin/docker і перетворює кожну команду docker на команду podman. Виконайте rpm -qf "$(command -v docker)", щоб визначити, якому пакету належить цей шлях. Якщо вивід починається з podman-docker, команди обробляє podman. Docker не підтримує одночасне використання обох рушіїв у такій конфігурації. На важливому сервері виберіть один із них.
Чому контейнер отримує помилку permission denied під час використання bind mount?
У Rocky Linux і AlmaLinux SELinux за замовчуванням працює в режимі enforcing. Контейнери запускаються з типом container_t і можуть працювати лише з файлами, позначеними як container_file_t. Тому створений вами каталог має неправильну мітку, і доступ до нього заборонено незалежно від власника та прав доступу. Перевірте це за допомогою ls -ldZ для шляху на хості та sudo ausearch -m avc -ts recent, яка виводить avc: denied із двома контекстами, що не збігаються. Додайте :z до аргументу тому для даних, спільних між контейнерами, або :Z для даних, призначених лише для одного контейнера. Ніколи не вказуйте :Z для /home або /usr, оскільки перемаркування виконується рекурсивно та пошкодить систему хоста.
Чи потрібно відкривати порт у firewalld, щоб опублікувати порт контейнера?
Ні. Саме це і є проблемою. Правило NAT Docker змінює адресу призначення до того, як пакет потрапляє на вхідний шлях хоста. Тому зональні правила firewalld його не перевіряють. Docker також додає свої мости до зони firewalld з назвою docker і target ACCEPT. Контейнер, запущений з -p 8080:80, доступний з інтернету, тоді як sudo firewall-cmd --list-ports не виводить нічого. Використовуйте -p 127.0.0.1:8080:80 для публікації на конкретній адресі, якщо доступ до сервісу має бути лише з хоста. Або додайте правила фільтрації до ланцюжка DOCKER-USER, який Docker обробляє перед власними правилами accept.
Чи безпечно додавати мого користувача до групи docker?
Це надає root-доступ. Учасник групи docker може записувати до /var/run/docker.sock, після чого docker run --rm -v /:/host alpine wc -l /host/etc/shadow читає файл, доступний лише root, від імені облікового запису без прав sudo. У документації Docker після встановлення зазначено ту саму еквівалентність. Додавайте лише ті облікові записи, яким ви вже довірили б sudo, а для спільних або сервісних облікових записів продовжуйте використовувати sudo docker. Rootless mode — альтернатива, коли контейнери потрібно запускати від непривілейованого користувача. Це окремий спосіб встановлення, а не параметр конфігурації.