Skip to content

Instantly share code, notes, and snippets.

@StanleyMasinde
Created June 19, 2025 22:33
Show Gist options
  • Save StanleyMasinde/5949668e2430f88c2a2005d9d9c26c90 to your computer and use it in GitHub Desktop.
Save StanleyMasinde/5949668e2430f88c2a2005d9d9c26c90 to your computer and use it in GitHub Desktop.
Script to create a deploy a site to VPS via reverse proxy.
#!/usr/bin/env bash
set -euo pipefail
log() { echo -e "\033[1;32m[+]\033[0m $1"; }
err() { echo -e "\033[1;31m[!]\033[0m $1" >&2; }
[[ "$EUID" -eq 0 ]] || { err "Run as root"; exit 1; }
if [[ $# -lt 4 || $# -gt 8 ]]; then
err "Usage:"
err " sudo $0 <domain> <port> <repo_url> \"<start_command>\" [branch] [build_command] [output_dir] [env_file]"
exit 1
fi
DOMAIN="$1"
PORT="$2"
REPO_URL="$3"
USER_CMD="$4"
BRANCH="${5:-main}"
BUILD_CMD="${6:-}"
OUTPUT_DIR="${7:-}"
ENV_FILE="${8:-}"
DEPLOY_DIR="/root/deploys/$DOMAIN"
WEBROOT="/var/www/$DOMAIN"
SERVICE_NAME="app-${DOMAIN//./-}"
log "Installing dependencies..."
apt update
apt install -y nginx git rsync certbot python3-certbot-nginx
# --- Git shallow clone ---
log "Cloning $REPO_URL into $DEPLOY_DIR (branch: $BRANCH)"
rm -rf "$DEPLOY_DIR"
mkdir -p "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
git init
git remote add origin "$REPO_URL"
git fetch --depth 1 origin "$BRANCH"
git checkout FETCH_HEAD
SHORT_HASH=$(git rev-parse --short HEAD)
COMMIT_MSG=$(git log -1 --pretty=%s)
DEPLOY_META="$SHORT_HASH $COMMIT_MSG ($BRANCH)"
echo "$DEPLOY_META" > "$DEPLOY_DIR/.commit"
log "Checked out: $DEPLOY_META"
# --- Optional build ---
if [[ -n "$BUILD_CMD" ]]; then
log "Running build command: $BUILD_CMD"
bash -c "$BUILD_CMD"
fi
# --- Rsync (no ignore) ---
log "Deploying to $WEBROOT"
rm -rf "$DEPLOY_DIR/.git"
mkdir -p "$WEBROOT"
SOURCE_DIR="$DEPLOY_DIR"
[[ -n "$OUTPUT_DIR" ]] && SOURCE_DIR="$DEPLOY_DIR/$OUTPUT_DIR"
rsync -a --delete "$SOURCE_DIR/" "$WEBROOT/"
cp "$DEPLOY_DIR/.commit" "$WEBROOT/.commit"
# --- Resolve absolute binary path ---
CMD_BINARY=$(echo "$USER_CMD" | awk '{print $1}')
CMD_ARGS=$(echo "$USER_CMD" | cut -d' ' -f2-)
BIN_PATH=$(command -v "$CMD_BINARY" || true)
[[ -z "$BIN_PATH" ]] && { err "Binary '$CMD_BINARY' not found in PATH"; exit 1; }
FULL_CMD="$BIN_PATH $WEBROOT/$CMD_ARGS"
log "Resolved start command: $FULL_CMD"
# --- Determine Nginx layout ---
if [[ -d /etc/nginx/sites-available && -d /etc/nginx/sites-enabled ]]; then
log "Detected Ubuntu-style Nginx layout"
NGINX_DIR="/etc/nginx/sites-available"
NGINX_LINK="/etc/nginx/sites-enabled/$DOMAIN.conf"
NGINX_CONF="$NGINX_DIR/$DOMAIN.conf"
LINK_REQUIRED=true
else
log "Detected Debian-style Nginx layout"
NGINX_DIR="/etc/nginx/conf.d"
NGINX_CONF="$NGINX_DIR/$DOMAIN.conf"
LINK_REQUIRED=false
fi
# --- Create Nginx config ---
log "Creating Nginx config at $NGINX_CONF"
cat > "$NGINX_CONF" <<EOF
server {
listen 80;
server_name $DOMAIN;
location / {
proxy_pass http://127.0.0.1:$PORT;
proxy_http_version 1.1;
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;
}
}
EOF
if [[ "$LINK_REQUIRED" = true ]]; then
log "Creating symlink in sites-enabled"
ln -sf "$NGINX_CONF" "$NGINX_LINK"
fi
log "Testing and reloading Nginx"
nginx -t
systemctl reload nginx
# --- Certbot ---
log "Issuing SSL cert for $DOMAIN"
certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos -m "admin@$DOMAIN" --redirect
# --- .env handling for systemd ---
ENV_LINES=""
if [[ -n "$ENV_FILE" && -f "$ENV_FILE" ]]; then
log "Injecting env vars from $ENV_FILE"
while IFS='=' read -r key value; do
[[ "$key" =~ ^#.*$ || -z "$key" ]] && continue
ENV_LINES+="Environment=${key}=${value}"$'\n'
done < "$ENV_FILE"
fi
# --- Create systemd service ---
log "Creating systemd service: $SERVICE_NAME"
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=App service for $DOMAIN
After=network.target
[Service]
Type=simple
WorkingDirectory=$WEBROOT
ExecStart=/bin/bash -c '$FULL_CMD'
Restart=always
RestartSec=5
User=www-data
Environment=NODE_ENV=production
${ENV_LINES}
[Install]
WantedBy=multi-user.target
EOF
log "Reloading systemd and restarting service"
systemctl daemon-reload
systemctl stop "$SERVICE_NAME" || true
systemctl enable "$SERVICE_NAME"
systemctl start "$SERVICE_NAME"
log "βœ… Deployed https://$DOMAIN"
log "πŸ†” Commit: $DEPLOY_META"
log "πŸš€ Service running: $SERVICE_NAME"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment