Skip to content

Instantly share code, notes, and snippets.

@arpee
Last active October 17, 2025 02:39
Show Gist options
  • Select an option

  • Save arpee/22cec55b8e74c09c2e17e5a42eead6cf to your computer and use it in GitHub Desktop.

Select an option

Save arpee/22cec55b8e74c09c2e17e5a42eead6cf to your computer and use it in GitHub Desktop.
#!/bin/bash
# Saito Node Setup Script for Ubuntu
# This script installs dependencies, sets up Saito node, configures nginx with SSL
set -e # Exit on any error
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if running as root
if [ "$EUID" -ne 0 ]; then
print_error "This script must be run as root (use sudo)"
exit 1
fi
# Display warning and confirmation
echo ""
print_warning "=========================================="
print_warning " SAITO WEB NODE PROVISIONING SCRIPT"
print_warning "=========================================="
echo ""
print_error "WARNING: This script will provision a complete Saito Web Node"
echo ""
print_status "This script is designed for:"
echo "• Fresh installation of Ubuntu 24.04 LTS"
echo "• Setting up a new Saito Web Node from scratch"
echo ""
print_status "What will be installed/configured:"
echo "• System packages: git, g++, make, python3, Node.js, TypeScript"
echo "• Web server: nginx with SSL/TLS (Let's Encrypt)"
echo "• Process manager: PM2 for service management"
echo "• Saito blockchain node software"
echo ""
print_error "IMPORTANT WARNINGS:"
echo "• This will OVERWRITE any existing Saito installation in /opt/saito"
echo "• This will OVERWRITE nginx configurations for your domain"
echo "• This assumes a clean Ubuntu 24.04 system"
echo "• Your domain must point to this server's IP address"
echo ""
print_warning "If you have an existing Saito installation or custom nginx configs,"
print_warning "they will be replaced. Make backups if needed!"
echo ""
# Confirmation prompt
while true; do
read -p "Do you want to continue with the installation? (yes/no): " yn
case $yn in
[Yy]es ) break;;
[Nn]o ) print_status "Installation cancelled."; exit 0;;
* ) echo "Please answer yes or no.";;
esac
done
print_status "Starting Saito Node setup for Ubuntu..."
# Prompt for TLD early in the process
print_status "Domain configuration required..."
while true; do
read -p "Enter your domain name (e.g., example.com, sub.example.com, api.v1.example.com): " TLD
# Enhanced regex to allow subdomains of any depth
if [[ $TLD =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$ ]]; then
print_success "Domain configured: $TLD"
break
else
print_error "Please enter a valid domain name (supports subdomains like sub.example.com or api.v1.example.com)"
fi
done
print_status "Proceeding with installation for domain: $TLD"
# Get server's public IP
print_status "Testing domain configuration..."
echo "Getting server's public IP address..."
# Try multiple methods to get public IP
SERVER_IP=""
for method in "curl -s ifconfig.me" "curl -s ipecho.net/plain" "dig +short myip.opendns.com @resolver1.opendns.com" "curl -s icanhazip.com"; do
if SERVER_IP=$(eval $method 2>/dev/null) && [[ $SERVER_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
break
fi
done
if [ -z "$SERVER_IP" ]; then
print_error "Could not determine server's public IP address"
print_warning "Please ensure your server has internet connectivity"
exit 1
fi
print_success "Server public IP: $SERVER_IP"
# Test domain resolution
print_status "Testing if $TLD resolves to this server..."
# Try to resolve the domain
DOMAIN_IP=""
if command -v dig &> /dev/null; then
DOMAIN_IP=$(dig +short "$TLD" A | tail -n1)
elif command -v nslookup &> /dev/null; then
DOMAIN_IP=$(nslookup "$TLD" | grep -A1 "Name:" | tail -n1 | awk '{print $2}' 2>/dev/null)
fi
if [ -z "$DOMAIN_IP" ]; then
print_error "Could not resolve domain $TLD"
DOMAIN_TEST_FAILED=true
elif [ "$DOMAIN_IP" = "$SERVER_IP" ]; then
print_success "✅ Domain test passed! $TLD resolves to $SERVER_IP"
DOMAIN_TEST_FAILED=false
else
print_error "❌ Domain test failed!"
print_error " Domain $TLD resolves to: $DOMAIN_IP"
print_error " Server IP is: $SERVER_IP"
print_error " These don't match!"
DOMAIN_TEST_FAILED=true
fi
# Handle domain test results
if [ "$DOMAIN_TEST_FAILED" = true ]; then
echo ""
print_warning "Your domain is not properly configured!"
print_status "To fix this, you need to:"
echo "1. Log into your domain registrar or DNS provider"
echo "2. Create an A record pointing $TLD to $SERVER_IP"
echo "3. Wait for DNS propagation (can take 5-60 minutes)"
echo ""
while true; do
echo "Options:"
echo "1. Continue anyway (SSL setup will fail, site will be HTTP-only)"
echo "2. Pause here while you configure DNS"
echo "3. Exit and run script later"
read -p "Choose option (1/2/3): " choice
case $choice in
1)
print_warning "Continuing without proper DNS configuration"
print_warning "SSL certificate setup will fail"
print_warning "Your site will only work over HTTP (unsecured)"
print_warning "Many modern web features require HTTPS and won't work!"
print_warning "Users may see security warnings in their browsers"
echo ""
read -p "Are you sure you want to continue? (yes/no): " confirm
if [[ $confirm =~ ^[Yy]es$ ]]; then
SKIP_SSL=true
break
fi
;;
2)
print_status "Script paused. Configure your DNS then press Enter to test again..."
read -p "Press Enter when ready to test DNS again, or Ctrl+C to abort: "
# Re-test domain resolution
print_status "Re-testing domain resolution..."
if command -v dig &> /dev/null; then
DOMAIN_IP=$(dig +short "$TLD" A | tail -n1)
elif command -v nslookup &> /dev/null; then
DOMAIN_IP=$(nslookup "$TLD" | grep -A1 "Name:" | tail -n1 | awk '{print $2}' 2>/dev/null)
fi
if [ "$DOMAIN_IP" = "$SERVER_IP" ]; then
print_success "Domain now resolves correctly!"
DOMAIN_TEST_FAILED=false
break
else
print_error "Domain still not resolving correctly"
print_error "Current resolution: $DOMAIN_IP (expected: $SERVER_IP)"
fi
;;
3)
print_status "Installation cancelled."
print_warning "Remember: SSL certificates are required for:"
print_warning "• Secure data transmission"
print_warning "• Modern browser features (geolocation, camera, etc.)"
print_warning "• Service workers and PWA functionality"
print_warning "• Protection against man-in-the-middle attacks"
print_warning "• User trust and search engine rankings"
exit 0
;;
*)
echo "Please choose 1, 2, or 3."
;;
esac
done
fi
echo ""
# Update package list
print_status "Updating package list..."
apt update
# Install basic dependencies if they don't exist
print_status "Installing basic dependencies..."
# Check and install git
if ! command -v git &> /dev/null; then
print_status "Installing git..."
apt install -y git
else
print_success "git is already installed"
fi
# Check and install g++
if ! command -v g++ &> /dev/null; then
print_status "Installing g++..."
apt install -y g++
else
print_success "g++ is already installed"
fi
# Check and install make
if ! command -v make &> /dev/null; then
print_status "Installing make..."
apt install -y make
else
print_success "make is already installed"
fi
# Check and install python3
if ! command -v python3 &> /dev/null; then
print_status "Installing python3..."
apt install -y python3 python3-pip
else
print_success "python3 is already installed"
fi
# Install Node.js and npm (required for tsc and pm2)
if ! command -v node &> /dev/null; then
print_status "Installing Node.js..."
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt install -y nodejs
else
print_success "Node.js is already installed"
fi
# Check and install TypeScript compiler
if ! command -v tsc &> /dev/null; then
print_status "Installing TypeScript..."
npm install -g typescript
else
print_success "TypeScript is already installed"
fi
# Install nginx
print_status "Installing nginx..."
apt install -y nginx
# Install pm2
if ! command -v pm2 &> /dev/null; then
print_status "Installing pm2..."
npm install -g pm2
else
print_success "pm2 is already installed"
fi
# Install certbot for Let's Encrypt
print_status "Installing certbot..."
apt install -y certbot python3-certbot-nginx
# Change to /opt directory
print_status "Changing to /opt directory..."
cd /opt
# Clone Saito repository
if [ ! -d "saito" ]; then
print_status "Cloning Saito repository..."
git clone https://github.com/saitotech/saito
git checkout prod
else
print_warning "Saito directory already exists, pulling latest changes..."
cd saito
git checkout prod
git pull
cd /opt
fi
# Install Saito dependencies
print_status "Installing Saito node dependencies..."
cd saito/node
npm install
# Run npm nuke to initialize/reset Saito
print_status "Running npm run nuke to initialize Saito..."
npm run nuke
print_status "Configuring nginx for domain: $TLD"
# Create initial HTTP-only nginx configuration
cat > "/etc/nginx/sites-available/$TLD" << EOF
server {
listen 80;
server_name $TLD www.$TLD;
# Gzip compression
gzip on;
gzip_proxied any;
gzip_min_length 256;
gzip_comp_level 9;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Proxy timeouts
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Cache headers (7 days)
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 7d;
add_header Cache-Control "public, immutable";
proxy_pass http://localhost:12101;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
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;
}
# Main location block
location / {
proxy_pass http://localhost:12101;
# Generic headers
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
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;
# Additional headers for better proxy handling
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
}
}
EOF
# Enable the site
print_status "Enabling nginx site..."
ln -sf "/etc/nginx/sites-available/$TLD" "/etc/nginx/sites-enabled/$TLD"
# Remove default nginx site if it exists
if [ -f "/etc/nginx/sites-enabled/default" ]; then
rm /etc/nginx/sites-enabled/default
fi
# Test nginx configuration
print_status "Testing nginx configuration..."
if nginx -t; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration test failed"
exit 1
fi
# Start and enable nginx
print_status "Starting nginx..."
systemctl start nginx
systemctl enable nginx
print_status "Obtaining Let's Encrypt SSL certificate..."
# Skip SSL if domain test failed and user chose to continue anyway
if [ "$SKIP_SSL" = true ]; then
print_warning "Skipping SSL certificate setup due to DNS configuration issues"
print_warning "Your site will only be accessible via HTTP"
else
# Obtain SSL certificate
if certbot --nginx -d "$TLD" -d "www.$TLD" --non-interactive --agree-tos --email "admin@$TLD"; then
print_success "SSL certificate obtained successfully"
print_status "Certbot has automatically updated the nginx configuration with SSL and HTTPS redirect"
else
print_warning "SSL certificate creation failed. Site will remain HTTP-only."
print_warning "You can manually run: certbot --nginx -d $TLD -d www.$TLD"
fi
fi
# Restart nginx to apply all changes
print_status "Restarting nginx..."
systemctl restart nginx
# Setup pm2 to auto-start Saito node
print_status "Setting up PM2 for Saito node..."
cd /opt/saito/node
# Create PM2 ecosystem file
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'saito-node',
script: 'npm start',
instances: 1,
exec_mode: 'fork',
watch: false,
max_memory_restart: '2G',
error_file: './logs/err.log',
out_file: './logs/out.log',
log_file: './logs/saito.log',
time: true
}]
};
EOF
# Create logs directory
mkdir -p logs
# Start Saito with PM2
print_status "Starting Saito node with PM2..."
pm2 start ecosystem.config.js
pm2 save
pm2 startup systemd -u root --hp /root
print_success "Setup completed successfully!"
echo ""
print_status "Summary:"
echo "• Saito node installed in /opt/saito"
echo "• Nginx configured with SSL for $TLD"
echo "• PM2 managing Saito node process"
echo "• Gzip compression enabled"
echo "• 7-day cache headers for static assets"
echo "• HTTP traffic redirected to HTTPS"
echo ""
print_status "Next steps:"
echo "• Check PM2 status: pm2 status"
echo "• View Saito logs: pm2 logs saito-node"
echo "• Nginx logs: /var/log/nginx/"
echo ""
print_status "IMPORTANT: Please check your Saito installation:"
if [[ $TLD == *"https"* ]] || [ -f "/etc/letsencrypt/live/$TLD/fullchain.pem" ]; then
echo "Visit: https://$TLD/"
else
echo "Visit: http://$TLD/"
fi
echo ""
print_success "You should see a Saito welcome page with setup instructions!"
print_warning "If you don't see the welcome page, check the PM2 logs for any errors."
echo ""
print_warning "Don't forget to configure your firewall to allow ports 80 and 443!"
echo "Example: ufw allow 'Nginx Full'"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment