SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

Як встановити Docker у Rocky Linux і AlmaLinux

Встановіть Docker Engine через dnf у Rocky Linux або AlmaLinux і дізнайтеся, як виправити конфлікт Podman із docker та блокування bind mount через SELinux.

Встановлення Docker у Rocky Linux і AlmaLinux

Щоб установити Docker у Rocky Linux або AlmaLinux, додайте власний репозиторій Docker для dnf, установіть engine разом із compose plugin, а потім увімкніть сервіс. Для цього потрібні чотири команди. Процедура однакова в обох дистрибутивах, оскільки вони є перебудованими версіями Red Hat Enterprise Linux (RHEL) і використовують однакову структуру пакетів. CentOS Stream працює так само.

Процес установлення короткий, тому більша частина цього посібника присвячена відмінностям Enterprise Linux (EL) від Ubuntu. У вашому образі команду docker уже може обробляти Podman. SELinux блокує файли, змонтовані через bind mount, доки їм не буде призначено правильну мітку. Firewalld не фільтрує опубліковані Docker порти, тому порт контейнера може бути доступним з інтернету, навіть якщо firewall-cmd повідомляє, що відкритих портів немає.

Не використовуйте скрипт спрощеної установки Docker із get.docker.com. Власна документація Docker не рекомендує його для production-середовищ. Скрипт без запиту змінює конфігурацію репозиторіїв, і його не можна безпечно запускати повторно для оновлення. Якщо додати репозиторій вручну, dnf upgrade обробляє Docker так само, як і будь-який інший пакет у системі.

Чи вже відповідає 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-образи й може бути цілком придатним вибором. Якщо ви хочете використовувати його, на цьому можна зупинитися. Якщо вам потрібен 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. Станом на серпень 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 розгортає цю змінну на основі пакета release. 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 file, який ви щойно додали. Тому перед підтвердженням порівняйте 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-world

RPM-пакети Docker після встановлення залишають демон зупиненим і вимкненим. Тому цей крок є на сторінці 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. Відновлення сервісів після перезавантаження — це окреме питання, не пов’язане з увімкненням демона. Політики перезапуску визначають, чи запустяться сервіси 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= з назвою мітки, яку ви щойно побачили на каталозі. Розбіжність між цими двома полями пояснює всю проблему.

Виправленням є суфікс в аргументі тома. Docker сам змінить мітку шляху:

sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html

:z у нижньому регістрі позначає вміст як спільний, тому один каталог можуть використовувати кілька контейнерів. :Z у верхньому регістрі позначає вміст як приватний і не спільний. Такий каталог прив’язується до одного контейнера, а другий контейнер, який читає той самий шлях, отримує відмову в доступі. Використовуйте :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. Іменованим томам суфікс не потрібен, оскільки 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. Тепер опублікуйте порт і перевірте, які порти firewalld вважає відкритими:

sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-ports

firewall-cmd виводить порожній рядок. З іншої машини curl -I http://YOUR_SERVER_IP:8080/ повертає HTTP/1.1 200 OK. Порт відкритий для інтернету, але firewall не показує цього.

Причина полягає в маршруті пакета. Правила зон firewalld фільтрують трафік, адресований самому хосту. Опублікований порт не адресований хосту: Docker встановлює правило destination NAT (перетворення мережевої адреси), яке змінює адресу призначення на адресу контейнера ще до того, як пакет потрапляє на вхідний шлях хоста. Тому kernel пересилає пакет, а не доставляє його локально. Потім Docker додає свої bridge-інтерфейси до зони firewalld з назвою docker, у якій target має значення ACCEPT, і додає політику пересилання з назвою docker-forwarding, що дозволяє пересилання з будь-якої зони до зони docker. Правила ваших зон цей пакет не бачать.

Найкраще виправлення не потребує правила firewall. Прив’яжіть порт на стороні хоста до 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. Правила, додані таким способом, зникнуть після перезавантаження, якщо їх не зберегти. Коли завершите перевірку, додайте їх до unit systemd.

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-world

usermod -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, сервіс, який продовжує працювати після перезавантаження, а також три специфічні для EL варіанти поведінки, описані вище. Наступний крок — compose.yaml для кожного сервісу. У розділі анатомія Compose-файлу описано формат файлу та команди для роботи з ним. Якщо це ваш перший 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. Якщо під час отримання метаданих dnf з’являється Status code: 404, це означає, що змінна розгорнулася до номера випуску. Зміна /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 також додає свої bridge-інтерфейси до зони 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 є альтернативою, коли контейнери потрібно запускати від імені непривілейованого користувача. Це окремий шлях встановлення, а не параметр конфігурації.