#!/usr/bin/env bash set -euo pipefail # ----------------------------------------------------------------------------- # Standalone Ubuntu deployment bootstrap # Download this single file to the server, make it executable, then run it. # Configuration # ----------------------------------------------------------------------------- if [[ -t 1 ]]; then COLOR_INFO='\033[1;34m' COLOR_WARN='\033[1;33m' COLOR_ERROR='\033[1;31m' COLOR_RESET='\033[0m' else COLOR_INFO='' COLOR_WARN='' COLOR_ERROR='' COLOR_RESET='' fi BASE_PACKAGES=( apt-transport-https build-essential ca-certificates curl git gnupg jq lsb-release software-properties-common unzip wget zip ) INSTALL_DOCKER=false INSTALL_NODE=false INSTALL_AWS=false INSTALL_AZURE=false INSTALL_ANSIBLE=true INSTALL_INFISICAL=true INSTALL_PYTHON=false INSTALL_PHP=false INSTALL_COMPOSER=false INSTALL_JAVA=false INSTALL_MAVEN=false INSTALL_GO=false INSTALL_COREPACK=false INSTALL_NGINX=false INSTALL_UFW=false INSTALL_FAIL2BAN=false INSTALL_CERTBOT=false INSTALL_PM2=false DEPLOY_NODE=false DEPLOY_PYTHON=false DEPLOY_DOCKER=false DEPLOY_LARAVEL=false DEPLOY_SPRINGBOOT=false DEPLOY_NEXT=false DEPLOY_NUXT=false DEPLOY_GO_FIBER=false UPDATE_ONLY=false # ----------------------------------------------------------------------------- # Logging and helpers # ----------------------------------------------------------------------------- log() { printf '%b[INFO]%b %s\n' "${COLOR_INFO}" "${COLOR_RESET}" "$1" } warn() { printf '%b[WARN]%b %s\n' "${COLOR_WARN}" "${COLOR_RESET}" "$1" } die() { printf '%b[ERROR]%b %s\n' "${COLOR_ERROR}" "${COLOR_RESET}" "$1" >&2 exit 1 } install_apt_packages() { DEBIAN_FRONTEND=noninteractive apt-get install -y "$@" } enable_service() { systemctl enable --now "$1" } run_when_enabled() { local enabled="$1" local handler="$2" if [[ "${enabled}" == true ]]; then "${handler}" fi } usage() { cat <<'EOF' Usage: ./setup_ubuntu_tools.sh [options] This is a standalone bootstrap script. You only need this single file on the Ubuntu server. Default behavior: - Update apt cache - Upgrade installed packages - Install common base tools for Ubuntu servers - Install Ansible and Infisical CLI Options: --deploy-laravel Install a ready-to-deploy Laravel server stack --deploy-springboot Install a ready-to-deploy Spring Boot server stack --deploy-next Install a ready-to-deploy Next.js server stack --deploy-nuxt Install a ready-to-deploy Nuxt server stack --deploy-go-fiber Install a ready-to-deploy Go Fiber server stack --deploy-node Install a ready-to-deploy Node.js server stack --deploy-python Install a ready-to-deploy Python server stack --deploy-docker Install a ready-to-deploy Docker server stack --docker Install Docker Engine and Docker Compose plugin --node Install Node.js LTS --aws Install AWS CLI v2 --azure Install Azure CLI --no-ansible Skip default Ansible installation --no-infisical Skip default Infisical CLI installation --python Install Python 3, venv and pip --php Install PHP runtime for Laravel-style apps --composer Install Composer --java Install OpenJDK 26 --maven Install Maven --go Install Go toolchain from Ubuntu repo --corepack Enable pnpm and yarn via Corepack --nginx Install and enable Nginx --ufw Install and configure UFW for SSH, HTTP, HTTPS --fail2ban Install and enable Fail2ban --certbot Install Certbot for Let's Encrypt --pm2 Install PM2 globally with npm --update-only Only run apt update and upgrade -h, --help Show this help Examples: wget https://your-server/setup_ubuntu_tools.sh chmod +x setup_ubuntu_tools.sh sudo ./setup_ubuntu_tools.sh --deploy-laravel ./setup_ubuntu_tools.sh --deploy-laravel ./setup_ubuntu_tools.sh --deploy-springboot ./setup_ubuntu_tools.sh --deploy-next ./setup_ubuntu_tools.sh --deploy-go-fiber ./setup_ubuntu_tools.sh --deploy-node ./setup_ubuntu_tools.sh --deploy-python ./setup_ubuntu_tools.sh --deploy-docker ./setup_ubuntu_tools.sh --docker --node --python --nginx sudo ./setup_ubuntu_tools.sh --docker --aws --azure EOF } require_root() { if [[ "${EUID}" -ne 0 ]]; then die "Please run this script with sudo or as root." fi } require_ubuntu() { if [[ ! -r /etc/os-release ]]; then die "Cannot detect operating system." fi . /etc/os-release if [[ "${ID:-}" != "ubuntu" ]]; then die "This script currently supports Ubuntu only." fi } # ----------------------------------------------------------------------------- # Installers # ----------------------------------------------------------------------------- system_update() { log "Updating apt package index" apt-get update -y log "Upgrading installed packages" DEBIAN_FRONTEND=noninteractive apt-get upgrade -y } install_base_tooling() { log "Installing base packages" install_apt_packages "${BASE_PACKAGES[@]}" } install_runtime_python() { log "Installing Python tools" install_apt_packages python3 python3-pip python3-venv } install_runtime_php() { log "Installing PHP runtime and common Laravel extensions" install_apt_packages \ php-cli \ php-common \ php-curl \ php-fpm \ php-mbstring \ php-mysql \ php-xml \ php-zip \ php-bcmath \ php-intl } install_runtime_composer() { log "Installing Composer" install_apt_packages composer } install_runtime_java() { log "Installing OpenJDK 26" install_apt_packages openjdk-26-jdk } install_runtime_maven() { log "Installing Maven" install_apt_packages maven } install_runtime_go() { log "Installing Go toolchain" install_apt_packages golang-go } install_service_nginx() { log "Installing Nginx" install_apt_packages nginx enable_service nginx } install_service_ufw() { log "Installing and configuring UFW" install_apt_packages ufw ufw allow OpenSSH ufw allow 'Nginx Full' ufw --force enable } install_service_fail2ban() { log "Installing Fail2ban" install_apt_packages fail2ban enable_service fail2ban } install_service_certbot() { log "Installing Certbot" install_apt_packages certbot python3-certbot-nginx } install_runtime_node() { log "Installing Node.js LTS" curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - install_apt_packages nodejs } install_runtime_corepack() { if ! command -v corepack >/dev/null 2>&1; then die "Corepack requires Node.js. Re-run with --node, --deploy-next, or --deploy-nuxt." fi log "Enabling corepack and preparing pnpm/yarn" corepack enable corepack prepare pnpm@latest --activate corepack prepare yarn@stable --activate } install_runtime_pm2() { if ! command -v npm >/dev/null 2>&1; then die "PM2 requires Node.js. Re-run with --node or --deploy-node." fi log "Installing PM2" npm install -g pm2 } install_cli_aws() { local tmp_dir tmp_dir="$(mktemp -d)" log "Installing AWS CLI v2" curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${tmp_dir}/awscliv2.zip" unzip -q "${tmp_dir}/awscliv2.zip" -d "${tmp_dir}" "${tmp_dir}/aws/install" --update rm -rf "${tmp_dir}" } install_cli_azure() { log "Installing Azure CLI" curl -fsSL https://aka.ms/InstallAzureCLIDeb | bash } install_cli_ansible() { log "Installing Ansible" install_apt_packages ansible } install_cli_infisical() { log "Installing Infisical CLI" curl -fsSL https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh | bash apt-get update -y install_apt_packages infisical } install_runtime_docker() { log "Setting up Docker repository" install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc . /etc/os-release echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" \ > /etc/apt/sources.list.d/docker.list apt-get update -y log "Installing Docker Engine" install_apt_packages \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin if [[ -n "${SUDO_USER:-}" ]]; then usermod -aG docker "${SUDO_USER}" || warn "Could not add ${SUDO_USER} to docker group" warn "Log out and back in to use Docker without sudo." fi } prepare_deploy_directories() { log "Creating common deployment directories" install -d -m 0755 /var/www/apps install -d -m 0755 /var/log/apps } enable_common_web_profile() { INSTALL_NGINX=true INSTALL_UFW=true INSTALL_FAIL2BAN=true INSTALL_CERTBOT=true } enable_profile_laravel() { INSTALL_PHP=true INSTALL_COMPOSER=true enable_common_web_profile } enable_profile_springboot() { INSTALL_JAVA=true INSTALL_MAVEN=true enable_common_web_profile } enable_profile_next() { INSTALL_NODE=true INSTALL_COREPACK=true INSTALL_PM2=true enable_common_web_profile } enable_profile_nuxt() { enable_profile_next } enable_profile_go_fiber() { INSTALL_GO=true enable_common_web_profile } enable_profile_node() { INSTALL_NODE=true INSTALL_PM2=true enable_common_web_profile } enable_profile_python() { INSTALL_PYTHON=true enable_common_web_profile } enable_profile_docker() { INSTALL_DOCKER=true enable_common_web_profile } # ----------------------------------------------------------------------------- # Profiles # ----------------------------------------------------------------------------- apply_deploy_profiles() { if [[ "${DEPLOY_NODE}" == true ]]; then enable_profile_node fi if [[ "${DEPLOY_PYTHON}" == true ]]; then enable_profile_python fi if [[ "${DEPLOY_DOCKER}" == true ]]; then enable_profile_docker fi if [[ "${DEPLOY_LARAVEL}" == true ]]; then enable_profile_laravel fi if [[ "${DEPLOY_SPRINGBOOT}" == true ]]; then enable_profile_springboot fi if [[ "${DEPLOY_NEXT}" == true ]]; then enable_profile_next fi if [[ "${DEPLOY_NUXT}" == true ]]; then enable_profile_nuxt fi if [[ "${DEPLOY_GO_FIBER}" == true ]]; then enable_profile_go_fiber fi } # ----------------------------------------------------------------------------- # Output # ----------------------------------------------------------------------------- show_versions() { log "Installed versions" command -v git >/dev/null 2>&1 && git --version || true command -v curl >/dev/null 2>&1 && curl --version | head -n 1 || true command -v jq >/dev/null 2>&1 && jq --version || true command -v python3 >/dev/null 2>&1 && python3 --version || true command -v php >/dev/null 2>&1 && php --version | head -n 1 || true command -v composer >/dev/null 2>&1 && composer --version || true command -v java >/dev/null 2>&1 && java --version | head -n 1 || true command -v mvn >/dev/null 2>&1 && mvn --version | head -n 1 || true command -v go >/dev/null 2>&1 && go version || true command -v node >/dev/null 2>&1 && node --version || true command -v npm >/dev/null 2>&1 && npm --version || true command -v pnpm >/dev/null 2>&1 && pnpm --version || true command -v yarn >/dev/null 2>&1 && yarn --version || true command -v pm2 >/dev/null 2>&1 && pm2 --version || true command -v ansible >/dev/null 2>&1 && ansible --version | head -n 1 || true command -v infisical >/dev/null 2>&1 && infisical --version || true command -v aws >/dev/null 2>&1 && aws --version || true command -v az >/dev/null 2>&1 && az version | jq -r '.["azure-cli"]' || true command -v docker >/dev/null 2>&1 && docker --version || true command -v nginx >/dev/null 2>&1 && nginx -v 2>&1 || true command -v certbot >/dev/null 2>&1 && certbot --version || true } # ----------------------------------------------------------------------------- # CLI # ----------------------------------------------------------------------------- parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --deploy-laravel) DEPLOY_LARAVEL=true ;; --deploy-springboot) DEPLOY_SPRINGBOOT=true ;; --deploy-next) DEPLOY_NEXT=true ;; --deploy-nuxt) DEPLOY_NUXT=true ;; --deploy-go-fiber) DEPLOY_GO_FIBER=true ;; --deploy-node) DEPLOY_NODE=true ;; --deploy-python) DEPLOY_PYTHON=true ;; --deploy-docker) DEPLOY_DOCKER=true ;; --docker) INSTALL_DOCKER=true ;; --node) INSTALL_NODE=true ;; --aws) INSTALL_AWS=true ;; --azure) INSTALL_AZURE=true ;; --no-ansible) INSTALL_ANSIBLE=false ;; --no-infisical) INSTALL_INFISICAL=false ;; --python) INSTALL_PYTHON=true ;; --php) INSTALL_PHP=true ;; --composer) INSTALL_COMPOSER=true ;; --java) INSTALL_JAVA=true ;; --maven) INSTALL_MAVEN=true ;; --go) INSTALL_GO=true ;; --corepack) INSTALL_COREPACK=true ;; --nginx) INSTALL_NGINX=true ;; --ufw) INSTALL_UFW=true ;; --fail2ban) INSTALL_FAIL2BAN=true ;; --certbot) INSTALL_CERTBOT=true ;; --pm2) INSTALL_PM2=true ;; --update-only) UPDATE_ONLY=true ;; -h|--help) usage exit 0 ;; *) die "Unknown option: $1" ;; esac shift done } # ----------------------------------------------------------------------------- # Main flow # ----------------------------------------------------------------------------- main() { parse_args "$@" require_root require_ubuntu apply_deploy_profiles system_update if [[ "${UPDATE_ONLY}" == true ]]; then log "Update-only mode complete" exit 0 fi install_base_tooling prepare_deploy_directories run_when_enabled "${INSTALL_PYTHON}" install_runtime_python run_when_enabled "${INSTALL_PHP}" install_runtime_php run_when_enabled "${INSTALL_COMPOSER}" install_runtime_composer run_when_enabled "${INSTALL_JAVA}" install_runtime_java run_when_enabled "${INSTALL_MAVEN}" install_runtime_maven run_when_enabled "${INSTALL_GO}" install_runtime_go run_when_enabled "${INSTALL_NODE}" install_runtime_node run_when_enabled "${INSTALL_COREPACK}" install_runtime_corepack run_when_enabled "${INSTALL_PM2}" install_runtime_pm2 run_when_enabled "${INSTALL_ANSIBLE}" install_cli_ansible run_when_enabled "${INSTALL_INFISICAL}" install_cli_infisical run_when_enabled "${INSTALL_AWS}" install_cli_aws run_when_enabled "${INSTALL_AZURE}" install_cli_azure run_when_enabled "${INSTALL_DOCKER}" install_runtime_docker run_when_enabled "${INSTALL_NGINX}" install_service_nginx run_when_enabled "${INSTALL_UFW}" install_service_ufw run_when_enabled "${INSTALL_FAIL2BAN}" install_service_fail2ban run_when_enabled "${INSTALL_CERTBOT}" install_service_certbot show_versions log "Bootstrap complete" } main "$@"