SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-13

Rocky LinuxとAlmaLinuxにDockerをインストールする方法

Rocky LinuxとAlmaLinuxへDocker Engineをdnfで導入する4コマンドを解説します。podmanがdockerコマンドを占有する問題や、SELinuxのbind mount対策も確認できます。

Rocky Linux と AlmaLinux に Docker をインストールする

Rocky Linux または AlmaLinux に Docker をインストールするには、Docker 独自の dnf リポジトリを追加し、compose plugin とともにエンジンをインストールしてから、サービスを有効化します。この作業は4つのコマンドで完了し、両方のディストリビューションで同じです。どちらも Red Hat Enterprise Linux (RHEL) の再構築版であり、パッケージ構成を共有しているためです。CentOS Stream でも同じ手順を使用できます。

インストール手順は短いため、このガイドでは Enterprise Linux (EL) と Ubuntu の違いを主に説明します。使用しているイメージによっては、Podman がすでに docker コマンドを提供している場合があります。SELinux は、bind mount したファイルに適切なラベルが付くまで、そのファイルへのアクセスをブロックします。Firewalld は Docker が公開するポートをフィルタリングしないため、firewall-cmd では開いているポートがないと表示されても、コンテナのポートがインターネットからアクセス可能な場合があります。

get.docker.com の Docker convenience script は使用しないでください。Docker の公式ドキュメントでも、本番環境では推奨されていません。このスクリプトは確認なしにリポジトリ設定を書き換え、安全に再実行してアップグレードすることもできません。リポジトリを手動で追加すれば、dnf upgrade は Docker をシステム上の他のパッケージと同じように扱えます。

Docker コマンドに対して podman が応答していませんか?

Rocky Linux と AlmaLinux では、デフォルトのリポジトリから podman をインストールできます。また、多くの VPS イメージでは podman があらかじめインストールされています。さらに、一部のイメージでは podman-docker までインストールされます。これは /usr/bin/docker に、podman を呼び出すシェルスクリプトを配置します。そのため、入力したすべての docker コマンドは podman として実行され、Docker 向けに書かれた手順で予期しない出力が表示されます。

最初に確認できる兆候はバナーです。/usr/bin/docker スクリプトは /etc/containers/nodocker ファイルの有無を確認します。このファイルがない場合、何かを実行する前に次の1行を表示します。

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 イメージを実行でき、十分に実用的な選択肢です。Podman を使用する場合は、ここで終了してください。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 という名前を空け、containerd.io パッケージが置き換える runc も削除します。ただし、Docker のドキュメントでは podman を競合パッケージとして扱っているため、この構成は Docker がサポートするものではありません。インストール時に競合が報告される場合は、上記の削除一覧をすべて使用してください。

dnf config-manager で Docker のリポジトリを追加する

Docker は download.docker.com で Enterprise Linux 用の RPM を公開しています。リポジトリファイルは CentOS のツリーを参照します。Rocky Linux と AlmaLinux はこのツリーを使用します。2026年8月時点で、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

dnf の Version 5 では --add-repo 引数が削除されています。そのため、2つ目のコマンドは新しいリリースでは失敗します。使用しているバージョンを確認し、対応する形式を選択してください。

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 に同じファイルが作成されます。形式を間違えると、何も通知されずに誤った処理が行われるのではなく、unknown-argument エラーになります。そのため、誤りを見落とすことはありません。

このリポジトリファイルでは、baseurl$releasever を含むパスが設定されます。dnf はこの変数をリリースパッケージから展開します。Rocky Linux と AlmaLinux では、この変数にメジャーバージョン番号が設定されます。そのため、EL 9 では 9、EL 10 では 10 となります。これにより、Rocky のホスト上でも CentOS リポジトリが正しく解決されます。インストール前に、変数の展開結果を確認してください。

sudo dnf repoinfo docker-ce-stable

Repo-baseurl の行を確認します。行末は /9/x86_64/stable または /10/x86_64/stable になるはずです。リリースで $releasever9.6 のようなポイントバージョンが設定されている場合、dnf はメタデータの取得時に、その URL に対して Status code: 404 を報告します。/etc/yum.repos.d/docker-ce.repo を編集し、$releasever をメジャー番号だけに置き換えて修正してください。

エンジンと compose プラグインをインストールする

sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

5 つのパッケージがあり、それぞれ役割が異なります。docker-ce はデーモン、dockerd です。docker-ce-cli は入力する docker コマンドです。containerd.io はデーモンが制御するコンテナランタイムです。docker-buildx-plugin はイメージをビルドします。docker-compose-plugindocker compose をサブコマンドとして提供します。

これらのパッケージは、ハイフン付きの docker-compose バイナリをインストールしません。これは Compose v1 のバイナリで、2023 年 7 月にサポートが終了しました。docker-compose をハイフン付きで呼び出すものは、docker compose をスペース区切りで呼び出すように更新してください。

最初のインストールでは、Docker の署名鍵をインポートするために処理が停止し、フィンガープリントが表示されます。この鍵は、追加したばかりのリポジトリファイルにある gpgkey=https://download.docker.com/linux/centos/gpg から取得されます。そのため、受け入れる前に、dnf が表示したフィンガープリントをその URL と照合してください。

頻繁に発生する問題が 1 つあります。dnf が、containerd.io には container-selinux が必要で、それを提供するものがないと報告する場合、AppStream リポジトリが無効になっています。dnf repolist を実行し、appstream が一覧にあることを確認してください。EL 9 と EL 10 では、container-selinux がこのリポジトリからリリースされます。

Docker を起動して動作を確認する

sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-world

Docker の RPM パッケージは、インストール後も daemon を停止状態かつ無効状態にします。そのため、この手順は Docker の Ubuntu ページにはなく、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 に対する permission error が表示される場合は、sudo を付け忘れています。以下の docker group のセクションで解決できます。

compose plugin は別のパッケージであり、engine が正常でも不足している場合があるため、個別に確認します。

docker compose version

正常な結果は Docker Compose version v2.x.x のようになります。再起動後にサービスを復旧できるかどうかは daemon を有効化することとは別の問題です。Compose サービスがブート時に復旧するかどうかは restart policy で決まります

バインドマウントで permission denied が発生するのはなぜですか?

Rocky Linux と AlmaLinux は、デフォルトで SELinux(Security-Enhanced Linux)を enforcing モードで実行します。getenforce で確認できます。このコマンドは Enforcing を出力します。

Docker コンテナは SELinux type container_t で実行されます。この type では、container_file_t のラベルが付いたファイルだけを読み書きできます。ホスト上で作成したディレクトリには、親パスから継承したラベルが付いています。このラベルは container_file_t ではありません。そのため、ホスト側から見て所有者、グループ、mode が正しくても、コンテナからのアクセスは拒否されます。次の3つのコマンドで再現できます。

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

原因は2つのコマンドで確認できます。ls -ldZ /srv/site はラベルを出力します。/srv 配下のパスでは、そのラベルは system_u:object_r:var_t:s0 であり、container_file_t ではありません。次に sudo ausearch -m avc -ts recent はカーネルの audit レコードを出力します。このレコードには avc: denied { read }container_t を示す scontext= フィールド、そして先ほどディレクトリで確認したラベルを示す tcontext= フィールドが含まれます。この2つのフィールドの不一致が原因です。

修正するには、volume 引数に suffix を追加します。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 として再ラベル付けします。これは1つのコンテナに関連付けられるため、別のコンテナが同じパスを読み取ると拒否されます。sidecar または backup コンテナも使用するディレクトリには :z を指定します。1つのコンテナだけが所有するデータベースディレクトリには :Z を指定します。

Docker のドキュメントには、再ラベル付けが再帰的に行われるため、重要な警告があります。/home/usr などのシステムディレクトリを :Z 付きでバインドマウントすると、「ホストマシンが動作不能になり、ホストマシンのファイルを手動で再ラベル付けする必要が生じる場合があります」。これらの suffix はコンテナ用に作成したディレクトリだけに指定し、システムパスには指定しないでください。

Compose では、同じ文字列に suffix を追加します。

services:
  web:
    image: nginx:alpine
    volumes:
      - /srv/site:/usr/share/nginx/html:ro,z

注意すべき制限が2つあります。--mount flag では SELinux ラベルを設定できません。ラベルが必要な場合は -v を使用します。Named volume には suffix は必要ありません。Docker が /var/lib/docker/volumes 配下に作成するディレクトリへ、自動的にラベルを付けるためです。

