feat: adapt deployment for existing Docker server with nginx-proxy + Woodpecker

- docker-compose.yaml: remove port binding; add VIRTUAL_HOST/LETSENCRYPT_HOST
  env vars for nginx-proxy auto-routing; add internal + external proxy networks
- .woodpecker.yml: consolidate build steps into single ci step; add deploy-main
  step that builds + deploys on every push to main; keep deploy-tag for
  registry-pull deploys on version tags
- deploy/deploy.sh: simplify for manual/emergency use on existing server;
  add --from-registry flag for registry pull vs local build
- Remove deploy/setup-server.sh and deploy/nginx/ (not needed on existing server)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Head of Product & Engineering 2026-04-05 14:06:21 +02:00
parent f99772d8c0
commit f29b39b40c
5 changed files with 64 additions and 133 deletions

View File

@ -1,18 +1,10 @@
steps:
- name: build
image: golang:1.22-alpine
commands:
- go build ./cmd/server/
- name: vet
- name: ci
image: golang:1.22-alpine
commands:
- go vet ./...
- name: test
image: golang:1.22-alpine
commands:
- go test ./...
- go build ./cmd/server/
- name: docker-build
image: plugins/docker
@ -30,7 +22,7 @@ steps:
event: tag
ref: refs/tags/v*
- name: deploy
- name: deploy-tag
image: appleboy/drone-ssh
settings:
host:
@ -41,8 +33,30 @@ steps:
from_secret: deploy_key
script:
- cd /opt/cast-ghl-provider
- docker compose pull
- docker compose pull bridge
- docker compose up -d --remove-orphans
- sleep 5
- docker compose ps bridge
when:
event: tag
ref: refs/tags/v*
- name: deploy-main
image: appleboy/drone-ssh
settings:
host:
from_secret: deploy_host
username:
from_secret: deploy_user
key:
from_secret: deploy_key
script:
- cd /opt/cast-ghl-provider
- git pull --ff-only
- docker compose build --no-cache bridge
- docker compose up -d --remove-orphans
- sleep 5
- docker compose ps bridge
when:
branch: main
event: push

View File

@ -1,33 +1,35 @@
#!/usr/bin/env bash
# deploy.sh — Pull latest code and redeploy via Docker Compose
# Run from /opt/cast-ghl-plugin after initial server setup.
# Usage: bash deploy/deploy.sh
# deploy.sh — Manual redeploy on an existing Docker server.
# Normally Woodpecker CI handles deploys automatically on push to main or tag.
# Use this script only for manual/emergency redeploys.
# Usage: bash deploy/deploy.sh [--from-registry]
# --from-registry Pull the pre-built image from registry instead of building locally
set -euo pipefail
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$APP_DIR"
FROM_REGISTRY=false
[[ "${1:-}" == "--from-registry" ]] && FROM_REGISTRY=true
echo "==> Pulling latest code"
git pull --ff-only
echo "==> Building and restarting services"
docker compose pull mongo # pull latest Mongo image if updated
if $FROM_REGISTRY; then
echo "==> Pulling pre-built image from registry"
docker compose pull bridge
else
echo "==> Building image locally"
docker compose build --no-cache bridge
fi
echo "==> Restarting services"
docker compose up -d --remove-orphans
echo "==> Waiting for health check"
sleep 5
STATUS=$(docker compose ps --format json | python3 -c "
import sys, json
for line in sys.stdin:
s = json.loads(line)
if s.get('Service') == 'bridge':
print(s.get('Health', s.get('State', 'unknown')))
" 2>/dev/null || echo "unknown")
echo "Bridge container status: $STATUS"
echo "==> Tailing last 20 log lines"
docker compose ps bridge
docker compose logs --tail=20 bridge
echo ""

View File

@ -1,42 +0,0 @@
server {
listen 80;
server_name ghl.cast.ph;
# Let's Encrypt ACME challenge
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name ghl.cast.ph;
ssl_certificate /etc/letsencrypt/live/ghl.cast.ph/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ghl.cast.ph/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy no-referrer;
location / {
proxy_pass http://127.0.0.1:3002;
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;
# Timeouts — GHL expects quick 200 for webhook; 30s is generous
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}

View File

@ -1,60 +0,0 @@
#!/usr/bin/env bash
# setup-server.sh — Bootstrap a fresh Ubuntu 22.04/24.04 LTS Vultr VPS
# Run once as root (or with sudo) after provisioning.
# Usage: bash setup-server.sh
set -euo pipefail
DOMAIN="ghl.cast.ph"
APP_DIR="/opt/cast-ghl-plugin"
REPO_URL="https://github.com/CAST-ph/cast-ghl-plugin.git" # adjust if needed
echo "==> Updating system packages"
apt-get update -q && apt-get upgrade -y -q
echo "==> Installing dependencies"
apt-get install -y -q \
ca-certificates curl gnupg ufw \
nginx certbot python3-certbot-nginx \
git
echo "==> Installing Docker"
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -q
apt-get install -y -q docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker
echo "==> Configuring firewall"
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 'Nginx Full'
ufw --force enable
echo "==> Cloning application"
mkdir -p "$APP_DIR"
if [ -d "$APP_DIR/.git" ]; then
git -C "$APP_DIR" pull
else
git clone "$REPO_URL" "$APP_DIR"
fi
echo "==> Installing Nginx config"
cp "$APP_DIR/deploy/nginx/ghl.cast.ph.conf" /etc/nginx/sites-available/"$DOMAIN"
ln -sf /etc/nginx/sites-available/"$DOMAIN" /etc/nginx/sites-enabled/"$DOMAIN"
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
echo "==> Obtaining Let's Encrypt certificate"
certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos -m ops@cast.ph
systemctl reload nginx
echo ""
echo "=== Setup complete ==="
echo "Next: copy .env to $APP_DIR/.env then run deploy/deploy.sh"

View File

@ -1,13 +1,20 @@
services:
bridge:
build: .
# Bind to localhost only — Nginx proxies from outside
ports:
- "127.0.0.1:${PORT:-3002}:${PORT:-3002}"
# No port binding — nginx-proxy routes traffic via the shared proxy network
env_file: .env
environment:
# nginx-proxy / acme-companion auto-routing
- VIRTUAL_HOST=${VIRTUAL_HOST:-ghl.cast.ph}
- VIRTUAL_PORT=${PORT:-3002}
- LETSENCRYPT_HOST=${VIRTUAL_HOST:-ghl.cast.ph}
- LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL:-ops@cast.ph}
depends_on:
mongo:
condition: service_healthy
networks:
- internal
- proxy # shared nginx-proxy network — must match the nginx-proxy container's network
restart: unless-stopped
logging:
driver: json-file
@ -20,6 +27,8 @@ services:
# No ports exposed — only reachable by bridge on the internal network
volumes:
- mongo-data:/data/db
networks:
- internal
restart: unless-stopped
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
@ -28,5 +37,13 @@ services:
retries: 5
start_period: 20s
networks:
internal:
# Private network for bridge ↔ mongo
proxy:
external: true
# Must match the name of the existing nginx-proxy Docker network on the server.
# Check with: docker network ls | grep proxy
volumes:
mongo-data: