Skip to content

Instantly share code, notes, and snippets.

@trvswgnr
Last active November 7, 2024 15:32
Show Gist options
  • Save trvswgnr/77e7c59f8893dbc690fc4d5f05683d9c to your computer and use it in GitHub Desktop.
Save trvswgnr/77e7c59f8893dbc690fc4d5f05683d9c to your computer and use it in GitHub Desktop.

VPS Setup Script for Bun Applications

An automated setup script for deploying Bun applications on a VPS with Nginx reverse proxy, SSL, and PM2 process management.

Features

  • 🚀 One-command setup for Bun applications
  • 🔒 Automatic SSL certificate configuration via Let's Encrypt
  • 🔄 Nginx reverse proxy with optimized settings
  • 📊 PM2 process management
  • 🛡️ Basic firewall configuration
  • 🔄 Automatic rollback on error
  • 📝 Detailed logging

Prerequisites

  • Ubuntu/Debian-based VPS
  • Root access
  • Domain name pointing to your server's IP
  • SSH access to your VPS

Quick Start

  1. SSH into your VPS
  2. Run the installation command:
bash <(curl -fsSL https://gist.githubusercontent.com/trvswgnr/77e7c59f8893dbc690fc4d5f05683d9c/raw/setup-vps-bun.sh)

What Gets Installed

  • Nginx web server
  • Certbot for SSL certificates
  • Node.js and npm
  • Bun runtime
  • PM2 process manager
  • Basic firewall rules

Directory Structure

/var/www/{your-domain}/
├── package.json
├── server.js
└── node_modules/

Configuration

The script will prompt you for:

  • Domain name
  • Email address (for SSL certificates)

Post-Installation

After successful installation:

  1. Your application will be available at https://your-domain.com
  2. Application files are located in /var/www/your-domain/
  3. PM2 process management:
    pm2 status    # Check application status
    pm2 logs      # View application logs

Reverting Changes

To remove all changes made by the setup script:

  1. SSH into your VPS
  2. Run the same script with the --revert flag (coming soon)

Logging

  • All operations are logged to /var/log/vps-setup.log
  • View real-time logs during installation

Security Features

  • 🔒 Automatic SSL/TLS configuration
  • 🛡️ Basic firewall rules
  • 🚫 Rate limiting
  • 🔐 Security headers
  • 🤖 Bot protection

Troubleshooting

  1. SSL Certificate Issues

    • Ensure DNS is properly configured
    • Wait for DNS propagation (up to 48 hours)
    • Check certbot logs
  2. Application Not Starting

    • Check PM2 logs: pm2 logs
    • Verify Bun installation: bun --version
    • Check nginx logs: /var/log/nginx/error.log
  3. To manually roll back changes made by the setup script, you can run this script:

bash <(curl -fsSL https://gist.githubusercontent.com/trvswgnr/77e7c59f8893dbc690fc4d5f05683d9c/raw/undo-setup-vps-bun.sh)

License

MIT License

Author

Travis Aaron Wagner
Email: [email protected]

Version

Current Version: 0.3.0


⭐ Star this if you find it helpful!

#!/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 "$@"
#!/bin/bash
VERSION="0.3.0"
# Exit on error
set -eo pipefail
# Configuration variables (should match the setup script)
OG_LOG_FILE="/var/log/vps-setup.log"
LOG_FILE="/var/log/vps-setup-revert.log"
DOMAIN="" # set by user input
NGINX_AVAILABLE="/etc/nginx/sites-available"
NGINX_ENABLED="/etc/nginx/sites-enabled"
APP_ROOT="/var/www"
ADMIN_EMAIL="" # set by user input
# 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 X
error() {
echo -e "${RED}✗ $1${NC}" | tee -a "$LOG_FILE"
return 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
}
# 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 setup log files..."
rm -f "$OG_LOG_FILE"
success "Log files removed"
}
# Main function
main() {
# Create log file if it doesn't exist
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
# Confirm with the user before proceeding
echo "WARNING: 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
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
echo "Reversion cancelled."
exit 0
fi
log "INFO" "Starting VPS revert script"
# Prompt for domain name
while true; do
read -p "Enter your domain name (without www): " 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
success "Domain name validated: $DOMAIN"
break
else
warn "Invalid domain name format. Please try again."
fi
done
# 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 any issues, check the log file at $LOG_FILE"
}
# Run main function
main "$@"
@AleksandrHovhannisyan
Copy link

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:

hostname -I | awk '{print $1}'

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment