Created
June 19, 2025 22:33
-
-
Save StanleyMasinde/5949668e2430f88c2a2005d9d9c26c90 to your computer and use it in GitHub Desktop.
Script to create a deploy a site to VPS via reverse proxy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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