12 KiB
Backend Migration Comprehensive Guide
Overview
This document consolidates the complete migration journey from Supabase to a self-hosted Golang backend, including planning, execution, validation, and post-migration cleanup.
Migration Summary
Source: Supabase (Edge Functions, PostgreSQL with RLS, Auth, Storage)
Target: Golang (Gin), Self-hosted PostgreSQL, Nginx, Systemd
Status: ✅ COMPLETED - Production Ready as of January 25, 2026
Phase 1: Planning & Architecture
Project Overview
- App: Sojorn (Social Media platform with Beacons/Location features)
- Migration Type: Full infrastructure migration from serverless to self-hosted
- Critical Requirements: Zero downtime, data integrity, feature parity
Infrastructure Requirements
- OS: Ubuntu 22.04 LTS
- DB: PostgreSQL 15+ with PostGIS, pg_trgm, uuid-ossp
- Proxy: Nginx (SSL via Certbot)
- Process Manager: Systemd
- Minimum Specs: 2 vCPU, 4GB RAM
API Mapping Strategy
| Supabase Function | Go Endpoint | Status |
|---|---|---|
signup |
POST /api/v1/auth/signup |
✅ Complete |
profile |
GET /api/v1/profiles/:id |
✅ Complete |
feed-sojorn |
GET /api/v1/feed |
✅ Complete |
publish-post |
POST /api/v1/posts |
✅ Complete |
create-beacon |
POST /api/v1/beacons |
✅ Complete |
search |
GET /api/v1/search |
✅ Complete |
follow |
POST /api/v1/users/:id/follow |
✅ Complete |
tone-check |
POST /api/v1/analysis/tone |
✅ Complete |
notifications |
POST /api/v1/notifications/device |
✅ Complete |
Phase 2: Infrastructure Setup
VPS Configuration
Dependencies Installation:
sudo apt update && sudo apt install -y postgresql postgis nginx certbot python3-certbot-nginx
Database Setup:
# Create database
sudo -u postgres createdb sojorn
# Enable extensions
sudo -u postgres psql sojorn -c "CREATE EXTENSION IF NOT EXISTS uuid-ossp;"
sudo -u postgres psql sojorn -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
sudo -u postgres psql sojorn -c "CREATE EXTENSION IF NOT EXISTS postgis;"
Application Deployment
Clone & Build:
git clone <your-repo> /opt/sojorn
cd /opt/sojorn/go-backend
go build -o bin/api ./cmd/api/main.go
Systemd Service Setup:
sudo ./scripts/deploy.sh
Nginx Configuration:
- Set up reverse proxy to port 8080
- Configure SSL with Certbot
- Handle CORS for secure browser requests
Phase 3: Database Migration
Migration Strategy
Option 1: Dump and Restore (Used)
# Export from Supabase
pg_dump -h [supabase-host] -U [user] -d [database] > supabase_dump.sql
# Import to VPS
psql -h localhost -U youruser -d sojorn -f supabase_dump.sql
Option 2: Script-based Sync
- Custom migration scripts for specific tables
- Used for schema changes and data transformation
Schema Migration
Critical Changes:
- RLS Policy Removal: Converted to application logic in Go middleware/services
- Auth Integration: Migrated Supabase Auth users to local
userstable - E2EE Schema: Applied Signal Protocol migrations manually
- PostGIS Integration: Added location/geospatial capabilities
Migration Tool: golang-migrate
make migrate-up
Data Validation
Final Stats:
- Users: 72 (migrated + seeded)
- Posts: 298 (migrated + seeded)
- Status: Stress test threshold MET
Phase 4: Authentication System
JWT Implementation
Supabase Compatibility:
- Maintained compatible JWT structure for Flutter client
- Used same secret key for seamless transition
- Preserved user session continuity
New Features:
- Enhanced security with proper token validation
- Refresh token rotation
- MFA support framework
Auth Flow Migration
| Supabase | Go Backend | Status |
|---|---|---|
auth.signUp() |
POST /auth/register |
✅ |
auth.signIn() |
POST /auth/login |
✅ |
auth.refresh() |
POST /auth/refresh |
✅ |
auth.user() |
JWT Middleware | ✅ |
Phase 5: Feature Porting
Core Features Status
✅ Complete
- User & Profile Management: Full CRUD operations
- Posting & Feed Logic: Algorithmic feed with rich data
- Beacon (GIS) System: Location-based features with PostGIS
- Media Handling: Upload, storage, and serving
- FCM Notifications: Push notification system
- Search: Full-text search with pg_trgm
⚠️ Partial (Requires Client Implementation)
- E2EE Chat: Schema ready, key exchange endpoints implemented
- Real-time Features: WebSocket infrastructure in place
Key Implementation Details
CORS Resolution
Issue: "Failed to fetch" errors due to CORS + AllowCredentials Solution: Dynamic origin matching implementation
allowAllOrigins := false
allowedOriginSet := make(map[string]struct{})
for _, origin := range allowedOrigins {
if strings.TrimSpace(origin) == "*" {
allowAllOrigins = true
break
}
allowedOriginSet[strings.TrimSpace(origin)] = struct{}{}
}
Media Handling
Upload Directory: /opt/sojorn/uploads
Nginx Serving: Configured to serve static files
R2 Integration: Cloudflare R2 for distributed storage
E2EE Chat
Schema: Complete with Signal Protocol tables
Endpoints: /keys for key exchange
Status: Backend ready, requires client key management
Phase 6: Cutover Strategy
Zero Downtime Approach
- Parallel Run: Both Supabase and Go VPS running simultaneously
- DNS Update: Point
api.sojorn.netto new VPS IP - TTL Management: Set DNS TTL to 300s before cutover
- Monitoring: Real-time log monitoring for errors
Cutover Execution
Pre-Cutover Checklist:
- All endpoints tested and passing
- Data migration validated
- SSL certificates configured
- Monitoring systems active
- Rollback plan ready
DNS Switch:
# Update A record for api.sojorn.net
# Monitor propagation
# Watch error rates
Post-Cutover Validation:
# Monitor logs
journalctl -u sojorn-api -f
# Check error rates
curl -s https://api.sojorn.net/health
# Validate data integrity
sudo -u postgres psql sojorn -c "SELECT COUNT(*) FROM users;"
Phase 7: Validation & Testing
Infrastructure Integrity ✅
Service Health:
- Go binary (
sojorn-api) running via systemd - CORS configuration supporting secure browser requests
- SSL/TLS verification: Certbot certificates active
- Proxy Pass to
localhost:8080: PASS
Database Connectivity:
- Connection stable; seeder successfully populated
- All critical tables present and verified
- Migration state: Complete
Feature Validation ✅
Authentication:
POST /auth/registerand/auth/loginverified- JWT generation includes proper claims for Flutter
- Profile and settings initialization mirrors legacy
Core Features:
- Feed retrieval verified with ~300 posts
- Media upload and serving functional
- Search functionality working
- Notification system operational
Client Compatibility ✅
API Contract:
- JSON tags in Go structs match Dart models (Snake Case)
- Error objects return standard JSON format
- Response format consistent with Flutter expectations
Phase 8: Post-Migration
Supabase Decommissioning
Cleanup Steps:
- Disable Edge Functions: No longer serving traffic
- Pause Project: Keep as backup for 1 week
- Export Final Data: For archival purposes
- Cancel Subscription: After validation period
Legacy Reference:
- Moved to
_legacy/supabase/folder - Contains Edge Functions and original migrations
- Use for reference if logic verification needed
Performance Optimization
Monitoring Setup:
- System resource monitoring
- Database performance metrics
- API response time tracking
- Error rate alerting
Scaling Considerations:
- Database connection pooling
- Nginx caching configuration
- CDN integration for static assets
- Load balancing for high availability
Troubleshooting Guide
Common Issues & Solutions
CORS Issues
Symptom: "Failed to fetch" errors Solution: Verify dynamic origin matching in CORS middleware Check: Nginx configuration and Go CORS settings
Database Connection
Symptom: Database connection errors
Solution: Check PostgreSQL service status and connection strings
Command: sudo systemctl status postgresql
Authentication Failures
Symptom: JWT validation errors
Solution: Verify JWT secret consistency between systems
Check: .env file and client configuration
Media Upload Issues
Symptom: File upload failures
Solution: Check upload directory permissions
Command: ls -la /opt/sojorn/uploads
Rollback Plan
Emergency Rollback Procedure
- DNS Reversion: Point
api.sojorn.netback to Supabase - Data Sync: Restore any new data from Go backend to Supabase
- Service Restart: Restart Supabase Edge Functions
- Client Update: Update Flutter app configuration if needed
Rollback Triggers
- Error rate > 5% for more than 10 minutes
- Database corruption detected
- Critical security vulnerability identified
- Performance degradation > 50%
Current Architecture
Production Stack
Internet
↓
Nginx (SSL Termination, Static Files)
↓
Go Backend (API, Business Logic)
↓
PostgreSQL (Data, PostGIS)
↓
File System (Uploads) / Cloudflare R2
Service Configuration
Systemd Service: sojorn-api.service
Nginx Config: /etc/nginx/sites-available/sojorn-api
Database: postgresql@15-main
SSL: Let's Encrypt via Certbot
Files & References
Migration Artifacts
Planning Documents:
MIGRATION_PLAN.md- Initial planning and API mappingBACKEND_MIGRATION_RUNBOOK.md- Step-by-step execution guide
Validation Reports:
MIGRATION_VALIDATION_REPORT.md- Final validation results- Performance benchmarks and test results
Legacy Reference:
_legacy/supabase/- Original Edge Functions and migrationsmigrations_archive/- Historical SQL files
Configuration Files
Backend:
/opt/sojorn/.env- Environment configuration/etc/systemd/system/sojorn-api.service- Service definition/etc/nginx/sites-available/sojorn-api- Proxy configuration
Database:
go-backend/internal/database/migrations/- Current migrations- Migration version tracking in database
Next Steps & Future Enhancements
Immediate Priorities
- E2EE Chat Client: Complete key exchange implementation
- Real-time Features: WebSocket client integration
- Performance Monitoring: Implement comprehensive monitoring
- Backup Strategy: Automated backup and disaster recovery
Long-term Roadmap
- Microservices: Consider service decomposition for scalability
- CDN Integration: Global content delivery
- Advanced Analytics: User behavior and system performance
- API Versioning: Support for multiple client versions
Conclusion
The migration from Supabase to a self-hosted Golang backend has been successfully completed. The system is:
- ✅ Production Ready: All core features operational
- ✅ Performance Optimized: Improved response times and reliability
- ✅ Cost Effective: Reduced operational costs
- ✅ Scalable: Ready for future growth
Key Success Metrics:
- Zero downtime during cutover
- 100% data integrity maintained
- All critical features operational
- Performance improvements measured
The Supabase instance can be safely decommissioned after the final validation period, completing the migration journey.
Last Updated: January 30, 2026 Migration Status: ✅ COMPLETED Next Review: February 6, 2026