#!/usr/bin/env bash set -euo pipefail log() { printf '\n[%s] %s\n' "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" "$*" } require_root() { if [[ ${EUID:-$(id -u)} -ne 0 ]]; then echo "Please run this script as root or with sudo." >&2 exit 1 fi } command_exists() { command -v "$1" >/dev/null 2>&1 } get_os_info() { if [[ ! -f /etc/os-release ]]; then echo "Cannot detect OS: /etc/os-release not found." >&2 exit 1 fi # shellcheck disable=SC1091 . /etc/os-release OS_ID="${ID:-}" OS_LIKE="${ID_LIKE:-}" OS_VERSION_ID="${VERSION_ID:-}" OS_CODENAME="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}" OS_NAME="${PRETTY_NAME:-${NAME:-unknown}}" } install_debian_family() { log "Detected Debian/Ubuntu family: ${OS_NAME}" apt update apt install -y ca-certificates curl install -m 0755 -d /etc/apt/keyrings local repo_base case "$OS_ID" in ubuntu) repo_base="https://download.docker.com/linux/ubuntu" ;; debian) repo_base="https://download.docker.com/linux/debian" ;; *) # Fallback for derivatives repo_base="https://download.docker.com/linux/ubuntu" ;; esac curl -fsSL "${repo_base}/gpg" -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc if [[ -z "${OS_CODENAME}" ]]; then echo "Could not determine distro codename from /etc/os-release." >&2 exit 1 fi cat >/etc/apt/sources.list.d/docker.sources </dev/null 2>&1 || systemctl enable --now docker; then log "Docker service enabled and started." else log "Docker installed, but systemd could not enable/start it automatically." systemctl start docker || true fi fi } install_rhel_family() { log "Detected RHEL/Alma/Rocky/CentOS family: ${OS_NAME}" if ! command_exists dnf; then echo "dnf is required for RHEL-family installation but was not found." >&2 exit 1 fi dnf -y install dnf-plugins-core dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin if command_exists systemctl; then systemctl enable --now docker log "Docker service enabled and started." fi } verify_install() { log "Verifying installation" if command_exists docker; then docker --version else echo "docker command not found after installation." >&2 exit 1 fi if docker compose version; then : else echo "docker compose plugin not available after installation." >&2 exit 1 fi if command_exists systemctl; then systemctl --no-pager --full status docker || true fi } main() { require_root get_os_info case "$OS_ID" in ubuntu|debian) install_debian_family ;; almalinux|rhel|rocky|centos|fedora) install_rhel_family ;; *) if [[ "$OS_LIKE" == *debian* ]]; then install_debian_family elif [[ "$OS_LIKE" == *rhel* ]] || [[ "$OS_LIKE" == *fedora* ]]; then install_rhel_family else echo "Unsupported OS: ${OS_NAME} (ID=${OS_ID}, ID_LIKE=${OS_LIKE})" >&2 exit 1 fi ;; esac verify_install log "Done." } main "$@"