In the fast-paced world of software development, staying ahead often means automating repetitive tasks. One such task that has become critical is the deployment of applications.
Services like Render.com have gained popularity for simplifying the deployment process — but what if you could build your own auto-deploy system?
That's exactly what I set out to do at 9 PM one night — and by 1 AM, a fully working prototype was up and running.
🧠 The Thought
I wanted to replicate one powerful Render.com feature: automatic deployments when a pull request is made. This meant setting up a flow where:
- A pull request is created on GitHub
- A webhook notifies your server
- Your server spins up a new app instance on a unique subdomain
Let’s break it down.
🧩 The Problem
Our goal:
When a pull request is made → a subdomain like feature-xyz.domain.com
is created and the app is deployed live.
To do this, we need:
- ✅ Wildcard DNS setup
- ✅ GitHub webhook configuration
- ✅ Server webhook listener + shell script
- ✅ Nginx + HestiaCP setup
- ✅ Secure deployment and SSL
🌍 Setting Up DNS Records
- Buy a domain (I used GoDaddy)
- Use Cloudflare for DNS management
- Add a wildcard DNS record:
- Type: A
- Name:
*
- Points to: Your VPS IP
Test it: Visit test.domain.com
in a browser — it should resolve to your server. This step is crucial for dynamic subdomain deployments.
🔗 Configuring GitHub Webhooks
- Go to your repo → Settings → Webhooks → Add webhook
- Set the payload URL to your server
- Select
application/json
and enable the Pull Request event - Save
This will now send webhook data to your server anytime a PR is opened.
🛠️ Server Configuration & Script
You’ll need a webhook listener (like adnanh/webhook) configured to trigger a deployment script:
JSON configuration:
[
{
"id": "github-webhook",
"execute-command": "./webhookscript.sh",
"command-working-directory": ".",
"response-message": "Webhook received and processing...",
"pass-arguments-to-command": [
{
"source": "payload",
"name": "action"
},
{
"source": "entire-payload"
}
]
}
]
## ⚙️ webhookscript.sh
#!/bin/bash
if [ "$1" == "opened" ]; then
action=$(echo "$2" | jq -r '.action')
pull_request_title=$(echo "$2" | jq -r '.pull_request.title')
pull_request_url=$(echo "$2" | jq -r '.pull_request.html_url')
sender_login=$(echo "$2" | jq -r '.sender.login')
clone_url=$(echo "$2" | jq -r '.repository.ssh_url')
if [ -z "$action" ] || [ -z "$pull_request_title" ] || [ -z "$pull_request_url" ] || [ -z "$sender_login" ]; then
echo "Error: Required fields are missing or empty in the payload."
exit 1
fi
prname="${pull_request_title// /}"
prname="${prname}.domain.com"
alias="www.$prname"
v-add-web-domain server $prname ipv4 yes $alias
v-add-letsencrypt-domain server $prname $alias no
v-add-web-domain-ssl server $prname /home/server/conf/web/$prname
v-add-web-domain-ssl-force server $prname
web_directory="/home/server/web/$prname/public_html"
cd $web_directory
if [ ! -d "$web_directory/repo_name" ]; then
git clone "$clone_url"
fi
cd repo_name/app
cp -r ~/.env $web_directory
npm install
npm run build
mv -vf dist/* ../../
fi
🧭 Final Thoughts
We covered:
- Setting up DNS for wildcard subdomains
- Wiring GitHub → Webhook → Script → Deployment
- Using HestiaCP’s
v-add-*
commands to manage subdomains and SSL
At 1 AM, exhausted but proud, I looked at the live PR preview link and smiled.
This is what it means to build something end to end. 💻
Wishing you productive coding and seamless deployments! 🚀
Follow me on GitHub for more DIY infra experiments and DevOps setups.
#DevOps #GitHubWebhooks #HestiaCP #Nginx #SelfHosting #Rustacean #Automation #PRDeployments