Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis
This guide walks you through deploying OpenCodeHub to Vercel using:
- Turso - Serverless SQLite database (edge-compatible)
- Google Drive API - File storage for repositories
- Upstash Redis - Serverless Redis for sessions and caching
Estimated time: 30-45 minutes
Cost: Free tier available for all services
- Prerequisites
- Setup Turso Database
- Setup Google Drive Storage
- Setup Upstash Redis
- Configure Project
- Deploy to Vercel
- Post-Deployment
- Troubleshooting
Before starting, ensure you have:
- GitHub account
- Vercel account (vercel.com)
- Turso account (turso.tech)
- Google Cloud account (console.cloud.google.com)
- Upstash account (upstash.com)
- OpenCodeHub repository forked/cloned
Turso provides edge-compatible, serverless SQLite databases perfect for Vercel.
# macOS/Linux
curl -sSfL https://get.tur.so/install.sh | bash
# Windows (PowerShell)
irm get.tur.so/install.ps1 | iex
# Verify installation
turso --versionturso auth loginThis opens your browser to authenticate.
# Create database
turso db create opencodehub
# Create auth token
turso db tokens create opencodehub
# Get database URL
turso db show opencodehub --urlSave these values:
TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io
TURSO_AUTH_TOKEN=eyJhbGciOiJ...# Download schema
curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql
# Apply schema
turso db shell opencodehub < schema.sql
# Verify tables
turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';"Expected output:
users
repositories
pull_requests
pr_stacks
merge_queue_items
ai_reviews
...
Google Drive API will store Git repositories and LFS files.
- Go to Google Cloud Console
- Click Select a project → New Project
- Name:
opencodehub-storage - Click Create
- In the Cloud Console, go to APIs & Services → Enable APIs
- Search for "Google Drive API"
- Click Enable
- Go to APIs & Services → Credentials
- Click Create Credentials → Service Account
- Name:
opencodehub-storage-sa - Click Create and Continue
- Grant role: Editor
- Click Done
- Click on the service account you just created
- Go to Keys tab
- Click Add Key → Create new key
- Choose JSON
- Click Create (downloads
opencodehub-storage-xxxxx.json)
- Go to Google Drive
- Create a new folder:
OpenCodeHub-Repositories - Right-click → Share
- Add the service account email:
opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com - Grant Editor access
- Copy the Folder ID from the URL:
https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J ^^^^^^^^^^^^^^^^^^^^ This is your folder ID
Open the downloaded JSON file and extract these values:
{
"type": "service_account",
"project_id": "opencodehub-storage",
"private_key_id": "abc123...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...",
"client_email": "opencodehub-storage-sa@...",
"client_id": "123456789...",
...
}Save for later:
GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J
GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
GOOGLE_CLIENT_ID=123456789...Upstash provides serverless Redis, perfect for Vercel edge functions.
- Go to Upstash Console
- Click Create Database
- Name:
opencodehub-sessions - Region: Choose closest to your users (e.g.,
us-east-1) - Type: Regional (free tier)
- Click Create
After creation, copy these values:
REST API:
UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io
UPSTASH_REDIS_REST_TOKEN=AaBbCc...==Redis URL (optional):
REDIS_URL=redis://default:password@region.upstash.io:portUpstash Redis will handle:
- User sessions
- Rate limiting
- Temporary locks
- Cache
No additional setup needed!
Create/update src/db/turso.ts:
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
const turso = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
export const db = drizzle(turso);Create/update src/lib/storage/google-drive.ts:
import { google } from 'googleapis';
const auth = new google.auth.GoogleAuth({
credentials: {
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
},
scopes: ['https://www.googleapis.com/auth/drive.file'],
});
const drive = google.drive({ version: 'v3', auth });
const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID;
export async function uploadFile(fileName: string, content: Buffer) {
const response = await drive.files.create({
requestBody: {
name: fileName,
parents: [folderId!],
},
media: {
body: content,
},
});
return response.data.id;
}
export async function downloadFile(fileId: string) {
const response = await drive.files.get(
{ fileId, alt: 'media' },
{ responseType: 'arraybuffer' }
);
return Buffer.from(response.data as ArrayBuffer);
}
export async function deleteFile(fileId: string) {
await drive.files.delete({ fileId });
}Create/update src/lib/redis.ts:
import { Redis } from '@upstash/redis';
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// Session management
export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) {
await redis.setex(sessionId, ttl, JSON.stringify(data));
}
export async function getSession(sessionId: string) {
const data = await redis.get(sessionId);
return data ? JSON.parse(data as string) : null;
}
export async function deleteSession(sessionId: string) {
await redis.del(sessionId);
}# Add Turso client
bun add @libsql/client drizzle-orm
# Add Google Drive API
bun add googleapis
# Add Upstash Redis
bun add @upstash/redisCreate .env.example:
# === Turso Database ===
TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=your-token
# === Google Drive Storage ===
GOOGLE_DRIVE_FOLDER_ID=your-folder-id
GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
GOOGLE_CLIENT_ID=your-client-id
# === Upstash Redis ===
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token
# === Application ===
SITE_URL=https://your-app.vercel.app
JWT_SECRET=generate-a-strong-secret
SESSION_SECRET=generate-another-strong-secret
INTERNAL_HOOK_SECRET=generate-hook-secret
# === Optional: AI Review ===
AI_PROVIDER=openai
OPENAI_API_KEY=sk-...# Generate secrets
openssl rand -base64 32 # JWT_SECRET
openssl rand -base64 32 # SESSION_SECRET
openssl rand -base64 32 # INTERNAL_HOOK_SECRETbun install -g vercel
vercel loginOption A: Via Vercel Dashboard
- Go to Vercel Dashboard
- Click Add New → Project
- Import your GitHub repository
- Select
OpencodeHub
Option B: Via CLI
cd opencodehub
vercelIn Vercel Dashboard:
- Go to Project Settings → Environment Variables
- Add all variables from step 4.5:
Turso:
TURSO_DATABASE_URL=libsql://opencodehub-xxxx.turso.ioTURSO_AUTH_TOKEN=eyJhbG...
Google Drive:
GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0JGOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@...GOOGLE_PRIVATE_KEY= Paste entire private key (including\n)GOOGLE_CLIENT_ID=123456789...
Upstash Redis:
UPSTASH_REDIS_REST_URL=https://...upstash.ioUPSTASH_REDIS_REST_TOKEN=AaBbCc...
Application:
SITE_URL=https://your-project.vercel.app(update later)JWT_SECRET= Generated secretSESSION_SECRET= Generated secretINTERNAL_HOOK_SECRET= Generated secret
AI (Optional):
AI_PROVIDER=openaiOPENAI_API_KEY=sk-...
In Project Settings → Build & Development Settings:
- Framework Preset: Astro
- Build Command:
bun run build - Output Directory:
dist - Install Command:
bun install
Click Deploy or run:
vercel --prodDeployment will:
- Install dependencies
- Build the project
- Deploy to Vercel Edge Network
- Provide deployment URL
- Copy your Vercel deployment URL:
https://opencodehub-abc123.vercel.app - Update environment variable:
- Go to Settings → Environment Variables
- Edit
SITE_URL= your actual URL
- Redeploy (Vercel Dashboard → Deployments → Redeploy)
Option A: Via Turso CLI
turso db shell opencodehub
-- In the Turso shell:
INSERT INTO users (id, email, password_hash, username, role, created_at)
VALUES (
'admin-user-id',
'admin@yourcompany.com',
'$2b$10$...', -- bcrypt hash of your password
'admin',
'admin',
current_timestamp
);Generate bcrypt hash:
// Run in Node.js
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash('your-password', 10);
console.log(hash);Option B: Via Deployment Script
Create scripts/create-admin-vercel.ts:
import { db } from '../src/db/turso';
import { users } from '../src/db/schema';
import bcrypt from 'bcrypt';
const email = process.env.ADMIN_EMAIL || 'admin@example.com';
const password = process.env.ADMIN_PASSWORD || 'changeme';
const hash = await bcrypt.hash(password, 10);
await db.insert(users).values({
id: crypto.randomUUID(),
email,
username: 'admin',
password_hash: hash,
role: 'admin',
createdAt: new Date(),
});
console.log(`✅ Admin user created: ${email}`);Run once:
ADMIN_EMAIL=your@email.com ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts- Go to Settings → Domains
- Add your custom domain:
git.yourcompany.com - Follow DNS configuration instructions
- Update
SITE_URLto your custom domain
Vercel Analytics:
- Go to Analytics tab
- Enable Web Analytics
- Enable Speed Insights
Sentry (Optional):
bun add @sentry/astroAdd to .env:
SENTRY_DSN=https://...@sentry.io/...Visit: https://your-app.vercel.app/api/health
Should return:
{
"status": "ok",
"database": "connected",
"redis": "connected",
"storage": "configured"
}- Login as admin
- Create a repository
- Push a commit
- Verify file appears in Google Drive folder
- Login
- Check session persists
- Logout
- Session cleared
# Install hey
go install github.com/rakyll/hey@latest
# Test
hey -n 1000 -c 10 https://your-app.vercel.appExpected: >90% success rate
Error: Failed to connect to Turso
Solution:
# Verify token is valid
turso db tokens validate opencodehub YOUR_TOKEN
# Regenerate if needed
turso db tokens create opencodehubUpdate environment variable and redeploy.
Error: Insufficient permissions
Solution:
- Verify service account has Editor access to folder
- Check private key doesn't have extra spaces/newlines
- Test locally:
bun test src/lib/storage/google-drive.test.ts
Error: ETIMEDOUT connecting to Redis
Solution:
- Verify Upstash Redis region matches Vercel deployment region
- Check REST URL and token are correct
- Test connection:
curl https://YOUR_REDIS_URL/get/test \ -H "Authorization: Bearer YOUR_TOKEN"
Error: Command "build" exited with 1
Solution:
# Test build locally
bun run build
# Check for TypeScript errors
bun run typecheck
# Check environment variables are set
vercel env lsError: JavaScript heap out of memory
** Solution:** Upgrade Vercel plan or optimize build:
// package.json
{
"scripts": {
"build": "NODE_OPTIONS=--max_old_space_size=4096 astro build"
}
}Turso:
- Database: Free up to 8 GB
- Rows: 500M
- Locations: 1 primary + 2 replicas
Upstash Redis:
- Commands: 10,000/day
- Storage: 256 MB
- Bandwidth: 256 MB/month
Google Drive:
- Storage: 15 GB free
- API calls: Unlimited
Vercel:
- Bandwidth: 100 GB/month
- Deployments: Unlimited
- Edge Functions: 100 GB-hrs
Low traffic (< 1,000 users):
- All free tier ✅
Medium traffic (1,000-10,000 users):
- Turso: $0 (within limits)
- Upstash: $10/month (higher tier)
- Google Drive: $2/month (Google One 100GB)
- Vercel: $20/month (Pro plan) Total: ~$32/month
High traffic (>10,000 users):
- Turso: $29/month (Pro)
- Upstash: $50/month (Pro)
- Google Drive: $10/month (Google Workspace)
- Vercel: $20/month (Pro) Total: ~$109/month
Add to vercel.json:
{
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}Turso handles this automatically with libSQL.
// Cache expensive queries
async function getRepository(id: string) {
const cached = await redis.get(`repo:${id}`);
if (cached) return JSON.parse(cached as string);
const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) });
await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min
return repo;
}- All secrets in environment variables (not committed to Git)
- HTTPS enabled (automatic on Vercel)
- Database access restricted to Vercel IPs
- Google Drive folder shared only with service account
- Redis requires authentication
- Rate limiting enabled
- CORS configured properly
- CSP headers set
- Setup CI/CD: Automatic deployments on push to main
- Add Monitoring: Set up Sentry for error tracking
- Configure Backups: Turso auto-backups + manual exports
- Scale Redis: Upgrade to Global for multi-region
- Add CDN: Use Vercel Edge Network or Cloudflare
- Turso Documentation
- Google Drive API Reference
- Upstash Redis Docs
- Vercel Deployment Docs
- OpenCodeHub GitHub
Need help?
Congratulations! 🎉 Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure!