Automating deployment whenever you push to a specific branch removes the manual "SSH in and pull" step, reducing both effort and human error. This guide covers a simple, self-hosted webhook-based deployment setup.
How It Works
- You push code to your Git repository
- Your Git host sends a webhook notification to your VPS
- A small listener on your VPS receives the webhook and runs your deployment script
Prerequisites
- A VPS with your application already deployed
- Nginx installed
- A Git repository hosted somewhere that supports webhooks (GitHub, GitLab, Gitea, etc.)
Step 1 — Install webhook (Lightweight Webhook Listener)
sudo apt install webhook -y
Step 2 — Create a Deployment Script
sudo nano /usr/local/bin/deploy.sh
#!/bin/bash
set -e
cd /var/www/myapp
git pull origin main
npm install --production
pm2 reload myapp
echo "Deployed at $(date)" >> /var/log/deploy.log
sudo chmod +x /usr/local/bin/deploy.sh
Step 3 — Configure the Webhook Definition
sudo nano /etc/webhook.conf
[
{
"id": "deploy-myapp",
"execute-command": "/usr/local/bin/deploy.sh",
"command-working-directory": "/var/www/myapp",
"trigger-rule": {
"match": {
"type": "value",
"value": "YOUR_SECRET_TOKEN",
"parameter": {
"source": "header",
"name": "X-Webhook-Token"
}
}
}
}
]
Step 4 — Start the Webhook Service
sudo systemctl enable --now webhook
Step 5 — Configure Nginx to Proxy the Webhook Endpoint
location /hooks/ {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
}
sudo nginx -t && sudo systemctl reload nginx
Step 6 — Configure the Webhook in Your Git Host
In your repository's settings (GitHub: Settings → Webhooks → Add webhook), set:
- Payload URL:
https://yourdomain.com/hooks/deploy-myapp - Custom header:
X-Webhook-Token: YOUR_SECRET_TOKEN(or use the platform's built-in HMAC signature verification if supported, which is more secure than a static token) - Trigger: push events to your deployment branch
Step 7 — Test
Push a commit and check:
tail -f /var/log/deploy.log
Improving Security with HMAC Signature Verification
A static token in a header is simpler but less secure than HMAC signature verification (which most Git hosts support) — verifying the payload was genuinely signed by your Git host's secret, rather than just checking a static string that could be intercepted or guessed.
Restricting Which Branch Triggers Deployment
#!/bin/bash
set -e
cd /var/www/myapp
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ]; then
exit 0
fi
git pull origin main
# ... rest of deployment
Common Errors
Webhook received but deployment doesn't run — check webhook service logs:
sudo journalctl -u webhook -n 50
Permission denied during deployment — verify the user running the webhook service has appropriate permissions on the application directory.
Best Practices
- Use HMAC signature verification over static tokens where your Git host supports it
- Restrict the webhook endpoint to specific expected source IPs if your Git host publishes them
- Always run deployment scripts with
set -eso failures stop the script rather than continuing silently
Continue Reading
- How to Set Up a Deployment User with Restricted SSH Access
- How to Roll Back a Bad Deployment Quickly
- CI/CD Pipeline Security Best Practices
Browse more articles in DevOps & CI/CD.
