Database reset, indexes, RLS policies, triggers, and updated deployment guide Co-authored-by: Moheeb Zara <[email protected]>
16 KiB
Hackerspace.sh — Deployment Guide
Complete instructions for every supported deployment target.
Table of Contents
- Prerequisites
- Supabase Setup (required for all targets)
- Environment Variables Reference
- Option A — Vercel (recommended)
- Option B — DigitalOcean App Platform
- Option C — DigitalOcean Droplet (Docker / self-hosted)
- Post-Deploy Checklist
- Upgrading an Existing Deployment
- Troubleshooting
1. Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 20+ | Local development |
| pnpm | 9+ | Package manager |
| Git | any | Source control |
| Supabase account | free tier works | Database + auth + realtime |
| Vercel / DigitalOcean account | — | Hosting |
Clone the repo:
git clone https://github.com/virgilvox/hackerspace-management.git
cd hackerspace-management
pnpm install
2. Supabase Setup
This step is identical regardless of which hosting option you choose.
2.1 Create a Supabase project
- Go to supabase.com → New project
- Choose a region close to your users
- Set a strong database password and save it — you won't need it directly but keep it safe
- Wait ~2 minutes for provisioning
2.2 Apply the schema
- In the Supabase dashboard, go to SQL Editor
- Click New query
- Paste the entire contents of
scripts/schema.sql - Click Run (or Ctrl+Enter)
You should see no errors. The script is idempotent — safe to re-run.
2.3 Configure Auth
In the Supabase dashboard:
Authentication → Settings → General
- Set Site URL to your app's public URL (e.g.
https://yourapp.vercel.app) - Add the same URL to Redirect URLs
- Also add
http://localhost:3000for local dev
Authentication → Settings → Email
- Enable Confirm email if you want verified signups
- Customize the email templates as needed
Authentication → Providers
- Email/Password is enabled by default — no changes needed
- Optionally enable Google, GitHub, etc. (social login buttons are wired in the UI)
2.4 Enable Realtime
The schema script already runs ALTER PUBLICATION supabase_realtime ADD TABLE ... for comms_messages and comms_channels. Verify in:
Database → Replication — confirm both tables appear under supabase_realtime.
2.5 Get your API keys
Project Settings → API
Copy these four values — you'll need them in every deployment:
NEXT_PUBLIC_SUPABASE_URL = https://xxxxxxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY = eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY = eyJhbGci... (keep this SECRET — server-side only)
2.6 Create your first Space + Admin user
After deploying the app, navigate to /signup. The signup flow will:
- Create an
auth.usersrow in Supabase - Trigger
handle_space_signup()which inserts aspace_membersrow
To make yourself an admin, run this in the SQL editor (replace values):
UPDATE public.space_members
SET role = 'admin', status = 'current'
WHERE email = '[email protected]';
3. Environment Variables
Create .env.local for local dev. For deployed environments, set these in your host's dashboard.
# ── Supabase ──────────────────────────────────────────────────
NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
# ── App ───────────────────────────────────────────────────────
NEXT_PUBLIC_APP_URL=https://yourdomain.com
# ── PayPal (optional — set in app Settings UI too) ────────────
# These are also stored per-space in the integrations table.
# The API route reads from the DB, not from env vars.
# No env vars needed for PayPal.
# ── Node ──────────────────────────────────────────────────────
NODE_ENV=production
Never commit .env.local to git. The .gitignore already excludes it.
4. Option A — Vercel
The simplest and fastest path. Zero server management.
4.1 Deploy via Vercel CLI
# Install Vercel CLI globally
pnpm add -g vercel
# From the project root:
vercel
# Follow the prompts:
# - Link to existing project or create new
# - Framework: Next.js (auto-detected)
# - Root directory: ./ (default)
4.2 Set environment variables
vercel env add NEXT_PUBLIC_SUPABASE_URL production
vercel env add NEXT_PUBLIC_SUPABASE_ANON_KEY production
vercel env add SUPABASE_SERVICE_ROLE_KEY production
vercel env add NEXT_PUBLIC_APP_URL production
Or set them in the Vercel dashboard: Project → Settings → Environment Variables
4.3 Deploy to production
vercel --prod
4.4 Connect a custom domain (optional)
Vercel dashboard → Domains → add your domain → follow DNS instructions.
4.5 Continuous deployment
Push to main branch → Vercel auto-deploys. Push to any other branch → preview deployment at a unique URL.
git push origin main # triggers production deploy
git push origin feat/my-feature # triggers preview deploy
5. Option B — DigitalOcean App Platform
Managed container hosting. No Docker knowledge required. Scales automatically.
5.1 Push your repo to GitHub
DigitalOcean App Platform deploys directly from GitHub.
git remote add origin https://github.com/YOUR_ORG/hackerspace-management.git
git push -u origin main
5.2 Create the app
- Go to cloud.digitalocean.com/apps
- Click Create App → GitHub → authorize and select your repo
- Select branch:
main - DigitalOcean auto-detects Next.js — confirm the build settings:
- Build command:
pnpm install && pnpm build - Run command:
pnpm start - HTTP port:
3000
- Build command:
5.3 Set environment variables
In the App Platform UI, go to Settings → App-Level Environment Variables and add:
NEXT_PUBLIC_SUPABASE_URL = (your value)
NEXT_PUBLIC_SUPABASE_ANON_KEY = (your value)
SUPABASE_SERVICE_ROLE_KEY = (mark as Encrypted)
NEXT_PUBLIC_APP_URL = https://your-app.ondigitalocean.app
NODE_ENV = production
Mark SUPABASE_SERVICE_ROLE_KEY as Encrypted so it's not visible in logs.
5.4 Choose a plan
- Basic ($5/mo): fine for small hackerspaces (<50 members)
- Professional ($12/mo): recommended for production — includes more RAM and 99.99% SLA
5.5 Deploy
Click Create Resources → wait ~3 minutes for the first build.
5.6 Custom domain
App Platform dashboard → Settings → Domains → add domain → update your DNS with the CNAME provided.
5.7 Continuous deployment
App Platform auto-deploys on every push to main by default. You can also configure it to deploy only on tagged releases.
6. Option C — DigitalOcean Droplet
Full self-hosted on a VPS. Maximum control. Use this if you want everything on your own infrastructure.
6.1 Create a Droplet
- Go to cloud.digitalocean.com/droplets
- Create Droplet
- Image: Ubuntu 24.04 LTS
- Plan: Basic — Regular — $12/mo (2 vCPU, 2GB RAM) minimum; $24/mo recommended
- Datacenter: closest to your users
- Authentication: SSH key (strongly recommended over password)
- Note the Droplet IP address
6.2 Initial server setup
SSH in and harden the server:
ssh root@YOUR_DROPLET_IP
# Create a non-root user
adduser deploy
usermod -aG sudo deploy
# Copy SSH key to new user
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
# Switch to deploy user
su - deploy
6.3 Install Docker and Docker Compose
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker deploy
# Install Docker Compose plugin
sudo apt-get install -y docker-compose-plugin
# Verify
docker --version
docker compose version
6.4 Install Nginx and Certbot
sudo apt-get update
sudo apt-get install -y nginx certbot python3-certbot-nginx
6.5 Clone the repo on the server
cd /opt
sudo git clone https://github.com/YOUR_ORG/hackerspace-management.git hackerspace
sudo chown -R deploy:deploy /opt/hackerspace
cd /opt/hackerspace
6.6 Create the Docker Compose file
cat > /opt/hackerspace/docker-compose.yml << 'EOF'
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: hackerspace_app
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env.production
environment:
NODE_ENV: production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
default:
name: hackerspace_net
EOF
6.7 Create the Dockerfile
cat > /opt/hackerspace/Dockerfile << 'EOF'
FROM node:20-alpine AS base
RUN corepack enable pnpm
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
EOF
6.8 Enable standalone output in Next.js config
Edit next.config.mjs and ensure it contains:
const nextConfig = {
output: 'standalone',
// ...rest of your config
}
6.9 Create the production env file
cat > /opt/hackerspace/.env.production << 'EOF'
NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
NEXT_PUBLIC_APP_URL=https://yourdomain.com
NODE_ENV=production
EOF
# Lock down permissions — this file contains secrets
chmod 600 /opt/hackerspace/.env.production
6.10 Build and start the container
cd /opt/hackerspace
docker compose build
docker compose up -d
# Check logs
docker compose logs -f app
6.11 Configure Nginx as reverse proxy
sudo nano /etc/nginx/sites-available/hackerspace
Paste:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Enable and test:
sudo ln -s /etc/nginx/sites-available/hackerspace /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
6.12 SSL with Let's Encrypt
Point your domain's A record to the Droplet IP first, then:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will auto-configure HTTPS and set up auto-renewal. Verify renewal works:
sudo certbot renew --dry-run
6.13 Set up automatic deployments (optional)
Create a deploy script:
cat > /opt/hackerspace/deploy.sh << 'EOF'
#!/bin/bash
set -e
cd /opt/hackerspace
git pull origin main
docker compose build app
docker compose up -d --no-deps app
docker image prune -f
echo "Deploy complete: $(date)"
EOF
chmod +x /opt/hackerspace/deploy.sh
To trigger a deploy from your local machine:
ssh deploy@YOUR_DROPLET_IP '/opt/hackerspace/deploy.sh'
Or set up a GitHub Actions workflow:
# .github/workflows/deploy.yml
name: Deploy to Droplet
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DROPLET_IP }}
username: deploy
key: ${{ secrets.DROPLET_SSH_KEY }}
script: /opt/hackerspace/deploy.sh
Add DROPLET_IP and DROPLET_SSH_KEY to your GitHub repo's Settings → Secrets.
6.14 Firewall
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
7. Post-Deploy Checklist
Run through this after every fresh deployment:
- App loads at your domain with HTTPS
/signupcreates a user and redirects to/dashboard- Dashboard shows "No members yet" (not an error)
- Supabase dashboard → Authentication → Users shows the signup
space_memberstable has a row for the new user- Chat page loads channels (general, announcements, ops)
- Send a message — it appears immediately without page refresh
- Upgrade your user to admin via SQL (see section 2.6)
- Settings page → save space settings → no error toast
- PayPal integration → save credentials → "LIVE" badge appears
8. Upgrading
When the schema changes, a new numbered script will be added to scripts/. For example scripts/012_add_feature.sql. Run only the new script in the Supabase SQL editor — do not re-run schema.sql on an existing database.
scripts/
schema.sql ← fresh deployment only
012_add_feature.sql ← incremental upgrade
013_...sql
For Vercel / App Platform: push to main, the build auto-deploys.
For Droplet: ssh deploy@IP '/opt/hackerspace/deploy.sh'
9. Troubleshooting
"relation does not exist" errors
The schema was not applied or was partially applied. Re-run scripts/schema.sql in the Supabase SQL editor.
Users can sign up but see no data
The handle_space_signup trigger is not firing or the space_id metadata was not passed during signup. Check:
SELECT * FROM public.space_members ORDER BY joined_at DESC LIMIT 5;
If empty, ensure a spaces row exists first:
INSERT INTO public.spaces (name, slug, invite_code)
VALUES ('My Hackerspace', 'my-hackerspace', 'INVITE123');
Then re-run signup with the space's id in the metadata.
Chat messages not appearing in realtime
Verify realtime is enabled:
SELECT * FROM pg_publication_tables WHERE pubname = 'supabase_realtime';
Both comms_messages and comms_channels must appear. If not:
ALTER PUBLICATION supabase_realtime ADD TABLE public.comms_messages;
ALTER PUBLICATION supabase_realtime ADD TABLE public.comms_channels;
500 errors on API routes
Check that SUPABASE_SERVICE_ROLE_KEY is set in your deployment environment. This key is required for server-side actions.
Docker container exits immediately
docker compose logs app
Most common cause: missing .env.production file or a missing required env var.
SSL certificate not renewing
sudo systemctl status certbot.timer
sudo journalctl -u certbot
If the timer is not running: sudo systemctl enable --now certbot.timer