The UBSC Chapter Website can be deployed to multiple platforms. This guide covers deployment to Fly.io (recommended) and Vercel (alternative).
- All environment variables configured
- Database migrations applied
- Initial data seeded (roles, admin user)
- Build succeeds locally (
npm run build) - Tests pass (when test infrastructure exists)
- Security review completed
- JWT secrets changed from defaults
- Email configuration verified
- Domain/DNS configured
# Build Identity
IS_DEVELOPMENT=false
IS_STAGING=false # or true for staging
IS_PRODUCTION=true
DEBUG=false
# Database
DATABASE_URL="postgresql://..." # Pooled connection
DATABASE_URL_PROD="postgresql://..." # Direct connection (non-pooled)
# Security - MUST CHANGE THESE
JWT_SECRET="your-production-jwt-secret-here-use-strong-random-string"
ACT_JWT_SECRET="your-production-activation-secret-here-use-strong-random-string"
# Session Configuration
SESSION_SAMESITE=lax
SESSION_HTTP_ONLY=true
SESSION_SECURE=true # MUST be true for HTTPS
SESSION_MAX_AGE=259200 # 72 hours
# Platform URLs
PLATFORM_URL="https://ubsc-chapter.ub.edu.bz"
PLATFORM_URL_DEVELOPMENT="http://localhost:5173"
# Email (Production credentials)
MAIL_DISPLAYNAME="UBSC Chapter"
MAIL_USERNAME="noreply@ubsc-chapter.ub.edu.bz"
MAIL_PASSWORD="your-email-password"
MAIL_SIGNATURE="UBSC Chapter IT Team"
PUBLIC_SUPPORT_EMAIL="support@ubsc-chapter.ub.edu.bz"# Generate JWT secrets
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"Fly.io is configured as the primary deployment platform with full Docker support.
-
Install Fly CLI:
curl -L https://fly.io/install.sh | sh -
Sign up/Login:
fly auth signup # or fly auth login
-
Configure Fly.io:
The
fly.tomlfile is already configured:app = 'talentpool-six' primary_region = 'iad' [http_service] internal_port = 3000 force_https = true auto_stop_machines = 'stop' auto_start_machines = true min_machines_running = 0
-
Create App (if not exists):
fly apps create ubsc-chapter
-
Set Secrets:
fly secrets set JWT_SECRET="your-secret-here" fly secrets set ACT_JWT_SECRET="your-activation-secret-here" fly secrets set DATABASE_URL="postgresql://..." fly secrets set DATABASE_URL_PROD="postgresql://..." fly secrets set MAIL_USERNAME="your-email" fly secrets set MAIL_PASSWORD="your-password" fly secrets set IS_PRODUCTION=true fly secrets set IS_DEVELOPMENT=false fly secrets set SESSION_SECURE=true
-
Deploy:
fly deploy
Option 1: Use Neon (Recommended)
- Create Neon database
- Set connection strings as secrets
- Apply migrations
Option 2: Fly Postgres
fly postgres create
fly postgres attach <postgres-app-name># SSH into Fly.io machine
fly ssh console
# Run migrations
cd /app
node -e "require('dotenv').config(); require('drizzle-kit').migrate();"Or run migrations from local machine:
DATABASE_URL="your-production-db-url" npm run db:migrate-
Add Certificate:
fly certs create ubsc-chapter.ub.edu.bz
-
Configure DNS: Add CNAME record:
ubsc-chapter.ub.edu.bz → [your-app].fly.dev -
Verify:
fly certs check ubsc-chapter.ub.edu.bz
# View logs
fly logs
# Check status
fly status
# View metrics
fly dashboard# Scale machines
fly scale count 2
# Change machine size
fly scale vm shared-cpu-2x
# Set autoscaling
fly autoscale set min=1 max=3Vercel is configured as an alternative deployment option, primarily for development.
-
Install Vercel CLI:
npm install -g vercel
-
Login:
vercel login
-
Link Project:
vercel link
-
Configure Environment Variables:
- Go to Vercel Dashboard → Project Settings → Environment Variables
- Add all required environment variables
- Or use CLI:
vercel env add JWT_SECRET vercel env add DATABASE_URL # ... etc
-
Deploy:
vercel --prod
Use Neon or Vercel Postgres:
-
Neon (Recommended):
- Create Neon database
- Configure connection strings in Vercel environment variables
-
Vercel Postgres:
- Create Vercel Postgres database
- Link to project
- Connection strings automatically added to environment
- Uses
@sveltejs/adapter-vercelwhenIS_DEVELOPMENT=true - Serverless functions have limitations (execution time, memory)
- May require different configuration for file uploads
- Built-in edge network for global performance
Advantages:
- Serverless PostgreSQL
- Automatic scaling
- Connection pooling (PgBouncer)
- Free tier available
- Excellent SvelteKit integration
Setup:
- Create account at neon.tech
- Create new project
- Copy connection strings (pooled and direct)
- Use pooled connection for app, direct for migrations
- Supabase: PostgreSQL with additional services
- Railway: PostgreSQL with simple deployment
- DigitalOcean Managed Database: Traditional managed PostgreSQL
- AWS RDS: Enterprise-grade PostgreSQL
Create .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Type check
run: npm run check
- name: Build
run: npm run build
- name: Deploy to Fly.io
uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}- Go to Repository Settings → Secrets and Variables → Actions
- Add required secrets:
FLY_API_TOKEN(get fromfly auth token)- Other environment-specific secrets
SSL is automatic with force_https = true in fly.toml.
SSL is automatic for all deployments.
If using custom reverse proxy:
- Use Let's Encrypt for free SSL certificates
- Configure certbot for automatic renewal
- Ensure proper redirect from HTTP to HTTPS
-
Ensure production build:
NODE_ENV=production npm run build
-
Check bundle size:
npm run build -- --analyze
-
Database Connection Pooling:
- Use pooled connection (PgBouncer on Neon)
- Configure appropriate pool size
-
Caching:
- Add Redis for session caching (future enhancement)
- Use HTTP caching headers
- Enable CDN for static assets
-
Image Optimization:
- Use WebP/AVIF formats
- Implement lazy loading
- Use responsive images
Neon:
- Automatic daily backups
- Point-in-time recovery available
- Download backups from dashboard
Manual Backup:
pg_dump $DATABASE_URL_PROD > backup-$(date +%Y%m%d).sqlAutomated Backup Script:
#!/bin/bash
DATE=$(date +%Y%m%d)
pg_dump $DATABASE_URL_PROD | gzip > backups/backup-$DATE.sql.gz
# Upload to S3, Google Cloud Storage, etc.If using local file storage:
- Regular backups of upload directory
- Consider S3 or cloud storage for production
Recommended Services:
- Sentry: Error tracking
- LogRocket: Session replay
- Datadog: Full-stack monitoring
Setup Example (Sentry):
npm install @sentry/sveltekitConfigure in hooks.server.ts:
import * as Sentry from '@sentry/sveltekit';
Sentry.init({
dsn: 'your-sentry-dsn',
environment: IS_PRODUCTION ? 'production' : 'development'
});Fly.io:
# Live logs
fly logs
# Export logs
fly logs > app-logs.txtStructured Logging: Consider adding a logging library:
npm install pinoAdd health check endpoint: src/routes/health/+server.ts
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
export const GET: RequestHandler = async () => {
try {
// Test database connection
await db.execute(sql`SELECT 1`);
return json({
status: 'healthy',
database: 'connected',
timestamp: new Date().toISOString()
});
} catch (error) {
return json({
status: 'unhealthy',
database: 'disconnected',
error: error.message
}, { status: 503 });
}
};# List releases
fly releases
# Rollback to previous release
fly deploy --image [previous-image-id]- Go to Vercel Dashboard
- Select deployment
- Click "Promote to Production"
# Restore from backup
psql $DATABASE_URL_PROD < backup-20240101.sql
# Or use Neon's point-in-time recovery- Change all default credentials
- Use environment variables for secrets (never commit)
- Enable HTTPS only (
SESSION_SECURE=true) - Configure CSP headers
- Set up rate limiting
- Review CORS settings
- Audit dependencies for vulnerabilities
- Enable database SSL connections
- Configure firewall rules
- Set up monitoring and alerts
- Review and test error handling
- Validate all user inputs
Add to hooks.server.ts:
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');-
Verify Deployment:
- Check application loads
- Test authentication flow
- Verify database connectivity
- Test critical user flows
-
Seed Production Data:
# Run from local with production DATABASE_URL DATABASE_URL=$PROD_DB_URL npm run db:seed
-
Create Admin Account:
- Login with seeded admin
- Change default password
- Create additional admin accounts if needed
-
Configure Email:
- Test email sending
- Verify SMTP settings
- Check spam folder
-
Monitor Logs:
- Watch for errors
- Check performance metrics
- Verify no security issues
See TROUBLESHOOTING.md for common deployment issues and solutions.
-
Weekly:
- Review logs for errors
- Check performance metrics
- Monitor database size
-
Monthly:
- Update dependencies
- Review security advisories
- Test backup restoration
- Clean up old data
-
Quarterly:
- Security audit
- Performance review
- Cost optimization review
- Use
auto_stop_machinesfor low-traffic periods - Optimize machine size based on actual usage
- Use shared CPU for development/staging
- Archive old data
- Optimize queries
- Use appropriate connection pooling
- Monitor storage usage
- Fly.io Docs: https://fly.io/docs
- Vercel Docs: https://vercel.com/docs
- SvelteKit Deployment: https://kit.svelte.dev/docs/adapters
- Neon Docs: https://neon.tech/docs