Add Docker/Compose always-check and SSH key import to AI stack setup

- Docker + Docker Compose check now runs on every invocation (not skipped
  on updates), installing if missing — ensures the stack can always run
- Added Docker Compose plugin install as fallback if compose is missing
- NVIDIA Container Toolkit check also always runs (not update-gated)
- Added SSH key import wizard question: import public keys from GitHub
  (gh:username) and/or Launchpad (lp:username) using ssh-import-id,
  so the machine is accessible over SSH immediately after setup

https://claude.ai/code/session_012gDnantBmFTWZGCiKyjazx
This commit is contained in:
Claude
2026-03-20 22:22:02 +00:00
parent 45dfa58d93
commit 796a073c0a
+67 -29
View File
@@ -137,13 +137,31 @@ fi
$INSTALL_AI || $INSTALL_KIWIX || $INSTALL_POSTINSTALL \
|| die "Nothing selected — run again and select at least one option."
# ── Q1b: SSH keys from GitHub / Launchpad ─────────────────────────────────────
SSH_IMPORT_IDS=() # list of "gh:username" or "lp:username" entries
if ! $IS_UPDATE || [[ ! -f "$HOME/.ssh/authorized_keys" ]]; then
echo ""
echo -e " ${BOLD}[SSH] Import SSH public keys? (for passwordless SSH into this machine)${NC}"
echo " Pulls your public keys from GitHub or Launchpad and adds them to"
echo " ~/.ssh/authorized_keys using ssh-import-id."
echo ""
echo " Examples: gh:yourusername lp:yourlaunchpadid"
echo " Multiple: gh:alice lp:alice"
echo ""
read -rp " Usernames (or Enter to skip): " SSH_INPUT
if [[ -n "$SSH_INPUT" ]]; then
read -ra SSH_IMPORT_IDS <<< "$SSH_INPUT"
fi
fi
# ── Q2: Storage for Ollama models ─────────────────────────────────────────────
OLLAMA_STORAGE="volume" # "volume" = Docker named volume, else a host path
OLLAMA_HOST_PATH=""
if $INSTALL_AI; then
echo ""
echo -e " ${BOLD}[2/5] Where should Ollama models be stored?${NC}"
echo -e " ${BOLD}[2/6] Where should Ollama models be stored?${NC}"
echo " (Models are large — 5-50GB each. A fast SSD or large HDD is ideal.)"
echo ""
echo " Available mount points with >20GB free:"
@@ -172,7 +190,7 @@ KIWIX_DIR="$BASE/kiwix" # default
if $INSTALL_KIWIX; then
echo ""
echo -e " ${BOLD}[3/5] Where should Kiwix ZIM files be stored?${NC}"
echo -e " ${BOLD}[3/6] Where should Kiwix ZIM files be stored?${NC}"
echo " (ZIMs are large — Wikipedia alone is ~46GB. Total collection ~130GB.)"
echo ""
echo " Available mount points with >50GB free:"
@@ -191,7 +209,7 @@ if $INSTALL_KIWIX; then
# ── Q4: Download ZIMs now? ─────────────────────────────────────────────────
echo ""
echo -e " ${BOLD}[4/5] Download ZIM files? (offline Wikipedia, Stack Overflow, etc.)${NC}"
echo -e " ${BOLD}[4/6] Download ZIM files? (offline Wikipedia, Stack Overflow, etc.)${NC}"
echo " Downloads run in the background — safe to start now and leave overnight."
echo " Total: ~130GB (Wikipedia 46GB, Project Gutenberg 60GB, others smaller)"
echo ""
@@ -211,7 +229,7 @@ if command -v ufw &>/dev/null && { [[ ! -f "$BASE/.ufw-done" ]] || $FORCE; }; th
echo ""
# Auto-detect likely subnet from current IP
AUTO_SUBNET=$(echo "$LOCAL_IP" | awk -F. '{print $1"."$2"."$3".0/24"}')
echo -e " ${BOLD}[4b] Firewall — allow LAN access to services${NC}"
echo -e " ${BOLD}[4b/6] Firewall — allow LAN access to services${NC}"
read -rp " LAN subnet [${AUTO_SUBNET}]: " LAN_INPUT
LAN_SUBNET="${LAN_INPUT:-$AUTO_SUBNET}"
[[ "$LAN_SUBNET" =~ /[0-9]+$ ]] || LAN_SUBNET="${LAN_SUBNET}/24"
@@ -223,7 +241,7 @@ PULL_DEEPSEEK=false
if $INSTALL_AI; then
echo ""
echo -e " ${BOLD}[5/5] Download AI models now?${NC}"
echo -e " ${BOLD}[5/6] Download AI models now?${NC}"
echo " GPU detected: ${GPU_NAME} (${VRAM_GB}GB VRAM) → recommended tier: ${GPU_TIER}"
echo ""
echo " Models that will be downloaded:"
@@ -248,6 +266,7 @@ echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━
echo ""
$INSTALL_POSTINSTALL && echo " ✓ Full system setup (ubuntu-post-install.sh — interactive)"
$INSTALL_AI && echo " ✓ AI stack (Ollama · WebUI · RAG · MCP · Gitea · InvokeAI · Portainer)"
[[ "${#SSH_IMPORT_IDS[@]}" -gt 0 ]] && echo " ✓ SSH keys → ${SSH_IMPORT_IDS[*]}"
$INSTALL_KIWIX && echo " ✓ Kiwix offline library"
if $INSTALL_AI; then
if [[ "$OLLAMA_STORAGE" == "bind" ]]; then
@@ -297,21 +316,28 @@ fi
# =============================================================================
section "Prerequisites"
# =============================================================================
if ! $IS_UPDATE; then
# Docker
if ! command -v docker &>/dev/null; then
info "Installing Docker..."
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER"
ok "Docker installed — log out/in or run: newgrp docker"
else
ok "Docker: $(docker --version)"
fi
# ── Docker (always check — required whether new install or update) ─────────────
if ! command -v docker &>/dev/null; then
info "Installing Docker..."
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER"
warn "Added $USER to docker group — changes take effect on next login."
warn "If docker commands fail, run: newgrp docker"
else
ok "Docker: $(docker --version | sed 's/Docker version //')"
fi
# NVIDIA Container Toolkit
if command -v nvidia-smi &>/dev/null && \
! dpkg -l 2>/dev/null | grep -q nvidia-container-toolkit; then
# Ensure Docker Compose plugin is present (included with modern Docker)
if ! docker compose version &>/dev/null; then
info "Installing docker-compose-plugin..."
sudo apt-get install -y docker-compose-plugin
fi
ok "Docker Compose: $(docker compose version --short 2>/dev/null || echo 'ok')"
# ── NVIDIA Container Toolkit ───────────────────────────────────────────────────
if command -v nvidia-smi &>/dev/null; then
if ! dpkg -l 2>/dev/null | grep -q nvidia-container-toolkit; then
info "Installing NVIDIA Container Toolkit..."
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor --yes \
@@ -324,20 +350,32 @@ if ! $IS_UPDATE; then
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
ok "NVIDIA Container Toolkit installed"
elif command -v nvidia-smi &>/dev/null; then
ok "NVIDIA Container Toolkit already present"
else
warn "No NVIDIA GPU detected — CPU-only (Ollama will use CPU)"
ok "NVIDIA Container Toolkit: already present"
fi
# ripgrep (used by MCP search_code tool)
if ! command -v rg &>/dev/null; then
sudo apt-get install -y ripgrep
ok "ripgrep installed"
fi
else
ok "Skipping prereq checks (update mode)"
info "No NVIDIA GPU detected — Ollama will run on CPU"
fi
# ── ripgrep (used by MCP search_code tool) ─────────────────────────────────────
if ! command -v rg &>/dev/null; then
sudo apt-get install -y ripgrep
ok "ripgrep installed"
fi
# ── SSH key import from GitHub / Launchpad ─────────────────────────────────────
if [[ "${#SSH_IMPORT_IDS[@]}" -gt 0 ]]; then
section "SSH Keys"
if ! command -v ssh-import-id &>/dev/null; then
info "Installing ssh-import-id..."
sudo apt-get install -y ssh-import-id
fi
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
for id in "${SSH_IMPORT_IDS[@]}"; do
info "Importing SSH keys: $id"
ssh-import-id "$id" && ok "Keys imported: $id" || warn "Failed to import: $id"
done
fi
# =============================================================================