|
#!/bin/bash |
|
|
|
#=============================================================================== |
|
# Title: VPS Setup Script for Bun Applications |
|
# Description: Automated setup script for deploying Bun applications on a VPS |
|
# with Nginx reverse proxy, SSL, and PM2 process management. |
|
# |
|
# Author: Travis Aaron Wagner <[email protected]> |
|
# Version: 0.3.0 |
|
# |
|
# Dependencies: |
|
# - Ubuntu/Debian-based system |
|
# - Root access |
|
# - Internet connection |
|
# |
|
# Usage: |
|
# 1. ssh into your VPS |
|
# 2. Run the following command: |
|
# `bash <(curl -fsSL https://techsavvytravvy.com/public/setup-vps-bun.sh)` |
|
# |
|
# Features: |
|
# - Automated system updates |
|
# - Nginx installation and configuration |
|
# - SSL certificate setup via Let's Encrypt |
|
# - Bun runtime installation |
|
# - PM2 process manager setup |
|
# - Basic firewall configuration |
|
# - Automatic application directory structure |
|
# - Reverse proxy configuration |
|
# - Full revert capability |
|
# |
|
# Notes: |
|
# - Requires valid domain name pointing to server IP |
|
# - Assumes clean Ubuntu/Debian installation |
|
# - Creates application in /var/www/{domain} |
|
# - Logs to /var/log/vps-setup.log |
|
# |
|
# License: MIT |
|
#=============================================================================== |
|
|
|
## SETUP SCRIPT |
|
|
|
VERSION="0.3.0" |
|
|
|
# Exit on error, but allow for proper error handling |
|
set -eo pipefail |
|
|
|
# Configuration variables |
|
LOG_FILE="/var/log/vps-setup.log" |
|
DOMAIN="" # set by user input |
|
NGINX_AVAILABLE="/etc/nginx/sites-available" |
|
NGINX_ENABLED="/etc/nginx/sites-enabled" |
|
APP_ROOT="/var/www" |
|
SERVER_IP=$(curl -s4 ifconfig.me) |
|
ADMIN_EMAIL="" # set by user input |
|
NODE_ENV="production" |
|
BUN_PORT=3000 |
|
|
|
# Color codes for output |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
NC='\033[0m' # No Color |
|
|
|
# Logging function |
|
log() { |
|
local level=$1 |
|
shift |
|
local message="$*" |
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S') |
|
echo -e "${timestamp} [${level}] ${message}" | tee -a "$LOG_FILE" |
|
} |
|
|
|
# Success message with checkmark |
|
success() { |
|
echo -e "${GREEN}✓ $1${NC}" | tee -a "$LOG_FILE" |
|
} |
|
|
|
# Error message with ✗ |
|
error() { |
|
echo -e "${RED}✗ $1${NC}" | tee -a "$LOG_FILE" |
|
exit 1 |
|
} |
|
|
|
# Warning message with ! |
|
warn() { |
|
echo -e "${YELLOW}! $1${NC}" | tee -a "$LOG_FILE" |
|
} |
|
|
|
# Check if a command exists |
|
command_exists() { |
|
command -v "$1" >/dev/null 2>&1 |
|
} |
|
|
|
# Check if running as root |
|
check_root() { |
|
if [[ $EUID -ne 0 ]]; then |
|
error "This script must be run as root" |
|
fi |
|
} |
|
|
|
# Validate domain name |
|
validate_domain() { |
|
if [[ ! $1 =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then |
|
error "Invalid domain name format" |
|
return 1 |
|
fi |
|
return 0 |
|
} |
|
|
|
# Get domain name from user |
|
get_domain() { |
|
while true; do |
|
read -p "Enter your domain name (without www): " DOMAIN |
|
if validate_domain "$DOMAIN"; then |
|
success "Domain name validated: $DOMAIN" |
|
break |
|
else |
|
warn "Please enter a valid domain name" |
|
fi |
|
done |
|
} |
|
|
|
# Update system packages |
|
update_system() { |
|
log "INFO" "Checking for system updates..." |
|
if ! apt update; then |
|
error "Failed to check for updates" |
|
fi |
|
|
|
# Check if any upgrades are available |
|
if apt list --upgradable 2>/dev/null | grep -q "upgradable"; then |
|
log "INFO" "Updates available, upgrading system packages..." |
|
if apt upgrade -y; then |
|
success "System packages updated" |
|
else |
|
error "Failed to update system packages" |
|
fi |
|
else |
|
success "System is already up to date" |
|
fi |
|
} |
|
|
|
# Install required packages |
|
install_packages() { |
|
local packages=("nginx" "certbot" "python3-certbot-nginx" "curl" "unzip" "ca-certificates" "gnupg") |
|
|
|
for package in "${packages[@]}"; do |
|
if ! dpkg -s "$package" >/dev/null 2>&1; then |
|
log "INFO" "Installing $package..." |
|
if apt install -y "$package"; then |
|
success "$package installed" |
|
else |
|
error "Failed to install $package" |
|
fi |
|
else |
|
warn "$package is already installed" |
|
fi |
|
done |
|
} |
|
|
|
# Install Node.js |
|
install_nodejs() { |
|
if ! command_exists node; then |
|
log "INFO" "Installing Node.js..." |
|
|
|
# Install Node.js from Ubuntu's default repository |
|
apt update |
|
apt install -y nodejs npm |
|
success "Node.js installed" |
|
else |
|
warn "Node.js is already installed" |
|
fi |
|
} |
|
|
|
# Install Bun |
|
install_bun() { |
|
if ! command_exists bun; then |
|
log "INFO" "Installing Bun..." |
|
curl -fsSL https://bun.sh/install | bash |
|
|
|
# Add Bun to PATH for the current session |
|
export BUN_INSTALL="$HOME/.bun" |
|
export PATH=$BUN_INSTALL/bin:$PATH |
|
|
|
# Add Bun to PATH permanently |
|
if ! grep -q "BUN_INSTALL" /root/.bashrc; then |
|
echo 'export BUN_INSTALL="$HOME/.bun"' >> /root/.bashrc |
|
echo 'export PATH=$BUN_INSTALL/bin:$PATH' >> /root/.bashrc |
|
fi |
|
|
|
success "Bun installed" |
|
else |
|
warn "Bun is already installed" |
|
fi |
|
} |
|
|
|
# Install PM2 |
|
install_pm2() { |
|
if ! command_exists pm2; then |
|
log "INFO" "Installing PM2..." |
|
if npm install -g pm2; then |
|
success "PM2 installed" |
|
|
|
# Set up PM2 startup script |
|
pm2 startup systemd -u root --hp /root |
|
success "PM2 startup configured" |
|
else |
|
error "Failed to install PM2" |
|
fi |
|
else |
|
warn "PM2 is already installed" |
|
fi |
|
} |
|
|
|
# Set up application directory and files |
|
setup_application() { |
|
local app_dir="$APP_ROOT/$DOMAIN" |
|
|
|
# Create directory structure |
|
if [[ ! -d "$app_dir" ]]; then |
|
log "INFO" "Creating application directory structure..." |
|
mkdir -p "$app_dir" |
|
success "Directory structure created" |
|
else |
|
warn "Application directory already exists" |
|
fi |
|
|
|
# Create package.json |
|
if [[ ! -f "$app_dir/package.json" ]]; then |
|
log "INFO" "Creating package.json..." |
|
cat > "$app_dir/package.json" <<EOF |
|
{ |
|
"name": "${DOMAIN}-server", |
|
"version": "1.0.0", |
|
"type": "module", |
|
"scripts": { |
|
"start": "bun run server.js", |
|
"dev": "bun --hot run server.js" |
|
} |
|
} |
|
EOF |
|
success "package.json created" |
|
fi |
|
|
|
# Create server.js |
|
if [[ ! -f "$app_dir/server.js" ]]; then |
|
log "INFO" "Creating server.js..." |
|
cat > "$app_dir/server.js" <<EOF |
|
const server = Bun.serve({ |
|
port: ${BUN_PORT}, |
|
async fetch(request) { |
|
return new Response("Hello from Bun on ${DOMAIN}!"); |
|
}, |
|
}); |
|
|
|
console.log(\`Listening on http://localhost:\${server.port}\`); |
|
EOF |
|
success "server.js created" |
|
fi |
|
|
|
# Set permissions |
|
log "INFO" "Setting directory permissions..." |
|
chown -R www-data:www-data "$app_dir" |
|
chmod -R 755 "$app_dir" |
|
success "Permissions set" |
|
|
|
# Install dependencies |
|
log "INFO" "Installing dependencies..." |
|
cd "$app_dir" |
|
bun install |
|
success "Dependencies installed" |
|
} |
|
|
|
# Configure nginx as reverse proxy |
|
setup_nginx() { |
|
local nginx_conf="$NGINX_AVAILABLE/$DOMAIN" |
|
if [[ ! -f "$nginx_conf" ]]; then |
|
log "INFO" "Creating nginx configuration..." |
|
cat > "$nginx_conf" <<EOF |
|
limit_req_zone \$binary_remote_addr zone=general:10m rate=30r/s; |
|
|
|
server { |
|
listen 80; |
|
listen [::]:80; |
|
server_name $DOMAIN www.$DOMAIN; |
|
|
|
# Enhanced security headers |
|
add_header X-Frame-Options "SAMEORIGIN" always; |
|
add_header X-XSS-Protection "1; mode=block" always; |
|
add_header X-Content-Type-Options "nosniff" always; |
|
add_header Referrer-Policy "no-referrer-when-downgrade" always; |
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'self';" always; |
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; |
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always; |
|
|
|
# Additional security measures |
|
server_tokens off; |
|
|
|
# Enhanced gzip configuration |
|
gzip_comp_level 6; |
|
gzip_types application/json |
|
application/javascript |
|
application/x-javascript |
|
application/xml |
|
application/xml+rss |
|
text/css |
|
text/javascript |
|
text/plain |
|
text/xml |
|
image/svg+xml; |
|
gzip_proxied any; |
|
|
|
# Apply rate limiting |
|
limit_req zone=general burst=10 nodelay; |
|
|
|
# SSL configuration will be added by certbot |
|
|
|
# Optimize file serving |
|
client_max_body_size 10m; |
|
client_body_buffer_size 200K; |
|
client_header_buffer_size 2k; |
|
large_client_header_buffers 3 1k; |
|
|
|
# Timeouts |
|
client_header_timeout 60; |
|
client_body_timeout 60; |
|
keepalive_timeout 65; |
|
send_timeout 60; |
|
|
|
# Gzip compression |
|
gzip on; |
|
gzip_vary on; |
|
gzip_min_length 1024; |
|
gzip_proxied expired no-cache no-store private auth; |
|
|
|
# Reverse proxy to Bun server |
|
location / { |
|
proxy_pass http://localhost:${BUN_PORT}; |
|
proxy_http_version 1.1; |
|
proxy_set_header Upgrade \$http_upgrade; |
|
proxy_set_header Connection 'upgrade'; |
|
proxy_set_header Host \$host; |
|
proxy_cache_bypass \$http_upgrade; |
|
proxy_set_header X-Real-IP \$remote_addr; |
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; |
|
proxy_set_header X-Forwarded-Proto \$scheme; |
|
proxy_set_header X-Request-ID \$request_id; |
|
|
|
proxy_next_upstream error timeout http_500 http_502 http_503 http_504; |
|
proxy_next_upstream_tries 2; |
|
|
|
# Enhanced timeouts |
|
proxy_connect_timeout 60s; |
|
proxy_send_timeout 60s; |
|
proxy_read_timeout 300s; # Increased for long-running requests |
|
|
|
# WebSocket support |
|
proxy_buffering off; |
|
} |
|
|
|
# Block common vulnerability scanners and bad bots |
|
if (\$http_user_agent ~* (masscan|nmap|nikto|sqlmap|python|curl|wget|scanbot|bot|spider)) { |
|
return 403; |
|
} |
|
|
|
# Deny access to hidden files |
|
location ~ /\. { |
|
deny all; |
|
access_log off; |
|
log_not_found off; |
|
} |
|
|
|
# Caching for static files |
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|woff2|woff|ttf)$ { |
|
expires 30d; |
|
add_header Cache-Control "public, no-transform, must-revalidate"; |
|
access_log off; |
|
tcp_nodelay off; |
|
open_file_cache max=3000 inactive=120s; |
|
open_file_cache_valid 45s; |
|
open_file_cache_min_uses 2; |
|
open_file_cache_errors off; |
|
} |
|
|
|
# Additional security for PHP/sensitive files (if applicable) |
|
location ~ \.(php|env|git|sql|conf|log|ini)$ { |
|
deny all; |
|
return 404; |
|
} |
|
} |
|
EOF |
|
success "Nginx configuration created" |
|
else |
|
warn "Nginx configuration already exists" |
|
fi |
|
|
|
# Enable site |
|
local nginx_enabled="$NGINX_ENABLED/$DOMAIN" |
|
if [[ ! -L "$nginx_enabled" ]]; then |
|
log "INFO" "Enabling website..." |
|
ln -s "$nginx_conf" "$nginx_enabled" |
|
success "Website enabled" |
|
else |
|
warn "Website already enabled" |
|
fi |
|
|
|
# Remove default site if it exists |
|
local default_site="$NGINX_ENABLED/default" |
|
if [[ -L "$default_site" ]]; then |
|
log "INFO" "Removing default nginx site..." |
|
rm "$default_site" |
|
success "Default site removed" |
|
fi |
|
} |
|
|
|
# Configure firewall |
|
setup_firewall() { |
|
if command_exists ufw; then |
|
log "INFO" "Configuring firewall..." |
|
ufw allow 'Nginx Full' >/dev/null 2>&1 |
|
ufw --force enable >/dev/null 2>&1 |
|
success "Firewall configured" |
|
else |
|
warn "UFW not installed, skipping firewall configuration" |
|
fi |
|
} |
|
|
|
# Set up PM2 process |
|
setup_pm2_process() { |
|
local app_dir="$APP_ROOT/$DOMAIN" |
|
|
|
log "INFO" "Setting up PM2 process..." |
|
cd "$app_dir" |
|
|
|
# Start the application with PM2 |
|
NODE_ENV=production pm2 start server.js --name "$DOMAIN-server" --interpreter "$(which bun)" |
|
|
|
# Save PM2 process list |
|
pm2 save |
|
|
|
success "PM2 process configured" |
|
} |
|
|
|
configure_ssl() { |
|
read -p "Enter an email address for SSL certificate notifications [admin@$DOMAIN]: " ADMIN_EMAIL |
|
ADMIN_EMAIL=${ADMIN_EMAIL:-"admin@$DOMAIN"} |
|
log "INFO" "Setting up SSL certificate..." |
|
if certbot --nginx -d "$DOMAIN" -d "www.$DOMAIN" --non-interactive --agree-tos --email "$ADMIN_EMAIL" --redirect; then |
|
success "SSL certificate installed" |
|
else |
|
warn "SSL certificate installation failed. Please make sure your DNS is properly configured and try again manually" |
|
fi |
|
} |
|
|
|
# Configure SSL |
|
setup_ssl() { |
|
if command_exists certbot; then |
|
# check if domain is already properly configured |
|
if dig +short "$DOMAIN" | grep -q "$SERVER_IP"; then |
|
configure_ssl |
|
return 0 |
|
fi |
|
|
|
echo |
|
echo "Before proceeding with SSL setup, please make sure your domain is properly configured:" |
|
echo "1. Your domain ($DOMAIN) should point to this server's IP: $SERVER_IP" |
|
echo "2. DNS changes may take up to 48 hours to propagate globally" |
|
echo |
|
|
|
while true; do |
|
read -p "Have you configured your DNS settings and verified they are working? (y/n): " DNS_READY |
|
case $DNS_READY in |
|
[Yy]* ) |
|
configure_ssl |
|
break;; |
|
[Nn]* ) |
|
warn "Please configure your DNS settings first and run certbot manually later using:" |
|
echo "certbot --nginx -d $DOMAIN -d www.$DOMAIN" |
|
break;; |
|
* ) echo "Please answer yes or no.";; |
|
esac |
|
done |
|
else |
|
error "certbot is not installed on this system" |
|
fi |
|
} |
|
|
|
# Test nginx configuration |
|
test_nginx() { |
|
log "INFO" "Testing nginx configuration..." |
|
if nginx -t; then |
|
success "Nginx configuration test passed" |
|
systemctl reload nginx |
|
success "Nginx reloaded" |
|
else |
|
error "Nginx configuration test failed" |
|
fi |
|
} |
|
|
|
# Main function |
|
main() { |
|
echo "--------------------------" |
|
echo "VPS setup script v$VERSION" |
|
echo "--------------------------" |
|
echo |
|
|
|
# Create log file if it doesn't exist |
|
touch "$LOG_FILE" |
|
chmod 644 "$LOG_FILE" |
|
|
|
log "INFO" "Starting VPS setup script v$VERSION" |
|
|
|
# Check if running as root |
|
check_root |
|
|
|
# Get domain name |
|
get_domain |
|
|
|
# Run setup steps |
|
update_system |
|
install_packages |
|
install_nodejs |
|
install_bun |
|
install_pm2 |
|
setup_application |
|
setup_nginx |
|
setup_firewall |
|
test_nginx |
|
setup_pm2_process |
|
setup_ssl |
|
|
|
log "INFO" "VPS setup completed successfully" |
|
|
|
echo |
|
echo "Setup completed! Next steps:" |
|
echo "1. Verify DNS settings point to this server's IP address" |
|
echo "2. Visit https://$DOMAIN to confirm the site is working" |
|
echo "3. Your Bun application is located at /var/www/$DOMAIN/" |
|
echo "4. Use 'pm2 status' to check the application status" |
|
echo "5. Use 'pm2 logs' to view application logs" |
|
echo |
|
echo "For help, check the log file at $LOG_FILE" |
|
} |
|
|
|
## REVERT SETUP SCRIPT |
|
|
|
# Stop and remove PM2 processes |
|
revert_pm2() { |
|
if command_exists pm2; then |
|
log "INFO" "Removing PM2 processes..." |
|
|
|
# Stop and delete the specific app |
|
pm2 delete "$DOMAIN-server" >/dev/null 2>&1 || warn "No PM2 process found for $DOMAIN-server" |
|
|
|
# Save PM2 process list |
|
pm2 save >/dev/null 2>&1 |
|
|
|
# Remove PM2 startup script |
|
pm2 unstartup systemd >/dev/null 2>&1 || warn "PM2 startup script not found" |
|
|
|
# Uninstall PM2 globally |
|
if npm uninstall -g pm2; then |
|
success "PM2 uninstalled" |
|
else |
|
warn "Failed to uninstall PM2" |
|
fi |
|
else |
|
warn "PM2 not found, skipping PM2 reversion" |
|
fi |
|
} |
|
|
|
# Remove Bun installation |
|
revert_bun() { |
|
if command_exists bun; then |
|
log "INFO" "Removing Bun..." |
|
|
|
# Remove Bun installation directory |
|
rm -rf "$HOME/.bun" |
|
|
|
# Remove Bun from PATH in bashrc |
|
sed -i '/export BUN_INSTALL/d' /root/.bashrc |
|
sed -i '/export PATH=\$BUN_INSTALL/d' /root/.bashrc |
|
|
|
success "Bun removed" |
|
else |
|
warn "Bun not found, skipping Bun reversion" |
|
fi |
|
} |
|
|
|
# Revert nginx configuration |
|
revert_nginx() { |
|
local nginx_conf="$NGINX_AVAILABLE/$DOMAIN" |
|
local nginx_enabled="$NGINX_ENABLED/$DOMAIN" |
|
|
|
if [[ -f "$nginx_conf" ]]; then |
|
log "INFO" "Removing nginx configuration..." |
|
rm -f "$nginx_conf" |
|
success "Nginx configuration removed" |
|
else |
|
warn "No nginx configuration found for $DOMAIN" |
|
fi |
|
|
|
if [[ -L "$nginx_enabled" ]]; then |
|
log "INFO" "Disabling site in nginx..." |
|
rm -f "$nginx_enabled" |
|
success "Site disabled in nginx" |
|
else |
|
warn "No enabled site found for $DOMAIN in nginx" |
|
fi |
|
|
|
# Restore default site if removed |
|
local default_site="$NGINX_ENABLED/default" |
|
if [[ ! -L "$default_site" && -f "$NGINX_AVAILABLE/default" ]]; then |
|
log "INFO" "Restoring default nginx site..." |
|
ln -s "$NGINX_AVAILABLE/default" "$default_site" |
|
success "Default site restored" |
|
fi |
|
|
|
# Reload nginx to apply changes |
|
if nginx -t; then |
|
success "Nginx configuration test passed" |
|
systemctl reload nginx |
|
success "Nginx reloaded" |
|
else |
|
error "Nginx configuration test failed; please review manually" |
|
fi |
|
} |
|
|
|
# Revert application directory |
|
revert_application_directory() { |
|
local app_dir="$APP_ROOT/$DOMAIN" |
|
|
|
if [[ -d "$app_dir" ]]; then |
|
log "INFO" "Removing application directory structure..." |
|
rm -rf "$app_dir" |
|
success "Application directory structure removed" |
|
else |
|
warn "No application directory found for $DOMAIN" |
|
fi |
|
} |
|
|
|
# Revert firewall configuration |
|
revert_firewall() { |
|
if command_exists ufw; then |
|
log "INFO" "Reverting firewall configuration..." |
|
ufw delete allow 'Nginx Full' >/dev/null 2>&1 |
|
success "Firewall rule reverted" |
|
warn "UFW was enabled by setup script; review firewall status as needed." |
|
else |
|
warn "UFW not installed, skipping firewall reversion" |
|
fi |
|
} |
|
|
|
# Remove SSL certificates if they were installed |
|
revert_ssl() { |
|
if command_exists certbot; then |
|
log "INFO" "Revoking SSL certificate..." |
|
certbot delete --cert-name "$DOMAIN" >/dev/null 2>&1 || warn "SSL certificate for $DOMAIN not found" |
|
success "SSL certificate revoked (if existed)" |
|
else |
|
warn "Certbot not installed, skipping SSL reversion" |
|
fi |
|
} |
|
|
|
# Reload or restart Nginx |
|
restart_nginx() { |
|
log "INFO" "Restarting Nginx..." |
|
if systemctl restart nginx; then |
|
success "Nginx restarted successfully" |
|
else |
|
error "Failed to restart Nginx; please review manually" |
|
fi |
|
} |
|
|
|
# Remove log files |
|
remove_logs() { |
|
log "INFO" "Removing log files..." |
|
rm -f "$LOG_FILE" |
|
success "Log files removed" |
|
} |
|
|
|
# Main function |
|
revert_main() { |
|
# Create log file if it doesn't exist |
|
touch "$LOG_FILE" |
|
chmod 644 "$LOG_FILE" |
|
|
|
# Confirm with the user before proceeding |
|
echo |
|
echo -e "${YELLOW}WARNING${NC} - This script will remove:" |
|
echo " - The Bun server application" |
|
echo " - PM2 process manager and its processes" |
|
echo " - Bun runtime" |
|
echo " - Node.js installation" |
|
echo " - Nginx configurations" |
|
echo " - SSL certificates" |
|
echo " - Application files and directories" |
|
echo |
|
read -p "Are you sure you want to revert all changes made by the setup script? (Y/n): " CONFIRM |
|
CONFIRM=${CONFIRM:-"Y"} |
|
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then |
|
echo "Reversion cancelled." |
|
exit 0 |
|
fi |
|
|
|
log "INFO" "Reverting..." |
|
|
|
# Run revert steps |
|
revert_pm2 |
|
revert_bun |
|
revert_nginx |
|
revert_application_directory |
|
revert_firewall |
|
revert_ssl |
|
restart_nginx # Apply nginx reload |
|
# remove_logs # Clean up log file |
|
|
|
log "INFO" "VPS revert script completed successfully" |
|
|
|
echo |
|
echo "Revert completed! The changes made by the setup script for $DOMAIN have been reverted." |
|
echo "Note: Some system packages (nginx, certbot, etc.) were not removed." |
|
echo "If you want to remove them, use: apt remove nginx certbot python3-certbot-nginx" |
|
echo |
|
echo "For more details, check the log file at $LOG_FILE" |
|
} |
|
|
|
# Clean up function to revert if main fails |
|
clean_up() { |
|
if [[ $? -ne 0 ]]; then |
|
revert_main |
|
fi |
|
} |
|
|
|
trap clean_up EXIT |
|
main "$@" |
Cheers for this! Been wanting to learn nginx and set up a small hobby site on a VPS, so I'm basically treating this as a tutorial. The code is very readable.
One minor enhancement for getting the machine's IPv4 address:
Not sure how reliable this method is across different distros, but I figured it's a little safer than
curl
-ing a third-party site.