SELinux を無効にしないでください。sudo setenforce 0 は1分間のテストにだけ使用します。これでコンテナが動作するなら、問題はラベルにあり、解決策は :z です。直ちに sudo setenforce 1 で元に戻してください。Enterprise Linux では、bind mount の permission denied には、コンテナ内からは同じに見える別々の原因があります。1つは SELinux ラベルです。もう1つは、通常の数値ユーザー所有者とグループ所有者です。これは PUID と PGID 変数で解決する理由です。ls -lnZ は mode、数値の所有者、ラベルを1行で表示します。これにより、どちらが原因かを判断できます。

firewalld が閉じているように見えるのに公開ポートへ接続できるのはなぜですか?

Firewalld は 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 が返ります。ポートはインターネットに公開されていますが、ファイアウォールには何も表示されません。

原因は、パケットが通過する経路にあります。Firewalld のゾーンルールは、ホスト自体を宛先とするトラフィックをフィルタリングします。公開ポートの宛先はホストではありません。Docker はパケットがホストの input 経路に到達する前に、宛先 NAT(ネットワークアドレス変換)ルールで宛先をコンテナのアドレスへ書き換えます。そのため、カーネルはパケットをローカルに配信せず、転送します。Docker はさらに、ブリッジインターフェースを docker という firewalld ゾーンに配置します。このゾーンの target は ACCEPT です。また、任意のゾーンから docker ゾーンへの転送を許可する docker-forwarding という forwarding policy を追加します。ゾーンルールはこのパケットを認識できません。

最も簡潔な解決策は、ファイアウォールルールを追加しないことです。公開のホスト側を loopback にバインドし、その前段にリバースプロキシを配置します。

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/

ローカルでの curlHTTP/1.1 200 OK を返し、別のマシンから同じリクエストを送っても接続できなくなります。-p 引数にホストアドレスを指定しない場合、すべてのインターフェースで公開されます。そのため、単独の -p 8080:80 は、そのサービスを公開する決定を意味すると考えてください。

一部のアドレスからは接続でき、他のアドレスからは接続できないようにする必要がある場合、Docker が予約している chain を使用できます。DOCKER-USER は Docker 自身の accept ルールより前に処理されます。そのため、ここに配置したルールは、Docker が再起動して chain を書き換えても残ります。

sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USER

インターフェース名は eth0 と仮定せず、ip route show default から取得してください。現在の EL イメージでは、enp1s0ens3 のような名前が使われます。Rocky と AlmaLinux では、iptables コマンドは nftables の互換レイヤーです。Docker の chain はこのコマンドで確認できます。この方法で追加したルールは、保存しない限り再起動後に失われます。ルールが適切だと確認できたら、systemd unit に記述してください。

2025 年にリリースされた Docker Engine 28.0 では、隣接する問題も解消されました。公開されていないコンテナポートへの直接の routed access が、DOCKER chain でブロックされるようになりました。この変更は公開ポートには影響しないため、現在のバージョンでも上記の説明はそのまま適用されます。習慣にしておくべき運用手順が 1 つあります。sudo firewall-cmd --reload の後には、公開ポートへ再接続してください。応答しなくなっていた場合は、sudo systemctl restart docker が Docker のルールを再インストールします。

Ubuntu の管理者も別のツールを通じて同じ問題に直面します。それが 公開した Docker ポートが ufw ルールを無視する理由 です。どちらの場合も原因は NAT 経路です。異なるのは、その手前にあるファイアウォールだけです。

root 以外のユーザーを docker グループに追加する

毎回 docker コマンドの前に sudo と入力するのは手間です。docker グループを使えば、この操作は不要になります。

sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-world

usermod -aG/etc/group を編集しますが、現在の shell にはすでにグループ一覧が読み込まれています。そのため、新しいグループ一覧を取得するまで変更は適用されません。newgrp docker はグループ設定を適用した shell を起動するため、すぐに動作を確認できます。新しい SSH セッションでは、自動的に変更が反映されます。

このグループが付与する権限を明確に理解してください。所属すると /var/run/docker.sock への書き込み権限が付与されます。この socket に接続できるものは、host のファイルシステムを mount する container を daemon に起動させることができます。次のコマンドで、その意味を確認できます。

