Docs / Automation & IaC / How to Set Up Webhook-Based Deployments

How to Set Up Webhook-Based Deployments

By Admin · Mar 2, 2026 · Updated Apr 24, 2026 · 27 views · 4 min read

How to Set Up Webhook-Based Deployments

Webhook-based deployments let you automatically deploy code to your Breeze instance whenever you push to your Git repository. Instead of manually SSH-ing in and pulling changes, a webhook triggers the deployment pipeline instantly.

How Webhook Deployments Work

  1. You push code to your Git repository (GitHub, GitLab, Gitea, etc.)
  2. The platform sends an HTTP POST request to your Breeze instance
  3. A webhook listener on your server validates the request and runs the deploy script
  4. Your application is updated with zero manual steps

Setting Up a Webhook Listener

Use the lightweight webhook tool to listen for incoming hooks:

# Install webhook
sudo apt-get install -y webhook

# Or download the latest binary
WEBHOOK_VERSION="2.8.1"
wget "https://github.com/adnanh/webhook/releases/download/${WEBHOOK_VERSION}/webhook-linux-amd64.tar.gz"
tar xzf webhook-linux-amd64.tar.gz
sudo cp webhook-linux-amd64/webhook /usr/local/bin/

Configuring Webhook Hooks

Create a hooks configuration file:

# /etc/webhook/hooks.json
[
  {
    "id": "deploy-app",
    "execute-command": "/usr/local/bin/deploy.sh",
    "command-working-directory": "/var/www/myapp",
    "pass-arguments-to-command": [
      {
        "source": "payload",
        "name": "ref"
      },
      {
        "source": "payload",
        "name": "head_commit.message"
      }
    ],
    "trigger-rule": {
      "and": [
        {
          "match": {
            "type": "payload-hmac-sha256",
            "secret": "YourWebhookSecretHere",
            "parameter": {
              "source": "header",
              "name": "X-Hub-Signature-256"
            }
          }
        },
        {
          "match": {
            "type": "value",
            "value": "refs/heads/main",
            "parameter": {
              "source": "payload",
              "name": "ref"
            }
          }
        }
      ]
    }
  }
]

Writing the Deploy Script

Create a deployment script that the webhook executes:

#!/usr/bin/env bash
# /usr/local/bin/deploy.sh
set -euo pipefail

APP_DIR="/var/www/myapp"
LOG_FILE="/var/log/deploy.log"
LOCK_FILE="/tmp/deploy.lock"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" | tee -a "$LOG_FILE"
}

# Prevent concurrent deployments
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
    log "Deploy already in progress, skipping"
    exit 0
fi

REF="${1:-unknown}"
COMMIT_MSG="${2:-no message}"

log "=== Starting deployment ==="
log "Ref: $REF"
log "Commit: $COMMIT_MSG"

cd "$APP_DIR"

# Pull latest changes
log "Pulling latest code..."
git fetch origin main
git reset --hard origin/main

# Install dependencies
log "Installing dependencies..."
composer install --no-dev --optimize-autoloader 2>&1 | tail -5
npm ci --production 2>&1 | tail -5

# Build assets
log "Building assets..."
npm run build 2>&1 | tail -5

# Run database migrations
log "Running migrations..."
php artisan migrate --force 2>&1

# Clear and rebuild caches
log "Clearing caches..."
php artisan cache:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Reload services
log "Reloading services..."
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx

# Health check
sleep 2
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost)
if [[ "$HTTP_STATUS" == "200" ]]; then
    log "Health check passed (HTTP $HTTP_STATUS)"
    log "=== Deployment successful ==="
else
    log "ERROR: Health check failed (HTTP $HTTP_STATUS)"
    log "=== Deployment may have issues ==="
    exit 1
fi

Running as a systemd Service

Create a systemd service so the webhook listener starts on boot:

# /etc/systemd/system/webhook.service
[Unit]
Description=Webhook Listener
After=network-online.target
Wants=network-online.target

[Service]
User=deploy
ExecStart=/usr/local/bin/webhook \
    -hooks /etc/webhook/hooks.json \
    -port 9000 \
    -verbose \
    -hotreload
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now webhook.service

Reverse Proxy Configuration

Place the webhook listener behind Nginx with HTTPS:

# /etc/nginx/sites-available/webhook
server {
    listen 443 ssl;
    server_name deploy.example.com;

    ssl_certificate /etc/letsencrypt/live/deploy.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/deploy.example.com/privkey.pem;

    location /hooks/ {
        proxy_pass http://127.0.0.1:9000/hooks/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Hub-Signature-256 $http_x_hub_signature_256;
    }
}

Configuring the GitHub Webhook

In your GitHub repository settings:

  1. Go to Settings > Webhooks > Add webhook
  2. Set Payload URL to https://deploy.example.com/hooks/deploy-app
  3. Set Content type to application/json
  4. Enter your webhook secret
  5. Select "Just the push event"
  6. Check "Active" and save

Security Best Practices

  • Always validate signatures — use HMAC verification to ensure requests come from your Git platform
  • Restrict to specific branches — only deploy from main or production branches
  • Use HTTPS — encrypt webhook traffic with TLS
  • Run as a limited user — the deploy user should only have access to the application directory
  • Use file locking — prevent concurrent deployments with flock
  • Add health checks — verify the application works after deployment
  • Log everything — maintain a deployment log for auditing and debugging
  • Add rollback capability — keep the previous version available for quick rollback

Webhook-based deployments give your Breeze instances a fast, automated deployment pipeline triggered directly from Git pushes.

Was this article helpful?