Last active
February 3, 2026 02:07
-
-
Save tmasjc/1065706f784850d9ec2ce540249ac6c7 to your computer and use it in GitHub Desktop.
compute server setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # Ubuntu bootstrap: base tools + Docker + Python (deadsnakes) + optional GPU utils + Ollama | |
| # Usage: | |
| # ./bootstrap.sh | |
| # ./bootstrap.sh --with-nvidia --nvidia-version 535 | |
| # ./bootstrap.sh --with-amd | |
| # ./bootstrap.sh --no-snap | |
| # ./bootstrap.sh --no-ollama | |
| # ./bootstrap.sh --python 3.11 | |
| # | |
| # Notes: | |
| # - Docker group change requires logout/login (or: newgrp docker) to take effect. | |
| # - Script is idempotent: safe to re-run. | |
| WITH_NVIDIA=0 | |
| WITH_AMD=0 | |
| NO_SNAP=0 | |
| NO_OLLAMA=0 | |
| PY_VER="3.11" | |
| NVIDIA_VER="535" | |
| log() { printf "\n==> %s\n" "$*"; } | |
| have_cmd() { command -v "$1" >/dev/null 2>&1; } | |
| apt_install() { | |
| # Install only packages that aren't already installed (reduces noise + speeds reruns) | |
| local pkgs=("$@") | |
| local missing=() | |
| for p in "${pkgs[@]}"; do | |
| dpkg -s "$p" >/dev/null 2>&1 || missing+=("$p") | |
| done | |
| if ((${#missing[@]})); then | |
| sudo apt-get install -y "${missing[@]}" | |
| fi | |
| } | |
| ensure_snap() { | |
| if (( NO_SNAP )); then | |
| log "Skipping snap (per --no-snap)" | |
| return 0 | |
| fi | |
| if ! have_cmd snap; then | |
| log "Installing snapd" | |
| sudo apt-get update -y | |
| apt_install snapd | |
| fi | |
| } | |
| snap_install_if_missing() { | |
| local name="$1" | |
| if (( NO_SNAP )); then return 0; fi | |
| if ! snap list 2>/dev/null | awk '{print $1}' | grep -qx "$name"; then | |
| sudo snap install "$name" | |
| fi | |
| } | |
| add_docker_repo() { | |
| log "Setting up Docker APT repo" | |
| apt_install ca-certificates gnupg | |
| sudo install -m 0755 -d /etc/apt/keyrings | |
| if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then | |
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg | |
| sudo chmod a+r /etc/apt/keyrings/docker.gpg | |
| fi | |
| local codename | |
| codename="$(. /etc/os-release && echo "${VERSION_CODENAME}")" | |
| local arch | |
| arch="$(dpkg --print-architecture)" | |
| local listfile="/etc/apt/sources.list.d/docker.list" | |
| local line="deb [arch=${arch} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${codename} stable" | |
| if [[ ! -f "$listfile" ]] || ! grep -qxF "$line" "$listfile"; then | |
| echo "$line" | sudo tee "$listfile" >/dev/null | |
| fi | |
| sudo apt-get update -y | |
| } | |
| install_docker() { | |
| log "Installing Docker Engine" | |
| add_docker_repo | |
| apt_install docker-ce docker-ce-cli containerd.io | |
| # Ensure service is enabled/started (no error if already running) | |
| sudo systemctl enable --now docker >/dev/null 2>&1 || true | |
| # Add current user to docker group | |
| if id -nG "$USER" | tr ' ' '\n' | grep -qx docker; then | |
| : | |
| else | |
| sudo usermod -aG docker "$USER" | |
| fi | |
| } | |
| install_base_tools() { | |
| log "Updating system + installing base tools" | |
| sudo apt-get update -y | |
| sudo apt-get upgrade -y | |
| apt_install \ | |
| curl wget git vim htop unzip net-tools \ | |
| build-essential software-properties-common \ | |
| tmux tree fd-find ripgrep | |
| } | |
| install_python() { | |
| log "Installing Python (system + deadsnakes ${PY_VER})" | |
| apt_install python3 python3-pip python3-venv | |
| # Add deadsnakes PPA once | |
| if [[ ! -f /etc/apt/sources.list.d/deadsnakes-ubuntu-ppa*.list ]] && \ | |
| ! grep -Rqs "^deb .\+deadsnakes/ppa" /etc/apt/sources.list /etc/apt/sources.list.d/*; then | |
| sudo add-apt-repository -y ppa:deadsnakes/ppa | |
| sudo apt-get update -y | |
| fi | |
| apt_install "python${PY_VER}" "python${PY_VER}-venv" "python${PY_VER}-dev" | |
| } | |
| install_gpu_utils() { | |
| if (( WITH_NVIDIA )); then | |
| log "Installing NVIDIA utilities (nvidia-utils-${NVIDIA_VER})" | |
| apt_install "nvidia-utils-${NVIDIA_VER}" | |
| fi | |
| if (( WITH_AMD )); then | |
| log "Installing AMD ROCm SMI (rocm-smi)" | |
| apt_install rocm-smi | |
| fi | |
| } | |
| install_ollama() { | |
| if (( NO_OLLAMA )); then | |
| log "Skipping Ollama (per --no-ollama)" | |
| return 0 | |
| fi | |
| if have_cmd ollama; then | |
| log "Ollama already installed" | |
| return 0 | |
| fi | |
| log "Installing Ollama" | |
| curl -fsSL https://ollama.com/install.sh | sh | |
| } | |
| usage() { | |
| cat <<'EOF' | |
| bootstrap.sh [options] | |
| Options: | |
| --with-nvidia Install nvidia-utils (default off) | |
| --nvidia-version N NVIDIA utils version (default: 535) | |
| --with-amd Install rocm-smi (default off) | |
| --python X.Y Install Python X.Y from deadsnakes (default: 3.11) | |
| --no-snap Skip snap and btop installation | |
| --no-ollama Skip Ollama installation | |
| -h, --help Show help | |
| EOF | |
| } | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --with-nvidia) WITH_NVIDIA=1; shift ;; | |
| --nvidia-version) NVIDIA_VER="${2:?}"; shift 2 ;; | |
| --with-amd) WITH_AMD=1; shift ;; | |
| --python) PY_VER="${2:?}"; shift 2 ;; | |
| --no-snap) NO_SNAP=1; shift ;; | |
| --no-ollama) NO_OLLAMA=1; shift ;; | |
| -h|--help) usage; exit 0 ;; | |
| *) echo "Unknown option: $1"; usage; exit 1 ;; | |
| esac | |
| done | |
| install_base_tools | |
| # btop + dust via snap (optional) | |
| ensure_snap | |
| if (( ! NO_SNAP )); then | |
| log "Installing btop (snap)" | |
| snap_install_if_missing btop | |
| log "Installing dust (snap)" | |
| snap_install_if_missing dust | |
| fi | |
| install_docker | |
| install_python | |
| install_gpu_utils | |
| install_ollama | |
| log "Done." | |
| echo "If you were added to the docker group, run: newgrp docker (or logout/login) before using 'docker' without sudo." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment