If you have a Linux server — whether a cloud VPS from DigitalOcean, Hetzner, Linode, or a physical machine in your data center — you can run MergeWatch with Docker Compose. This gives you full control over the deployment with minimal dependencies.
Overview
This guide covers running MergeWatch and Postgres as Docker containers, setting up a reverse proxy with TLS using nginx or Caddy, and configuring the GitHub App webhook to point at your domain.
Prerequisites
Install Docker and Docker Compose
# Ubuntu / Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Verify the installation:docker --version # 20.10+
docker compose version # 2.0+
Open port 443
Ensure your firewall allows inbound HTTPS traffic on port 443. If you use ufw: Point a domain at your server
Create a DNS A record pointing your domain (e.g. mergewatch.example.com) to your server’s public IP address. A domain is required for TLS certificates.
Gather your GitHub App credentials
| Variable | Description |
|---|
GITHUB_APP_ID | Numeric App ID from the GitHub App settings page |
GITHUB_PRIVATE_KEY | PEM-formatted private key generated for the App |
GITHUB_WEBHOOK_SECRET | Secret used to validate incoming webhook payloads |
Choose an LLM provider
Set LLM_PROVIDER to your preferred provider. For the default Anthropic provider, you also need ANTHROPIC_API_KEY.
Deploy with Docker Compose
Create the project directory
mkdir -p /opt/mergewatch && cd /opt/mergewatch
Create the docker-compose.yml
version: "3.8"
services:
mergewatch:
image: ghcr.io/santthosh/mergewatch:latest
container_name: mergewatch
restart: unless-stopped
ports:
- "3000:3000"
environment:
- GITHUB_APP_ID=${GITHUB_APP_ID}
- GITHUB_PRIVATE_KEY=${GITHUB_PRIVATE_KEY}
- GITHUB_WEBHOOK_SECRET=${GITHUB_WEBHOOK_SECRET}
- LLM_PROVIDER=${LLM_PROVIDER}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- DATABASE_URL=postgresql://mergewatch:${DB_PASSWORD}@postgres:5432/mergewatch
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:15-alpine
container_name: mergewatch-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=mergewatch
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=mergewatch
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mergewatch"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
Create the .env file
cat > .env << 'EOF'
GITHUB_APP_ID=your_app_id
GITHUB_PRIVATE_KEY=your_private_key
GITHUB_WEBHOOK_SECRET=your_webhook_secret
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=your_anthropic_key
DB_PASSWORD=a_strong_random_password
EOF
Protect the .env file. Set permissions to owner-only: chmod 600 .env
Start the services
Verify both containers are running:NAME STATUS PORTS
mergewatch Up 10 seconds 0.0.0.0:3000->3000/tcp
mergewatch-postgres Up 12 seconds 5432/tcp
Set up Postgres
By default, the docker-compose.yml above runs PostgreSQL as a container alongside MergeWatch. Data is persisted to a Docker volume (pgdata).
If you prefer an external managed database, remove the postgres service from docker-compose.yml and set DATABASE_URL in your .env file to the external connection string.
Set up a reverse proxy with TLS
Expose MergeWatch on port 443 with TLS using nginx or Caddy. GitHub requires HTTPS for webhook delivery.
# /etc/nginx/sites-available/mergewatch
server {
listen 443 ssl http2;
server_name mergewatch.example.com;
ssl_certificate /etc/letsencrypt/live/mergewatch.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mergewatch.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
server {
listen 80;
server_name mergewatch.example.com;
return 301 https://$host$request_uri;
}
Caddy automatically provisions and renews TLS certificates from Let’s Encrypt. With nginx, use certbot to obtain certificates: sudo certbot --nginx -d mergewatch.example.com
Enable the nginx site and reload:
sudo ln -s /etc/nginx/sites-available/mergewatch /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Set the webhook URL on your GitHub App to your domain followed by /webhook:
https://mergewatch.example.com/webhook
Do not expose port 3000 directly to the internet without TLS. Always use a reverse proxy with HTTPS.
Next steps