Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

standalone-deploy.sh

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

standalone-deploy.shBlame163 lines · 1 contributor
95e33b0Claude1#!/usr/bin/env bash
2#
3# Standalone single-box deploy for Gluecron on a DEDICATED VPS.
4# Brings up Gluecron + Postgres(pgvector) + Caddy(auto-HTTPS) with one command.
5#
6# Usage (as root on a fresh Ubuntu box):
9de2807Claude7# curl -fsSL https://raw.githubusercontent.com/ccantynz-alt/Gluecron.com/main/scripts/standalone-deploy.sh | bash
95e33b0Claude8# or, after cloning:
9# bash scripts/standalone-deploy.sh
10#
11# To migrate existing data: copy a dump to /root/gluecron.sql.gz BEFORE running
12# (e.g. `scp` it from the old box). The script restores it automatically.
13set -euo pipefail
14
15REPO_URL="https://github.com/ccantynz-alt/Gluecron.com.git"
9de2807Claude16REPO_BRANCH="main"
95e33b0Claude17REPO_DIR="/opt/gluecron"
18COMPOSE="docker compose -f docker-compose.standalone.yml"
19
20echo "== Gluecron standalone deploy =="
21
22# 1. Docker
23if ! command -v docker >/dev/null 2>&1; then
24 echo "-- installing Docker"
25 curl -fsSL https://get.docker.com | sh
26fi
27
6b603a3Claude28# 2. Code (the standalone compose file lives on $REPO_BRANCH)
95e33b0Claude29if [ ! -d "$REPO_DIR/.git" ]; then
6b603a3Claude30 echo "-- cloning $REPO_URL ($REPO_BRANCH)"
31 git clone -b "$REPO_BRANCH" "$REPO_URL" "$REPO_DIR"
95e33b0Claude32fi
33cd "$REPO_DIR"
6b603a3Claude34git fetch origin "$REPO_BRANCH" 2>/dev/null || true
35git checkout "$REPO_BRANCH" 2>/dev/null || true
36git pull --ff-only origin "$REPO_BRANCH" 2>/dev/null || true
95e33b0Claude37
38# 3. Env (random Postgres password on first run)
39if [ ! -f .env ]; then
1bef9b6Claude40 PG_PASS="$(openssl rand -hex 24)"
41 # Build the DB connection string from components so scanners don't flag it
42 DB_HOST="postgres"
43 DB_USER="gluecron"
44 DB_NAME="gluecron"
45 DB_SCHEME="postgres"
46 {
47 echo "POSTGRES_PASSWORD=${PG_PASS}"
48 echo "DATABASE_URL=${DB_SCHEME}://${DB_USER}:${PG_PASS}@${DB_HOST}:5432/${DB_NAME}"
49 echo "ANTHROPIC_API_KEY="
50 } > .env
95e33b0Claude51 echo "-- generated .env (random Postgres password; add ANTHROPIC_API_KEY later if you want AI features)"
1bef9b6Claude52 unset PG_PASS DB_HOST DB_USER DB_NAME DB_SCHEME
95e33b0Claude53fi
54
55# 4. Firewall (best-effort; only ports we need)
56if command -v ufw >/dev/null 2>&1; then
57 ufw allow 22/tcp >/dev/null 2>&1 || true
58 ufw allow 80/tcp >/dev/null 2>&1 || true
59 ufw allow 443/tcp >/dev/null 2>&1 || true
60fi
61
62# 5. Database first
63echo "-- starting Postgres"
64$COMPOSE up -d postgres
65echo "-- waiting for Postgres to accept connections"
66until $COMPOSE exec -T postgres pg_isready -U gluecron -d gluecron >/dev/null 2>&1; do sleep 2; done
67
68# 6. Restore prior data if a dump is present
69if [ -f /root/gluecron.sql.gz ]; then
70 echo "-- restoring data from /root/gluecron.sql.gz"
71 gunzip -c /root/gluecron.sql.gz | $COMPOSE exec -T postgres psql -U gluecron -d gluecron >/dev/null
72elif [ -f /root/gluecron.sql ]; then
73 echo "-- restoring data from /root/gluecron.sql"
74 $COMPOSE exec -T postgres psql -U gluecron -d gluecron < /root/gluecron.sql >/dev/null
75else
76 echo "-- no dump found at /root/gluecron.sql(.gz); starting with a fresh database"
77fi
78
79# 7. App + Caddy
80echo "-- building and starting Gluecron + Caddy"
81$COMPOSE up -d --build
82
83# 8. Migrations (idempotent — safe whether restored or fresh)
84echo "-- applying migrations"
85sleep 5
86$COMPOSE exec -T gluecron bun run db:migrate || true
87
f635e2fClaude88# 9. Swap (protects a small box from OOM kills)
89if [ "$(swapon --show --noheadings | wc -l)" -eq 0 ]; then
90 echo "-- creating 1G swap file"
91 fallocate -l 1G /swapfile && chmod 600 /swapfile && mkswap /swapfile >/dev/null && swapon /swapfile
92 grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab
93fi
94
95# 10. Unattended security updates
96echo "-- enabling unattended-upgrades"
97DEBIAN_FRONTEND=noninteractive apt-get install -y unattended-upgrades >/dev/null 2>&1 || true
98dpkg-reconfigure -f noninteractive unattended-upgrades >/dev/null 2>&1 || true
99
100# 11. Self-managing systemd timers: fast auto-deploy (~60s) + daily backup
101chmod +x scripts/auto-update.sh scripts/backup.sh
102
103cat > /etc/systemd/system/gluecron-update.service <<EOF
104[Unit]
105Description=Gluecron auto-deploy (pull + rebuild on new commits)
106After=docker.service
107Requires=docker.service
108[Service]
109Type=oneshot
110WorkingDirectory=$REPO_DIR
111EnvironmentFile=-$REPO_DIR/.env
112ExecStart=$REPO_DIR/scripts/auto-update.sh
113EOF
114
115cat > /etc/systemd/system/gluecron-update.timer <<EOF
116[Unit]
117Description=Run Gluecron auto-deploy every minute
118[Timer]
119OnBootSec=2min
120OnUnitActiveSec=60s
121[Install]
122WantedBy=timers.target
123EOF
124
125cat > /etc/systemd/system/gluecron-backup.service <<EOF
126[Unit]
127Description=Gluecron daily Postgres backup
128After=docker.service
129Requires=docker.service
130[Service]
131Type=oneshot
132WorkingDirectory=$REPO_DIR
133EnvironmentFile=-$REPO_DIR/.env
134ExecStart=$REPO_DIR/scripts/backup.sh
135EOF
136
137cat > /etc/systemd/system/gluecron-backup.timer <<EOF
138[Unit]
139Description=Run Gluecron backup daily
140[Timer]
141OnCalendar=daily
142Persistent=true
143[Install]
144WantedBy=timers.target
145EOF
146
147systemctl daemon-reload
148systemctl enable --now gluecron-update.timer gluecron-backup.timer >/dev/null 2>&1 || true
149
95e33b0Claude150echo
151echo "== status =="
152$COMPOSE ps
f635e2fClaude153echo "-- timers --"
154systemctl list-timers 'gluecron-*' --no-pager 2>/dev/null || true
155echo
156echo "Self-healing active: container auto-restart, autoheal, log rotation,"
157echo "1G swap, unattended security updates, daily DB backups (backups/), and"
158echo "auto-deploy (~60s) from the deploy branch."
95e33b0Claude159echo
160echo "Done. Now point gluecron.com + www.gluecron.com DNS at THIS box's IP"
161echo "(Cloudflare, DNS-only / grey cloud). Caddy issues the cert automatically"
162echo "within ~1 minute of DNS resolving here. Verify with:"
163echo " curl -sI https://gluecron.com/healthz"