setup-ec2.sh
bash
sha256:0997d6250ae6476362f6fe2025af7789f46d03df3e9f34356d5e8ee79b201923
fix(issues): use issue number as pagination cursor, not cre…
Sonnet 4.6
patch
9 days ago
| 1 | #!/usr/bin/env bash |
| 2 | # Run this ON the EC2 instance (via SSH) after provisioning. |
| 3 | # Installs Docker and nginx, then configures the Cloudflare Origin Certificate. |
| 4 | # No Certbot or Let's Encrypt — Cloudflare terminates SSL at the edge. |
| 5 | # |
| 6 | # Prerequisites: |
| 7 | # 1. Generate a Cloudflare Origin Certificate for musehub.ai: |
| 8 | # Cloudflare Dashboard → musehub.ai → SSL/TLS → Origin Server |
| 9 | # → Create Certificate → RSA 2048 → 15 years |
| 10 | # Save certificate → /etc/ssl/cloudflare/origin.pem |
| 11 | # Save private key → /etc/ssl/cloudflare/origin.key |
| 12 | # |
| 13 | # 2. In Cloudflare: SSL/TLS → Overview → set mode to "Full (Strict)" |
| 14 | # |
| 15 | # Usage (from your Mac): |
| 16 | # scp -i ~/.ssh/musehub-key.pem -r deploy/ [email protected]:/home/ubuntu/ |
| 17 | # # Copy cert files: |
| 18 | # scp -i ~/.ssh/musehub-key.pem origin.pem origin.key [email protected]:/home/ubuntu/ |
| 19 | # ssh -i ~/.ssh/musehub-key.pem [email protected] |
| 20 | # chmod +x ~/deploy/setup-ec2.sh && ~/deploy/setup-ec2.sh |
| 21 | |
| 22 | set -euo pipefail |
| 23 | |
| 24 | DOMAIN="musehub.ai www.musehub.ai" |
| 25 | SITE_NAME="musehub" |
| 26 | APP_DIR="/opt/musehub" |
| 27 | |
| 28 | echo "==> [1/7] System update" |
| 29 | sudo apt-get update -qq && sudo apt-get upgrade -y -qq |
| 30 | |
| 31 | echo "==> [2/7] Install Docker" |
| 32 | curl -fsSL https://get.docker.com | sudo sh |
| 33 | sudo usermod -aG docker ubuntu |
| 34 | echo " Docker installed. NOTE: You may need to log out/in for the group to take effect." |
| 35 | echo " For this script we use 'sudo docker' throughout." |
| 36 | |
| 37 | echo "==> [3/7] Install nginx" |
| 38 | sudo apt-get install -y -qq nginx |
| 39 | |
| 40 | echo "==> [4/7] Install Cloudflare Origin Certificate" |
| 41 | sudo mkdir -p /etc/ssl/cloudflare |
| 42 | |
| 43 | # Copy certs from home dir if present (scp'd in before running this script) |
| 44 | if [ -f ~/origin.pem ] && [ -f ~/origin.key ]; then |
| 45 | sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem |
| 46 | sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key |
| 47 | sudo chmod 644 /etc/ssl/cloudflare/origin.pem |
| 48 | sudo chmod 640 /etc/ssl/cloudflare/origin.key |
| 49 | sudo chown root:root /etc/ssl/cloudflare/origin.pem /etc/ssl/cloudflare/origin.key |
| 50 | echo " Origin certificate installed." |
| 51 | else |
| 52 | echo "" |
| 53 | echo " !! CERT FILES NOT FOUND — install them manually before nginx will start:" |
| 54 | echo " scp origin.pem origin.key ubuntu@<IP>:/home/ubuntu/" |
| 55 | echo " sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem" |
| 56 | echo " sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key" |
| 57 | echo " sudo chmod 640 /etc/ssl/cloudflare/origin.key" |
| 58 | echo " sudo nginx -s reload" |
| 59 | echo "" |
| 60 | fi |
| 61 | |
| 62 | echo "==> [5/7] Configure nginx" |
| 63 | |
| 64 | # Blue-green active-port file: deploy.sh rewrites this and does nginx -s reload |
| 65 | echo "server 127.0.0.1:1337;" | sudo tee /etc/nginx/musehub-active-port > /dev/null |
| 66 | |
| 67 | # Install the Cloudflare-ready nginx config, substituting the domain(s) |
| 68 | sudo sed "s/DOMAIN_PLACEHOLDER/$DOMAIN/g" \ |
| 69 | "$(dirname "$0")/nginx-cf.conf" \ |
| 70 | | sudo tee /etc/nginx/sites-available/$SITE_NAME > /dev/null |
| 71 | |
| 72 | sudo ln -sf /etc/nginx/sites-available/$SITE_NAME /etc/nginx/sites-enabled/$SITE_NAME |
| 73 | sudo rm -f /etc/nginx/sites-enabled/default |
| 74 | sudo nginx -t |
| 75 | sudo systemctl reload nginx |
| 76 | echo " nginx configured for $DOMAIN" |
| 77 | |
| 78 | echo "==> [6/7] Create app directory" |
| 79 | sudo mkdir -p "$APP_DIR" |
| 80 | sudo chown ubuntu:ubuntu "$APP_DIR" |
| 81 | echo " $APP_DIR ready — sync code from your Mac with:" |
| 82 | echo " rsync -az --exclude='.env' --exclude='node_modules/' --exclude='__pycache__/' \\" |
| 83 | echo " --exclude='.muse/' --exclude='*.pyc' \\" |
| 84 | echo " -e 'ssh -i ~/.ssh/musehub-key.pem' \\" |
| 85 | echo " ~/musehub/ ubuntu@\$(hostname -I | awk '{print \$1}'):$APP_DIR/" |
| 86 | |
| 87 | echo "==> [7/7] Create production .env" |
| 88 | if [ ! -f "$APP_DIR/.env" ]; then |
| 89 | DB_PASSWORD=$(openssl rand -hex 16) |
| 90 | WEBHOOK_SECRET_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 2>/dev/null || echo "GENERATE_ME_WITH_FERNET") |
| 91 | |
| 92 | cat > "$APP_DIR/.env" << EOF |
| 93 | DEBUG=false |
| 94 | DATABASE_URL=postgresql+asyncpg://musehub:${DB_PASSWORD}@postgres:5432/musehub |
| 95 | DB_PASSWORD=${DB_PASSWORD} |
| 96 | CORS_ORIGINS=["https://musehub.ai", "https://www.musehub.ai"] |
| 97 | WEBHOOK_SECRET_KEY=${WEBHOOK_SECRET_KEY} |
| 98 | MUSEHUB_ALLOWED_ORIGINS=["musehub.ai", "www.musehub.ai"] |
| 99 | EOF |
| 100 | echo " .env created with generated secrets" |
| 101 | echo "" |
| 102 | echo " !! SAVE THESE — they are in $APP_DIR/.env !!" |
| 103 | cat "$APP_DIR/.env" |
| 104 | echo "" |
| 105 | else |
| 106 | echo " .env already exists, skipping generation" |
| 107 | fi |
| 108 | |
| 109 | echo "" |
| 110 | echo "============================================================" |
| 111 | echo " SETUP COMPLETE" |
| 112 | echo "============================================================" |
| 113 | echo " https://$DOMAIN is live" |
| 114 | echo " App dir : $APP_DIR" |
| 115 | echo " Secrets : $APP_DIR/.env (back these up!)" |
| 116 | echo "" |
| 117 | echo " Next: sync code, run migrations, start the stack:" |
| 118 | echo " rsync -az ... ~/musehub/ ubuntu@<IP>:$APP_DIR/" |
| 119 | echo " cd $APP_DIR && sudo docker compose up -d postgres musehub-runner" |
| 120 | echo " sudo docker compose build musehub" |
| 121 | echo " sudo docker compose run --rm --no-deps musehub alembic upgrade head" |
| 122 | echo " sudo docker run -d --name musehub-blue --network musehub-internal \\" |
| 123 | echo " --env-file .env -e SKIP_MIGRATIONS=1 \\" |
| 124 | echo " -e DATABASE_URL=postgresql+asyncpg://musehub:\$(grep ^DB_PASSWORD .env | cut -d= -f2)@postgres:5432/musehub \\" |
| 125 | echo " -v musehub_data:/data -p 127.0.0.1:1337:1337 --restart unless-stopped musehub/musehub" |
| 126 | echo " bash deploy/deploy.sh --init" |
| 127 | echo "" |
| 128 | echo " Subsequent deploys (zero downtime):" |
| 129 | echo " rsync ... && bash deploy/deploy.sh" |
| 130 | echo " Run deploy/seed.sh to create your account + repos" |
| 131 | echo "============================================================" |
File History
1 commit
sha256:0997d6250ae6476362f6fe2025af7789f46d03df3e9f34356d5e8ee79b201923
fix(issues): use issue number as pagination cursor, not cre…
Sonnet 4.6
patch
9 days ago