Supabase Self-Hosted Stack Documentation¶
Table of Contents¶
- Overview
- Architecture
- Why Supabase
- Configuration Details
- Network Architecture
- Bootstrap and Initialization
- JWT Keys and Authentication
- Backup Strategy
- Common Operations
- Troubleshooting Guide
- Comparison with Hosted Supabase
- When to Use Supabase vs Pocketbase
Overview¶
Supabase is an open-source Backend-as-a-Service (BaaS) platform that provides a complete backend infrastructure for modern applications. It positions itself as an open-source alternative to Firebase, built on top of enterprise-grade open-source tools, primarily PostgreSQL.
In this infrastructure, Supabase is deployed as a self-hosted solution on Docker, providing: - PostgreSQL database with advanced extensions - Authentication service (GoTrue) - Real-time subscriptions (via WebSocket and PostgreSQL's logical replication) - Auto-generated REST APIs (PostgREST) - Object storage (S3-compatible) - Database management UI (Supabase Studio) - Edge Functions (Deno runtime for serverless functions) - Image transformations (Imgproxy for on-the-fly image optimization) - Log analytics (Logflare/Analytics for centralized logging) - Log aggregation (Vector for log collection)
The stack is exposed via the domain supabase.rbnk.uk with SSL termination handled by Traefik reverse proxy.
Architecture¶
The Supabase stack consists of 11 interconnected containers, each serving a specific purpose:
1. supa-db (PostgreSQL Database)¶
- Image:
supabase/postgres:15.8.1.085 - Role: Core database with Supabase-specific extensions and configurations
- Internal Port: 5432 (not exposed externally)
- Features:
- PostgreSQL 15.8 with extensions: pgvector, pg_stat_statements, pgaudit, pgjwt, pgsodium, pg_graphql
- Custom schemas: auth, storage, realtime, extensions
- Row Level Security (RLS) enabled by default
- Logical replication for real-time features
2. supa-auth (Authentication Service)¶
- Image:
supabase/gotrue:v2.183.0 - Role: Handles user authentication, registration, and session management
- Port: 8081 (internal), exposed as
/auth/v1/* - Features:
- JWT-based authentication
- OAuth provider integrations
- Email/password authentication
- Magic link authentication
- Multi-factor authentication support
3. supa-rest (REST API)¶
- Image:
postgrest/postgrest:v13.0.7 - Role: Auto-generates RESTful APIs from PostgreSQL schemas
- Port: 3000 (internal), exposed as
/rest/v1/* - Features:
- Instant CRUD APIs from database tables
- Respects PostgreSQL's Row Level Security
- Support for stored procedures as RPC endpoints
- GraphQL-like filtering and embedding
4. supa-storage (Object Storage)¶
- Image:
supabase/storage-api:v1.32.0 - Role: S3-compatible object storage for files and media
- Port: 5000 (internal), exposed as
/storage/v1/* - Features:
- File upload/download with access control
- Image transformations on-the-fly
- Integration with PostgreSQL for metadata
- Bucket-based organization
5. supa-studio (Management UI)¶
- Image:
supabase/studio:2025.11.26-sha-8f096b5 - Role: Web-based database management interface
- Port: 3000 (internal), exposed as root path
- Features:
- Visual table editor
- SQL query runner
- User management
- API documentation
- Real-time logs viewer
6. supa-meta (Database Introspection)¶
- Image:
supabase/postgres-meta:v0.93.1 - Role: Provides database metadata and introspection APIs
- Port: 8080 (internal only)
- Features:
- Schema introspection
- Table relationships mapping
- Database statistics
- Used by Studio for UI generation
7. supa-realtime (Real-time WebSocket Service)¶
- Image:
supabase/realtime:v2.65.3 - Role: Enables real-time subscriptions for database changes
- Port: 4000 (internal), exposed as
/realtime/v1/* - Features:
- WebSocket-based real-time subscriptions
- Postgres CDC (Change Data Capture) via logical replication
- Channel-based pub/sub messaging
- Row Level Security enforcement
- Presence tracking for user activity
8. supa-analytics (Log Analytics - Logflare)¶
- Image:
supabase/logflare:1.26.13 - Role: Centralized log analytics and monitoring
- Port: 4000 (internal), exposed as
/analytics/v1/* - Features:
- Real-time log ingestion and querying
- Structured log storage in PostgreSQL
- Integration with Vector for log collection
- Multi-tenant log isolation
- Query interface for log analysis
9. supa-vector (Log Aggregation)¶
- Image:
timberio/vector:0.28.1-alpine - Role: Collects and routes logs from all Supabase containers
- Port: 9001 (health check only)
- Features:
- Docker log collection from all supa-* containers
- Log filtering and transformation
- Batched delivery to Logflare
- Low-latency, high-throughput processing
10. supa-functions (Edge Functions)¶
- Image:
supabase/edge-runtime:v1.69.25 - Role: Serverless function execution with Deno runtime
- Port: 9000 (internal), exposed as
/functions/v1/* - Features:
- Deno-based serverless functions
- TypeScript/JavaScript support
- Direct database access via Supabase client
- JWT authentication integration
- Hot reloading for development
11. supa-imgproxy (Image Transformations)¶
- Image:
darthsim/imgproxy:v3.8.0 - Role: On-the-fly image resizing and optimization
- Port: 5001 (internal)
- Features:
- Dynamic image resizing and cropping
- Format conversion (WebP, AVIF, etc.)
- ETag support for caching
- Integration with storage service
- Memory-efficient processing
Why Supabase¶
Supabase was chosen for this infrastructure for several compelling reasons:
1. PostgreSQL Foundation¶
- Enterprise-grade database with 30+ years of development
- ACID compliance and strong consistency guarantees
- Advanced features: JSON, full-text search, geospatial data
- Extensibility through custom functions and extensions
2. Comprehensive Feature Set¶
- Authentication, database, storage, and real-time in one platform
- Reduces the need for multiple services and complex integrations
- Consistent security model across all components
3. Developer Experience¶
- Auto-generated APIs eliminate boilerplate code
- Type-safe client libraries for popular frameworks
- Row Level Security provides fine-grained access control
- Real-time subscriptions out of the box
4. Open Source¶
- No vendor lock-in
- Self-hostable with full control over data
- Active community and transparent development
- Can contribute fixes or features if needed
5. Production Ready¶
- Used by thousands of production applications
- Regular security updates and patches
- Comprehensive documentation and examples
- Enterprise support available if needed
Configuration Details¶
PostgreSQL Setup¶
The PostgreSQL instance is configured with:
services:
db:
image: ghcr.io/supabase/postgres:15.8.1.060
volumes:
- ./data/db:/var/lib/postgresql/data
- ./bootstrap-sql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:ro
Key PostgreSQL Extensions:
- uuid-ossp: UUID generation
- pgcrypto: Cryptographic functions
- pgjwt: JWT generation/validation
- pg_stat_statements: Query performance monitoring
- pgaudit: Audit logging
- pgsodium: Encryption library
- pg_graphql: GraphQL support
- pgvector: Vector similarity search
Auth Service (GoTrue) Configuration¶
environment:
GOTRUE_SITE_URL: https://supabase.rbnk.uk
GOTRUE_DB_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
GOTRUE_DB_NAMESPACE: auth
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_MAILER_AUTOCONFIRM: true # Dev setting
The auth service manages: - User registration and login - JWT token generation and validation - Password resets and email verification - OAuth provider integrations
PostgREST API Setup¶
environment:
PGRST_DB_URI: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
PGRST_DB_ANON_ROLE: anon
PGRST_DB_SCHEMAS: public
PGRST_JWT_SECRET: ${JWT_SECRET}
PostgREST configuration:
- Exposes public schema by default
- Uses anon role for unauthenticated requests
- Automatically switches to authenticated role for logged-in users
- Respects Row Level Security policies
Storage Configuration¶
environment:
STORAGE_BACKEND: file
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
STORAGE_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
Storage setup: - File-based backend (can be switched to S3) - Metadata stored in PostgreSQL - Access control via RLS policies - Automatic image optimization
Studio Access¶
Studio is configured as the default route:
This provides easy access to the management interface at the root domain.
Network Architecture¶
The Supabase stack implements a dual-network architecture for enhanced security:
1. Internal Network (supabase_internal)¶
- Bridge network for inter-service communication
- Database is only accessible on this network
- Services communicate using container names as hostnames
- Isolated from external access
2. External Network (traefik_proxy)¶
- Shared with Traefik reverse proxy
- Only services that need external access join this network
- SSL termination happens at Traefik level
- Provides unified access point
Security Benefits: - Database never exposed to external network - Service-to-service communication stays internal - Single entry point through Traefik - Easy to implement firewall rules
Service Network Membership:
- supa-db: internal only (maximum security)
- supa-auth: internal only (accessed via Kong)
- supa-rest: internal only (accessed via Kong)
- supa-storage: internal only (accessed via Kong)
- supa-studio: internal only (accessed via Kong)
- supa-meta: internal only
- supa-realtime: internal only (accessed via Kong)
- supa-functions: internal only (accessed via Kong)
- supa-analytics: internal only (accessed via Kong)
- supa-vector: internal only (log collection)
- supa-imgproxy: internal only (used by storage)
- supa-kong: internal + traefik_proxy (API gateway)
Bootstrap and Initialization¶
The PostgreSQL database is initialized with a comprehensive set of bootstrap scripts located in /docker-entrypoint-initdb.d/:
Initialization Order¶
000-create-supabase-admin.sh: Creates initial admin user00-schema.sql: PgBouncer setup00000000000000-initial-schema.sql: Core Supabase schemas00000000000001-auth-schema.sql: Authentication tables00000000000002-storage-schema.sql: Storage tables00000000000003-post-setup.sql: Final configurationsmigrations/*.sql: Incremental updates (sorted by timestamp)
Key Bootstrap Components¶
Roles Created:
- postgres: Superuser (existing)
- supabase_admin: Administrative operations
- supabase_auth_admin: Auth service operations
- supabase_storage_admin: Storage service operations
- anon: Anonymous/public access
- authenticated: Logged-in users
- service_role: Backend service access
- authenticator: PostgREST connection role
Schemas Created:
- auth: Authentication tables and functions
- storage: File storage metadata
- realtime: Real-time subscription management
- extensions: PostgreSQL extensions
- public: Application tables (user-defined)
Critical Tables:
- auth.users: User accounts
- auth.refresh_tokens: Session management
- storage.buckets: Storage containers
- storage.objects: File metadata
Initialization Process¶
- Container checks if
data/dbis empty - If empty, runs
initdbto create cluster - Executes all scripts in
/docker-entrypoint-initdb.d/ - Scripts run in lexicographical order
- Each script is executed as superuser
- Process is idempotent (can be re-run safely)
JWT Keys and Authentication¶
Supabase uses JWT (JSON Web Tokens) for authentication across all services:
Key Types¶
anonkey: Public key for unauthenticated access- Used in client-side applications
- Has minimal permissions (defined by RLS)
-
Safe to expose in frontend code
-
service_rolekey: Private key for backend services - Bypasses Row Level Security
- Full database access
- Must be kept secret
JWT Configuration¶
# Shared secret for signing/verifying JWTs
JWT_SECRET=your-32-byte-secret-key
# Keys are JWTs signed with the secret containing role claims
ANON_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
SERVICE_ROLE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Authentication Flow¶
- User Login:
- Client sends credentials to
/auth/v1/token - GoTrue validates and returns access token
-
Token contains user ID and role
-
API Request:
- Client includes token in Authorization header
- PostgREST validates token signature
- Database session uses token's role
-
RLS policies enforce access control
-
Token Refresh:
- Access tokens expire (default: 1 hour)
- Refresh token used to get new access token
- Maintains user session without re-login
Security Considerations¶
- JWT secret must be at least 32 characters
- Rotate keys periodically in production
- Use HTTPS for all API calls
- Implement token refresh logic in clients
- Monitor for suspicious authentication patterns
Backup Strategy¶
The backup system is automated via systemd timer (3 AM daily) with the following components:
Backup Script (/srv/dockerdata/_scripts/supabase-backup.sh)¶
What's Backed Up:
1. Database: Complete logical dump via pg_dumpall
- All databases, schemas, and data
- Roles and permissions
- Compressed with gzip -9
- Configuration:
docker-compose.ymlfiles- Environment files (
.env) - Bootstrap SQL scripts
- Traefik configuration
Backup Location: /srv/backups/supabase/
- db/: Database dumps
- config/: Configuration archives
Retention: 14 days (configurable)
Backup Process¶
# Daily automated backup
sudo systemctl status supabase-backup.timer
# Manual backup
sudo /srv/dockerdata/_scripts/supabase-backup.sh
Restore Process¶
- Stop services:
docker compose down - Restore database:
- Restore config: Extract config archive to appropriate locations
- Start services:
docker compose up -d
Backup Best Practices¶
- Test restore procedure regularly
- Store backups on different storage/server
- Monitor backup job success/failure
- Consider point-in-time recovery for production
- Encrypt backups containing sensitive data
Common Operations¶
Database Access¶
# Direct PostgreSQL access
docker exec -it supa-db psql -U postgres
# Common database commands
\l # List databases
\c postgres # Connect to database
\dn # List schemas
\dt public.* # List tables in public schema
\du # List roles
User Management¶
-- View all users
SELECT * FROM auth.users;
-- Create admin user (via SQL)
INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, role)
VALUES (
gen_random_uuid(),
'[email protected]',
crypt('password123', gen_salt('bf')),
now(),
'authenticated'
);
-- Grant admin privileges
UPDATE auth.users
SET raw_app_meta_data = '{"role": "admin"}'::jsonb
WHERE email = '[email protected]';
API Usage¶
# Test anonymous access
curl https://supabase.rbnk.uk/rest/v1/
# Query with anon key
curl https://supabase.rbnk.uk/rest/v1/your_table \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"
# Insert with service role key (bypasses RLS)
curl -X POST https://supabase.rbnk.uk/rest/v1/your_table \
-H "apikey: YOUR_SERVICE_KEY" \
-H "Authorization: Bearer YOUR_SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{"column": "value"}'
Service Management¶
# View service status
docker compose ps
# Restart specific service
docker compose restart supa-auth
# View logs
docker compose logs -f supa-storage
# Scale services (if stateless)
docker compose up -d --scale supa-rest=2
Database Migrations¶
-- Create migration
CREATE TABLE IF NOT EXISTS public.migrations (
id serial PRIMARY KEY,
name text NOT NULL,
executed_at timestamptz DEFAULT now()
);
-- Apply migration
BEGIN;
-- Your schema changes here
INSERT INTO migrations (name) VALUES ('add_user_profiles');
COMMIT;
Troubleshooting Guide¶
Common Issues¶
1. Auth Schema Does Not Exist¶
Symptoms: Services fail with "schema auth does not exist" Cause: Bootstrap SQL didn't run during initialization Fix:
# Ensure bootstrap scripts are mounted
docker compose down
sudo rm -rf data/db
docker compose up -d db
# Check logs for script execution
docker logs supa-db | grep "running /docker-entrypoint-initdb.d"
2. Role 'anon' Does Not Exist¶
Symptoms: PostgREST fails to start Cause: Database roles not created Fix: Re-run initialization or manually create roles
3. Storage Health Check Failing¶
Symptoms: Traefik shows storage as unhealthy Cause: Health endpoint path mismatch Fix: Already configured with custom health check rewrite in Traefik
4. JWT Signature Invalid¶
Symptoms: 401 errors on API calls Cause: Mismatched JWT secrets between services Fix: Ensure all services use same JWT_SECRET value
5. Cannot Connect to Database¶
Symptoms: Services can't reach PostgreSQL
Cause: Network configuration or credentials
Fix:
- Verify service is on internal network
- Check connection string uses db as hostname
- Verify PostgreSQL is running
Debug Commands¶
# Check network connectivity
docker compose exec supa-auth ping db
# Test database connection
docker compose exec supa-auth psql $GOTRUE_DATABASE_URL -c "SELECT 1"
# Verify JWT secret
docker compose exec supa-rest env | grep JWT_SECRET
# Check file permissions
docker compose exec db ls -la /var/lib/postgresql/data
# Inspect bootstrap scripts
docker compose exec db ls -la /docker-entrypoint-initdb.d/
Log Analysis¶
# Check all logs
docker compose logs
# Service-specific logs with timestamps
docker compose logs -t supa-auth
# Follow logs in real-time
docker compose logs -f
# Search for errors
docker compose logs | grep -i error
Comparison with Hosted Supabase¶
Self-Hosted Advantages¶
- Data Sovereignty: Complete control over data location
- Customization: Modify any component or configuration
- Cost Predictability: No per-request pricing, only infrastructure costs
- Network Control: Can run in air-gapped environments
- Compliance: Easier to meet specific regulatory requirements
Self-Hosted Disadvantages¶
- Maintenance Burden: You handle updates, backups, monitoring
- No Automatic Scaling: Manual intervention for scaling
- Missing Features: Some cloud-only features (edge functions, vector embeddings)
- Security Responsibility: You manage all security patches
- No Global CDN: Must implement your own edge caching
Feature Comparison¶
| Feature | Hosted | Self-Hosted |
|---|---|---|
| Automatic backups | ✅ | ❌ (manual setup) |
| Global CDN | ✅ | ❌ |
| Edge Functions | ✅ | ✅ (Deno runtime) |
| Real-time subscriptions | ✅ | ✅ |
| Log analytics | ✅ | ✅ (Logflare) |
| Image transformations | ✅ | ✅ (Imgproxy) |
| Automatic updates | ✅ | ❌ |
| Custom domains | ✅ | ✅ |
| Full SQL access | ✅ | ✅ |
| Custom extensions | ❌ | ✅ |
| Air-gapped deploy | ❌ | ✅ |
When to Use Supabase vs Pocketbase¶
Choose Supabase When:¶
- PostgreSQL Required:
- Need advanced SQL features
- Complex queries and joins
- Existing PostgreSQL expertise
-
Need specific PostgreSQL extensions
-
Scale Matters:
- Expecting millions of records
- High concurrent users
- Complex access patterns
-
Need read replicas
-
Feature Requirements:
- Real-time subscriptions
- Row Level Security
- Built-in auth with OAuth
-
File storage integration
-
Team Structure:
- Multiple developers
- Dedicated backend team
- Need role-based access
Choose Pocketbase When:¶
- Simplicity First:
- Single binary deployment
- Minimal configuration
- Quick prototypes
-
Learning projects
-
Resource Constraints:
- Limited RAM (< 1GB)
- Shared hosting
- Edge devices
-
Embedded scenarios
-
Use Case:
- Admin panels
- Internal tools
- Simple CRUD apps
-
Single-user applications
-
Development Speed:
- Need working backend in minutes
- No infrastructure expertise
- Minimal dependencies
Comparison Matrix¶
| Aspect | Supabase | Pocketbase |
|---|---|---|
| Database | PostgreSQL | SQLite |
| Deployment | 11+ containers | Single binary |
| Memory usage | 4GB+ | 10-50MB |
| Real-time | Native WebSocket | WebSocket (basic) |
| Auth providers | 20+ | 5+ |
| File storage | Integrated + Imgproxy | Integrated |
| Admin UI | Full Studio | Basic |
| API | REST + GraphQL | REST only |
| Edge Functions | Deno runtime | None |
| Log Analytics | Logflare | None |
| Migrations | SQL based | Go based |
| Extensions | 50+ available | None |
| Horizontal scale | Yes | No |
| Backup | pg_dump | File copy |
Migration Path¶
Pocketbase → Supabase: - Export data as JSON/CSV - Create PostgreSQL schema - Import data with transformations - Update client to use Supabase SDK
Supabase → Pocketbase: - Generally not recommended due to feature loss - Possible for simple schemas - Requires significant refactoring
Conclusion¶
The self-hosted Supabase stack provides a production-ready, open-source backend infrastructure with the power of PostgreSQL at its core. The architecture's separation of concerns, comprehensive security model, and extensive feature set make it suitable for applications ranging from startups to enterprise deployments.
Key takeaways: - Robust 11-container architecture with clear separation of concerns - Kong API gateway provides unified routing and authentication - Dual-network design enhances security - JWT-based authentication provides flexible access control - Edge Functions enable serverless workloads with Deno runtime - Real-time subscriptions via WebSocket for live database changes - Centralized log analytics with Logflare and Vector - Automated backup strategy ensures data safety - Comprehensive bootstrap process handles complex initialization - Choose based on your specific needs: Supabase for feature-rich applications, Pocketbase for simplicity
For production deployments, ensure proper secret rotation, monitoring, and backup testing. The self-hosted nature provides ultimate control but requires operational expertise to maintain effectively.