docker run --rm -v /:/host alpine wc -l /host/etc/shadow

これは、root だけが読み取れるファイルを、sudo 権限のないアカウントから読み取ります。Docker の post-install ドキュメントにも同じ説明があります。docker グループには root と同等の権限が付与されます。アカウントをこのグループに追加するのは、そのアカウントに sudo も付与する場合だけにしてください。新しい server でアカウントを設定する場合は、後から決めるのではなく、VPS での最小権限ユーザー設定 と併せて判断してください。

Docker には、daemon を非特権ユーザーとして実行する rootless mode もあります。これは別のインストール手順であり、storage driver と 1024 未満のポートの動作も変わります。後から flag を追加するものではなく、独立したプロジェクトとして計画してください。

次に進む先

これで、エンジン、Compose plugin、再起動後も稼働するサービス、および上記で説明した EL 固有の 3 つの動作がそろいました。次はサービスごとの compose.yaml です。Compose ファイルの構成では、ファイル形式と、それを操作するコマンドを説明します。初めてコンテナホストを構築する場合は、VPS で Docker を実行するで、このガイドでは扱っていないサイズ設計、ストレージ、イメージの衛生管理について確認できます。

FAQ

Docker の CentOS リポジトリは Rocky Linux と AlmaLinux で動作しますか?

はい。dnf config-manager を使用して https://download.docker.com/linux/centos/docker-ce.repo を追加します。そのファイルの 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 のドキュメントでは、podmanrunc が競合するパッケージとして記載されており、Docker Engine をインストールする前に両方を削除するよう求めています。具体的な競合は podman-docker パッケージです。このパッケージは /usr/bin/docker を所有し、すべての docker コマンドを podman コマンドとして実行します。rpm -qf "$(command -v docker)" を実行すると、そのパスを所有するパッケージを確認できます。出力が podman-docker で始まる場合は、podman が応答しています。両方のエンジンを共存させる構成は Docker がサポートしていません。そのため、該当するサーバーではどちらか一方を選択してください。

バインドマウントでコンテナに permission denied が発生するのはなぜですか?

Rocky Linux と AlmaLinux では、デフォルトで SELinux が enforcing になっています。コンテナは container_t タイプで実行され、container_file_t のラベルが付いたファイルにしかアクセスできません。そのため、作成したディレクトリに異なるラベルが付いていると、所有者やモードに関係なくアクセスが拒否されます。ホストのパスに対して ls -ldZ を実行し、sudo ausearch -m avc -ts recent で確認してください。後者は、異なる2つのコンテキストを含む avc: denied を出力します。コンテナ間で共有するコンテンツには、ボリューム引数に :z を追加します。1つのコンテナだけで使用するコンテンツには :Z を追加します。:Z/home または /usr に対して実行しないでください。再ラベル付けは再帰的に行われ、ホストが動作しなくなります。

コンテナポートを公開するために firewalld でポートを開く必要がありますか?

いいえ。それが問題になります。Docker の NAT ルールは、パケットがホストの input パスに到達する前に宛先アドレスを書き換えます。そのため、firewalld の zone ルールはそのパケットを検査しません。Docker はブリッジも、docker という firewalld の zone に、ACCEPT の target で配置します。-p 8080:80 で起動したコンテナは、sudo firewall-cmd --list-ports が何も出力しなくてもインターネットから到達可能です。ホストからだけサービスに到達できるようにする場合は、-p 127.0.0.1:8080:80 で特定のアドレスに公開します。または、DOCKER-USER chain にフィルタリングルールを挿入してください。Docker は独自の accept ルールより前にこの chain を処理します。

ユーザーを docker group に追加しても安全ですか?

root 権限が付与されます。docker group のメンバーは /var/run/docker.sock に書き込めます。docker run --rm -v /:/host alpine wc -l /host/etc/shadow を使えば、sudo 権限のないアカウントから root 専用ファイルを読み取れます。Docker のインストール後のドキュメントにも、同じ権限関係が記載されています。sudo を使える権限をすでに信頼しているアカウントだけを追加してください。共有アカウントやサービスアカウントでは、引き続き sudo docker を使用してください。非特権ユーザーでコンテナを実行する必要がある場合は rootless mode が代替手段です。これは設定項目ではなく、別のインストール手順です。