Files
Docker-Install/install-docker.sh
T
2026-09-08 19:09:33 +00:00

250 lines
8.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_NAME="$(basename "$0")"
DEFAULT_USER="${SUDO_USER:-${USER:-}}"
DOCKER_USER=""
ADD_DOCKER_GROUP="ask"
INSTALL_PORTAINER="no"
PORTAINER_HTTPS_PORT="9443"
PORTAINER_EDGE_PORT="8000"
PORTAINER_IMAGE="portainer/portainer-ce:lts"
usage() {
cat <<'USAGE'
Install Docker Engine and Docker Compose plugin on a fresh Debian/Ubuntu server.
Optionally install Portainer CE after Docker is running.
Usage:
./install-docker.sh [options]
curl -fsSL <raw-url>/install-docker.sh | bash -s -- [options]
Options:
--user USER Add USER to the docker group after install.
--current-user Add the invoking user to the docker group.
--no-docker-user Do not add anyone to the docker group.
--portainer Install or start Portainer CE after Docker install.
--portainer-port PORT Host port for Portainer HTTPS UI. Default: 9443.
--portainer-edge-port PORT Host port for Portainer Edge agent tunnel. Default: 8000.
--portainer-image IMAGE Portainer image to run. Default: portainer/portainer-ce:lts.
-y, --yes Non-interactive defaults. Installs Docker and skips docker group unless --user/--current-user is also supplied.
-h, --help Show this help.
Examples:
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/install-docker.sh | bash
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/install-docker.sh | bash -s -- --user obin
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/install-docker.sh | bash -s -- --current-user
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/install-docker.sh | bash -s -- --current-user --portainer
USAGE
}
log() { printf '\n[Docker-Install] %s\n' "$*"; }
warn() { printf '\n[Docker-Install WARNING] %s\n' "$*" >&2; }
fail() { printf '\n[Docker-Install ERROR] %s\n' "$*" >&2; exit 1; }
require_port_number() {
local name="$1"
local value="$2"
[[ "$value" =~ ^[0-9]+$ ]] || fail "$name must be a number. Got: $value"
(( value >= 1 && value <= 65535 )) || fail "$name must be between 1 and 65535. Got: $value"
}
install_portainer() {
log "Installing Portainer CE"
require_port_number "--portainer-port" "$PORTAINER_HTTPS_PORT"
require_port_number "--portainer-edge-port" "$PORTAINER_EDGE_PORT"
$SUDO docker volume create portainer_data >/dev/null
if $SUDO docker container inspect portainer >/dev/null 2>&1; then
if [[ "$($SUDO docker inspect -f '{{.State.Running}}' portainer 2>/dev/null)" == "true" ]]; then
log "Portainer container already exists and is running"
else
log "Portainer container already exists; starting it"
$SUDO docker start portainer >/dev/null
fi
else
$SUDO docker run -d \
--name portainer \
--restart=always \
-p "${PORTAINER_EDGE_PORT}:8000" \
-p "${PORTAINER_HTTPS_PORT}:9443" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
"$PORTAINER_IMAGE" >/dev/null
fi
local host_ip
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
[[ -n "$host_ip" ]] || host_ip="<server-ip>"
log "Portainer is available at: https://${host_ip}:${PORTAINER_HTTPS_PORT}"
warn "Portainer uses a self-signed certificate by default, so your browser may show a warning."
}
while [[ $# -gt 0 ]]; do
case "$1" in
--user)
[[ $# -ge 2 ]] || fail "--user requires a username"
DOCKER_USER="$2"
ADD_DOCKER_GROUP="yes"
shift 2
;;
--current-user)
[[ -n "$DEFAULT_USER" && "$DEFAULT_USER" != "root" ]] || fail "Could not determine a non-root invoking user. Use --user USER instead."
DOCKER_USER="$DEFAULT_USER"
ADD_DOCKER_GROUP="yes"
shift
;;
--no-docker-user)
ADD_DOCKER_GROUP="no"
DOCKER_USER=""
shift
;;
--portainer)
INSTALL_PORTAINER="yes"
shift
;;
--portainer-port)
[[ $# -ge 2 ]] || fail "--portainer-port requires a port number"
PORTAINER_HTTPS_PORT="$2"
shift 2
;;
--portainer-edge-port)
[[ $# -ge 2 ]] || fail "--portainer-edge-port requires a port number"
PORTAINER_EDGE_PORT="$2"
shift 2
;;
--portainer-image)
[[ $# -ge 2 ]] || fail "--portainer-image requires an image name"
PORTAINER_IMAGE="$2"
shift 2
;;
-y|--yes)
[[ "$ADD_DOCKER_GROUP" == "ask" ]] && ADD_DOCKER_GROUP="no"
shift
;;
-h|--help)
usage
exit 0
;;
*)
fail "Unknown option: $1. Run with --help for usage."
;;
esac
done
if [[ "${EUID}" -ne 0 ]]; then
if command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
fail "This installer needs root privileges. Re-run as root or install sudo."
fi
else
SUDO=""
fi
if [[ ! -r /etc/os-release ]]; then
fail "Cannot read /etc/os-release. This installer supports Debian/Ubuntu style systems."
fi
# shellcheck disable=SC1091
. /etc/os-release
OS_ID="${ID:-}"
OS_CODENAME="${VERSION_CODENAME:-}"
case "$OS_ID" in
ubuntu|debian) ;;
*) fail "Unsupported OS '${OS_ID:-unknown}'. This script currently supports Ubuntu and Debian." ;;
esac
if [[ -z "$OS_CODENAME" ]] && command -v lsb_release >/dev/null 2>&1; then
OS_CODENAME="$(lsb_release -cs)"
fi
[[ -n "$OS_CODENAME" ]] || fail "Could not determine OS codename for Docker apt repo."
if [[ "$ADD_DOCKER_GROUP" == "ask" ]]; then
if [[ -n "$DEFAULT_USER" && "$DEFAULT_USER" != "root" && -t 0 ]]; then
read -r -p "Add '${DEFAULT_USER}' to the docker group for passwordless docker commands? [y/N]: " reply
case "$reply" in
[Yy]*) ADD_DOCKER_GROUP="yes"; DOCKER_USER="$DEFAULT_USER" ;;
*) ADD_DOCKER_GROUP="no" ;;
esac
else
ADD_DOCKER_GROUP="no"
fi
fi
if [[ "$ADD_DOCKER_GROUP" == "yes" ]]; then
[[ -n "$DOCKER_USER" ]] || fail "Docker group add requested but no user was selected. Use --user USER."
id "$DOCKER_USER" >/dev/null 2>&1 || fail "User '$DOCKER_USER' does not exist. Create the user first or choose a different username."
fi
log "Installing prerequisites"
$SUDO apt-get update
$SUDO apt-get install -y ca-certificates curl gnupg
log "Adding Docker's official apt repository for ${OS_ID} ${OS_CODENAME}"
$SUDO install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/${OS_ID}/gpg" | $SUDO gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
$SUDO chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${OS_ID} ${OS_CODENAME} stable" \
| $SUDO tee /etc/apt/sources.list.d/docker.list >/dev/null
log "Installing Docker Engine and Compose plugin"
$SUDO apt-get update
$SUDO apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
log "Enabling and starting Docker"
$SUDO systemctl enable --now docker
if [[ "$ADD_DOCKER_GROUP" == "yes" ]]; then
log "Adding '${DOCKER_USER}' to the docker group"
$SUDO groupadd -f docker
$SUDO usermod -aG docker "$DOCKER_USER"
fi
log "Verifying install"
$SUDO docker --version
docker compose version || $SUDO docker compose version
$SUDO docker run --rm hello-world >/dev/null
if [[ "$INSTALL_PORTAINER" == "yes" ]]; then
install_portainer
fi
cat <<EOF
Docker install completed successfully.
Next steps:
EOF
if [[ "$ADD_DOCKER_GROUP" == "yes" ]]; then
cat <<EOF
- '${DOCKER_USER}' was added to the docker group.
- Log out and back in, or run: newgrp docker
- Then test without sudo: docker run --rm hello-world
EOF
else
cat <<EOF
- Use Docker with sudo, or add a user later with:
sudo usermod -aG docker <username>
newgrp docker
EOF
fi
if [[ "$INSTALL_PORTAINER" == "yes" ]]; then
cat <<EOF
- Finish Portainer setup in your browser:
https://<server-ip>:${PORTAINER_HTTPS_PORT}
EOF
else
cat <<EOF
- To install Portainer later, re-run with --portainer or run:
docker volume create portainer_data
docker run -d --name portainer --restart=always -p 8000:8000 -p 9443:9443 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:lts
EOF
fi