Files
vps/setup_ubuntu_env.sh

606 lines
15 KiB
Bash

#!/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 21
--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 21"
install_apt_packages openjdk-21-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 "$@"