Skip to content

Instantly share code, notes, and snippets.

@nukhes
Created August 9, 2025 21:21
Show Gist options
  • Save nukhes/cf4a7315aefcd5726b82312ea50c707a to your computer and use it in GitHub Desktop.
Save nukhes/cf4a7315aefcd5726b82312ea50c707a to your computer and use it in GitHub Desktop.
#!/bin/bash
# install_docker_agnostic.sh
# A comprehensive, agnostic bash script for Docker Engine installation and rootless configuration on Linux.
set -e
# --- Global Variables ---
DISTRO_ID=""
DISTRO_LIKE=""
PACKAGE_MANAGER=""
KERNEL_VERSION=""
USER_CHOICE_NON_ROOT=""
# --- Functions ---
# Function to log messages with consistent formatting
log_message() {
echo ">>> $(date +'%Y-%m-%d %H:%M:%S') - $1"
}
# Function to check for sudo privileges
check_sudo() {
log_message "Checking for sudo privileges..."
if! command -v sudo &> /dev/null; then
log_message "Error: sudo is not installed. Please install sudo or run as root."
exit 1
fi
if! sudo -l &> /dev/null; then
log_message "Error: Current user does not have sudo privileges. Please ensure your user is in the sudoers file."
exit 1
fi
log_message "Sudo privileges confirmed." [1, 2, 3]
}
# Function to check internet connectivity
check_internet_connectivity() {
log_message "Checking internet connectivity..."
if! curl -sSf https://download.docker.com/linux/static/stable/ > /dev/null; then
log_message "Error: No internet connectivity. Please check your network connection."
exit 1
fi
log_message "Internet connectivity confirmed."
}
# Function to check kernel version
check_kernel_version() {
log_message "Checking kernel version..."
KERNEL_VERSION=$(uname -r)
# Extract major and minor version for comparison
MAJOR_KERNEL=$(echo "$KERNEL_VERSION" | cut -d'.' -f1)
MINOR_KERNEL=$(echo "$KERNEL_VERSION" | cut -d'.' -f2)
if (( MAJOR_KERNEL < 3 |
| (MAJOR_KERNEL == 3 && MINOR_KERNEL < 10) )); then
log_message "Warning: Docker Engine requires Linux kernel version 3.10 or higher. Your kernel is $KERNEL_VERSION. Installation may fail." [1, 4, 5, 6]
read -p "Do you wish to continue anyway? (y/N): " continue_install
if$ ]]; then
log_message "Installation aborted due to low kernel version."
exit 1
fi
else
log_message "Kernel version $KERNEL_VERSION meets Docker requirements." [1, 4, 5, 6]
fi
}
# Function to check for existing Docker installation
check_existing_docker() {
log_message "Checking for existing Docker installation..."
if command -v docker &> /dev/null; then
log_message "Existing Docker installation detected: $(docker --version). This script will attempt to install/update Docker." [7]
read -p "Do you want to proceed with installation/update? (y/N): " proceed_existing
if$ ]]; then
log_message "Installation aborted by user."
exit 1
fi
else
log_message "No existing Docker installation found."
fi
}
# Function to detect Linux distribution and package manager
detect_distro_and_pm() {
log_message "Detecting Linux distribution and package manager..."
if [[ -f /etc/os-release ]]; then
# Source /etc/os-release to get distribution information [8, 9, 10, 5, 11, 12, 13]
. /etc/os-release
DISTRO_ID=$ID
DISTRO_LIKE=$ID_LIKE
log_message "Detected OS: $NAME (ID: $DISTRO_ID, ID_LIKE: $DISTRO_LIKE)" [8]
else
log_message "Error: /etc/os-release not found. Cannot reliably detect distribution."
exit 1
fi
if command -v apt &> /dev/null; then
PACKAGE_MANAGER="apt"
elif command -v dnf &> /dev/null; then
PACKAGE_MANAGER="dnf"
elif command -v yum &> /dev/null; then
PACKAGE_MANAGER="yum" # Fallback for older RHEL/CentOS [11]
elif command -v pacman &> /dev/null; then
PACKAGE_MANAGER="pacman"
elif command -v zypper &> /dev/null; then
PACKAGE_MANAGER="zypper"
else
log_message "Error: No supported package manager (apt, dnf, yum, pacman, zypper) found." [11]
exit 1
fi
log_message "Detected package manager: $PACKAGE_MANAGER." [11]
}
# Function to install Docker on Debian/Ubuntu (APT-based systems)
install_docker_apt() {
log_message "Installing Docker Engine on Debian/Ubuntu..."
sudo apt update -y [1, 14]
sudo apt install -y ca-certificates curl gnupg lsb-release [1, 14]
log_message "Adding Docker's official GPG key..."
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg [1, 14]
sudo chmod a+r /etc/apt/keyrings/docker.gpg
log_message "Setting up the stable Docker repository..."
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null [1, 14]
log_message "Updating apt package index again and installing Docker packages..."
sudo apt update -y [1, 14]
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin [1, 14]
log_message "Docker Engine installation complete for Debian/Ubuntu."
}
# Function to install Docker on RHEL/CentOS/Fedora (DNF-based systems)
install_docker_dnf() {
log_message "Installing Docker Engine on RHEL/CentOS/Fedora..."
log_message "Removing any conflicting unofficial Docker packages..."
sudo dnf remove -y docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine \
podman \
runc |
| true # Continue if packages are not found [2, 15]
log_message "Installing dnf-plugins-core and setting up Docker repository..."
sudo dnf -y install dnf-plugins-core [2, 15, 16]
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo |
| \
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo |
| \
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo [2, 15, 16]
log_message "Installing Docker Engine and its dependencies..."
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin [2, 15, 16]
log_message "Docker Engine installation complete for RHEL/CentOS/Fedora."
}
# Function to install Docker on Arch Linux (Pacman-based systems)
install_docker_pacman() {
log_message "Installing Docker Engine on Arch Linux..."
log_message "Updating pacman package database and installing Docker..."
sudo pacman -Syu --noconfirm docker [17]
log_message "Docker Engine installation complete for Arch Linux."
}
# Function for post-installation verification
post_installation_verification() {
log_message "Enabling and starting Docker service..."
sudo systemctl enable --now docker [2, 15, 17, 16]
log_message "Verifying Docker installation by running hello-world container..."
sudo docker run hello-world [14, 2, 15, 3, 4, 17, 16]
log_message "Docker installation verified successfully."
}
# Function to set up Docker Rootless mode
setup_rootless_mode() {
log_message "Setting up Docker in Rootless mode (Recommended for security)..."
log_message "Installing Rootless mode prerequisites (requires sudo for system packages)..."
case "$PACKAGE_MANAGER" in
apt)
sudo apt update -y
sudo apt install -y uidmap dbus-user-session [1, 18]
if]; then # Debian 11 specific [1, 18]
sudo apt install -y fuse-overlayfs [1, 18]
fi
sudo apt install -y slirp4netns |
| true # slirp4netns might be installed or not needed [1, 18]
;;
dnf|yum)
sudo dnf install -y newuidmap newgidmap dbus-daemon-tools |
| sudo yum install -y newuidmap newgidmap dbus-daemon-tools
sudo dnf install -y fuse-overlayfs slirp4netns |
| sudo yum install -y fuse-overlayfs slirp4netns |
| true
;;
pacman)
sudo pacman -Syu --noconfirm newuidmap newgidmap dbus fuse-overlayfs slirp4netns |
| true
;;
*)
log_message "Warning: Automatic installation of Rootless prerequisites not supported for $PACKAGE_MANAGER. Please install 'newuidmap', 'newgidmap', 'dbus-user-session' (or equivalent), 'fuse-overlayfs' (if on Debian 11), and 'slirp4netns' manually."
;;
esac
log_message "Running dockerd-rootless-setuptool.sh install..."
# Ensure dockerd-rootless-setuptool.sh is available, it's usually part of docker-ce-rootless-extras
if! command -v dockerd-rootless-setuptool.sh &> /dev/null; then
log_message "Error: dockerd-rootless-setuptool.sh not found. Please ensure 'docker-ce-rootless-extras' package is installed."
log_message "For Debian/Ubuntu: sudo apt install -y docker-ce-rootless-extras"
log_message "For RHEL/CentOS/Fedora: sudo dnf install -y docker-ce-rootless-extras"
log_message "For Arch Linux: It's usually part of the 'docker' package or 'docker-git'."
exit 1
fi
dockerd-rootless-setuptool.sh install [1, 18]
log_message "Enabling systemd lingering for current user ($USER) to auto-start rootless daemon on boot..."
sudo loginctl enable-linger "$USER" [1, 18]
log_message "Rootless mode setup complete. Please add the following to your shell profile (~/.bashrc, ~/.profile, etc.) and re-login or source it:"
echo "export PATH=/usr/bin:$PATH" [1, 18]
echo "export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock" [1, 18]
log_message "You can now run 'docker run hello-world' without sudo."
}
# Function to add user to docker group
setup_docker_group() {
log_message "Setting up Docker for non-root access by adding user to 'docker' group (less secure than Rootless mode)."
log_message "WARNING: Adding a user to the 'docker' group grants root-level privileges to that user. This is a significant security risk. Rootless mode is highly recommended instead." [1, 19, 20]
if! grep -q "^docker:" /etc/group; then
log_message "Creating 'docker' group..."
sudo groupadd docker [1, 19, 20, 21]
else
log_message "'docker' group already exists."
fi
log_message "Adding current user ($USER) to the 'docker' group..."
sudo usermod -aG docker "$USER" [1, 19, 7, 20, 21, 22]
log_message "Group membership updated. For changes to take effect, you must either:"
log_message "1. Log out and log back in." [1, 19, 7, 20, 21]
log_message "2. Run 'newgrp docker' in your current terminal session (this will start a new shell)." [1, 19, 7, 20, 21]
log_message "After re-logging in or running 'newgrp docker', verify with 'groups' command that 'docker' is listed." [16, 9, 23]
log_message "Checking and fixing permissions for ~/.docker directory if necessary..."
if [[ -d "$HOME/.docker" ]]; then
sudo chown "$USER":"$USER" "$HOME/.docker" -R [1, 19]
sudo chmod g+rwx "$HOME/.docker" -R [1, 19]
log_message "Permissions for ~/.docker adjusted."
fi
log_message "You should now be able to run 'docker run hello-world' without sudo after re-logging in or using 'newgrp docker'."
}
# --- Main Script Execution ---
log_message "Starting Docker Engine installation script."
check_sudo
check_internet_connectivity
check_kernel_version
check_existing_docker
detect_distro_and_pm
log_message "Proceeding with Docker Engine installation for $DISTRO_ID using $PACKAGE_MANAGER..."
case "$PACKAGE_MANAGER" in
apt)
install_docker_apt
;;
dnf|yum)
install_docker_dnf
;;
pacman)
install_docker_pacman
;;
*)
log_message "Error: Unsupported distribution or package manager. Exiting."
exit 1
;;
esac
post_installation_verification
log_message "Docker Engine is installed and running."
# --- Non-Root Setup Choice ---
echo ""
log_message "Choose your preferred method for non-root Docker access:"
echo "1. Rootless Mode (Recommended for security, daemon runs as non-root user)"
echo "2. Add user to 'docker' group (Simpler, but grants root-equivalent privileges)"
echo "3. Skip non-root setup (You will need to use 'sudo' for all Docker commands)"
read -p "Enter your choice (1, 2, or 3): " USER_CHOICE_NON_ROOT
case "$USER_CHOICE_NON_ROOT" in
1)
setup_rootless_mode
;;
2)
setup_docker_group
;;
3)
log_message "Skipping non-root setup. You will need to use 'sudo' for all Docker commands."
;;
*)
log_message "Invalid choice. Skipping non-root setup. You will need to use 'sudo' for all Docker commands."
;;
esac
log_message "Docker Engine installation and configuration script finished."
log_message "Please remember to re-login or open a new terminal session for group changes/environment variables to take effect."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment