Docker on a RHEL-family Workstation
These are the steps and the two hard-won fixes for standing up a development machine on the RHEL family. They were originally written for CentOS Linux 8, which reached end of life on 31 December 2021; CentOS Linux 7 followed on 30 June 2024. Neither receives updates. The replacements are RHEL 9, Rocky Linux 9, AlmaLinux 9 or CentOS Stream 9, and the commands below assume one of those.
Docker Engine
Section titled “Docker Engine”Red Hat ships podman-docker, which installs a docker shim at /usr/bin/docker and
therefore conflicts with docker-ce-cli. Remove it and anything left from an older Docker
before adding Docker’s own repository:
sudo dnf remove -y podman-docker docker docker-client docker-client-latest \ docker-common docker-latest docker-latest-logrotate docker-logrotate \ docker-selinux docker-engine-selinux docker-engine
sudo dnf -y install dnf-plugins-coresudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.reposudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudo systemctl enable --now dockerdocker-compose-plugin provides Compose v2 as the docker compose subcommand. The standalone
docker-compose v1 binary that older guides download from GitHub releases stopped receiving
updates in 2023 and should not be installed.
Add yourself to the docker group so the daemon socket is usable without sudo, then log out
and back in for the group to take effect:
sudo usermod -aG docker $USERMembership of the docker group is equivalent to root on the host: anyone in it can start a
privileged container that mounts the root filesystem. Treat it as an administrative privilege,
not a convenience.
Check the install:
docker versiondocker compose versiondocker run --rm hello-worldInstall the packaged LTS build; dnf search openjdk lists what the release carries.
sudo dnf install java-21-openjdk-develWhere several JDKs are installed, switch the default with
alternatives.
Git and GitHub over port 443
Section titled “Git and GitHub over port 443”sudo dnf install gitFollow connecting to GitHub with SSH
to generate a key and register it. On a network that blocks outbound port 22, GitHub serves SSH
on 443 as well — put this in ~/.ssh/config:
Host github.com Hostname ssh.github.com Port 443 User gitchmod 600 ~/.ssh/configssh -T git@github.comOther tooling
Section titled “Other tooling”- Gradle — install the current release from
gradle.org/install, or let each repository’s Gradle wrapper
(
./gradlew) pick its own version, which is preferable because the build then pins its own toolchain. - Node.js —
dnf module list nodejsshows the streams the release offers; install the one you need withsudo dnf module install nodejs:<stream>. - Kubernetes tooling — follow Install Tools for
kubectl, andkindorminikubefor a local cluster. - IntelliJ IDEA — install through the JetBrains Toolbox App so updates are managed for you,
rather than unpacking a fixed build into
/opt.
Troubleshooting
Section titled “Troubleshooting”408 Request Time-out signing in or pulling an image
Section titled “408 Request Time-out signing in or pulling an image”Caused here by the MTU on both the wired and wireless interfaces being left at AUTO. Lowering it to 900 fixed it. Three ways to do that, in increasing order of permanence:
Temporarily, for this boot. Useful to confirm the MTU really is the cause before changing anything persistent:
ip link showsudo ip link set wlp2s0 mtu 900sudo ip link set enp0s31f6 mtu 900Through the NetworkManager text UI. nmtui edits the stored connection profile, so the
change survives a reboot:
sudo nmtuisudo nmcli connection down [NIC_NAME]sudo nmcli connection up [NIC_NAME]By editing the connection profile directly. Equivalent to the above, and scriptable:
sudo nmcli connection modify [NIC_NAME] 802-3-ethernet.mtu 900sudo nmcli connection down [NIC_NAME]sudo nmcli connection up [NIC_NAME]ip addr show [NIC_NAME]Further reading: Docker connection refused and setting the MTU for a network interface.
NO ROUTE TO HOST between two containers
Section titled “NO ROUTE TO HOST between two containers”This is a host firewall problem, not a Docker one. Work through it in order:
- Confirm the port is listening at all:
ss -ltnp | grep <port>. - Confirm it is reachable from the host:
nc -vz localhost <port>. - Watch the target container accept — or not accept — the connection:
docker logs -f containerA. - If the host can connect and the other container cannot, stop
firewalldbriefly to confirm the firewall is responsible, then start it again. - Fix it properly by enabling masquerading on the zone Docker’s bridge sits in, which is what lets container traffic route out through the host:
sudo firewall-cmd --zone=public --add-masquerade --permanentsudo firewall-cmd --reloadNote --permanent, spelled in full — without it the rule is lost at the next reload, and a
misspelling makes the command fail outright.