How to Self-Host Supabase on a VPS: Open-Source Firebase Alternative with PostgreSQL

How to Self-Host Supabase on a VPS: Open-Source Firebase Alternative with PostgreSQL

Supabase is the open-source Firebase alternative built on PostgreSQL. Supabase Cloud costs $25/month for the Pro plan. Self-hosting on a VPS delivers the full feature set — PostgreSQL with Row Level Security, auto-generated REST and GraphQL APIs via PostgREST, realtime subscriptions, authentication via GoTrue, and file storage — on infrastructure you control.

Supabase vs Pocketbase: When to Choose Each

  • Choose Supabase: PostgreSQL is your database; you need SQL joins, pgvector for AI apps, PostGIS for geo; your team knows SQL; complex relational data models
  • Choose Pocketbase: Simpler schema; want a single binary with no Docker; SQLite is sufficient; smaller project; minimal setup overhead

Server Requirements

  • Minimum 4 GB RAM (Supabase runs ~12 Docker services)
  • Docker and Docker Compose
  • 40+ GB NVMe storage
  • Ubuntu 22.04 or 24.04

Step 1: Clone Supabase Self-Hosted Config

git clone --depth 1 https://github.com/supabase/supabase /opt/supabase
cd /opt/supabase/docker

Step 2: Configure Environment Variables

cp .env.example .env
nano .env
# CRITICAL: Change ALL of these before starting

# Database password
POSTGRES_PASSWORD=your_very_strong_postgres_password

# JWT secret (generate: openssl rand -base64 32)
JWT_SECRET=your_super_secret_jwt_token_at_least_32_chars

# JWT tokens (generate at: https://supabase.com/docs/guides/self-hosting/docker#generate-api-keys)
ANON_KEY=your_anon_key_here
SERVICE_ROLE_KEY=your_service_role_key_here

# Dashboard credentials
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=your_secure_dashboard_password

# Your domain
SITE_URL=https://supabase.yourdomain.com
API_EXTERNAL_URL=https://supabase.yourdomain.com

Generate JWT keys with Python:

pip install pyjwt
python3 -c "
import jwt, time
secret = 'your_jwt_secret_here'
anon = jwt.encode({'role':'anon','iss':'supabase','iat':int(time.time()),'exp':int(time.time())+315360000}, secret, algorithm='HS256')
service = jwt.encode({'role':'service_role','iss':'supabase','iat':int(time.time()),'exp':int(time.time())+315360000}, secret, algorithm='HS256')
print('ANON_KEY:', anon)
print('SERVICE_ROLE_KEY:', service)
"

Step 3: Start Supabase

cd /opt/supabase/docker
docker compose up -d

# Monitor startup — takes 2–5 minutes for all services
docker compose ps
docker compose logs -f supabase-db supabase-rest

# Wait until all containers show "healthy" or "Up"

Supabase runs ~12 services: PostgreSQL, PostgREST (REST API), GoTrue (auth), Realtime, Storage API, Studio (dashboard), Kong (API gateway), Imgproxy, and supporting services.

Step 4: Nginx Reverse Proxy

sudo nano /etc/nginx/sites-available/supabase
server {
    listen 80;
    server_name supabase.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name supabase.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/supabase.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/supabase.yourdomain.com/privkey.pem;

    client_max_body_size 500M;

    location / {
        proxy_pass http://127.0.0.1:8000;   # Kong API gateway
        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_read_timeout 3600s;
        proxy_buffering off;
    }
}
sudo ln -s /etc/nginx/sites-available/supabase /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d supabase.yourdomain.com

Using Supabase from JavaScript

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://supabase.yourdomain.com',
  'your_anon_key'       // ANON_KEY from .env
)

// Query data (respects Row Level Security automatically)
const { data, error } = await supabase
  .from('posts')
  .select('*, author:users(name, avatar)')
  .eq('published', true)
  .order('created_at', { ascending: false })
  .limit(10)

// Authentication
const { data: authData } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123'
})

// Realtime subscription
const channel = supabase
  .channel('messages')
  .on('postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages' },
    (payload) => console.log('New message:', payload.new)
  )
  .subscribe()

// File storage
const { data: file } = await supabase.storage
  .from('avatars')
  .upload('user-123/avatar.png', avatarFile)

Row Level Security (RLS) — The Key Feature

-- In Supabase SQL editor or psql:

-- Enable RLS on a table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Users can read published posts or their own drafts
CREATE POLICY "Read published posts" ON posts
  FOR SELECT USING (published = true OR author_id = auth.uid());

-- Users can only insert their own posts
CREATE POLICY "Insert own posts" ON posts
  FOR INSERT WITH CHECK (author_id = auth.uid());

-- Users can only update their own posts
CREATE POLICY "Update own posts" ON posts
  FOR UPDATE USING (author_id = auth.uid());

Updates and Maintenance

cd /opt/supabase/docker
git pull                   # Get latest config
docker compose pull        # Pull new images
docker compose up -d       # Restart with new images

Getting Started

Supabase’s 12-service Docker stack requires 4 GB RAM minimum. KVM VPS plans at VPS.DO with 4–8 GB RAM run the full Supabase stack comfortably. NVMe storage ensures PostgreSQL query performance and realtime event throughput remain fast under load.

Conclusion

Self-hosted Supabase delivers the complete platform — PostgreSQL with RLS, auto-generated REST and GraphQL APIs, realtime, auth, and storage — at the cost of a VPS plan rather than $25+/month cloud subscription. The trade-off is operational responsibility for the 12-service Docker stack. For applications needing data sovereignty, avoiding per-project billing, or requiring PostgreSQL features unavailable in managed BaaS platforms, self-hosting is compelling.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!