Skip to content

Complete guide to self-host Supabase on Oracle Cloud free tier with Docker, Nginx, SSL, and automatic updates. Includes auto-start after reboots and optimized backup system.

Notifications You must be signed in to change notification settings

JimPresting/supabase-oracle-cloud-docker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 

Repository files navigation

Self-Hosting Supabase on Oracle Cloud with Docker

A complete guide to set up your own Supabase instance on Oracle Cloud using Docker, Nginx, SSL certificates, and automatic updates.

πŸ“‹ Prerequisites

  • Oracle Cloud account (free tier works)
  • Domain name with DNS management access
  • SSH access to your Oracle Cloud VM
  • Basic command line knowledge

πŸš€ Step 1: Oracle Cloud VM Setup

1.1 Create VM Instance

  1. Login to Oracle Cloud Console
  2. Create a new VM instance:
    • Shape: VM.Standard.E2.1.Micro (Always Free)
    • Image: Ubuntu 22.04 LTS
    • SSH Keys: Generate and download your key pair
    • Networking: Allow HTTP (80) and HTTPS (443) traffic

1.2 Reserve Static IP

  1. Go to Networking β†’ Virtual Cloud Networks β†’ Public IPs
  2. Click on your VM's external IP β†’ Reserve Static IP
  3. Note down your static IP address (e.g., 123.456.78.90)

1.3 Configure Security Rules

  1. Networking β†’ Virtual Cloud Networks β†’ Security Lists
  2. Add ingress rules:
    • Port 80 (HTTP): Source 0.0.0.0/0
    • Port 443 (HTTPS): Source 0.0.0.0/0

🌐 Step 2: DNS Configuration

Set up your subdomain to point to your Oracle Cloud VM:

Record Type Host Value TTL
A sb 123.456.78.90 14400

Example: If your domain is example.com, create sb.example.com pointing to your VM's IP.

πŸ–₯️ Step 3: Server Setup

3.1 Connect to Your VM

ssh -i ~/path/to/your-ssh-key.key [email protected]

3.2 Update System & Install Dependencies

sudo apt update
sudo apt install nginx git -y

3.3 Install Docker

sudo apt install docker.io -y
sudo systemctl start docker
sudo systemctl enable docker

3.4 Install Docker Compose

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

3.5 Install Certbot

sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot

πŸ“¦ Step 4: Supabase Installation

4.1 Clone Supabase Repository

git clone --depth 1 https://github.com/supabase/supabase.git
cd supabase/docker
cp .env.example .env

4.2 Configure Environment Variables

Edit the .env file:

nano .env

Required Changes:

# Database Password (25+ characters, all characters allowed)
POSTGRES_PASSWORD=SecureDatabasePassword123!@#

# JWT Secret (32+ characters, letters and numbers only)
JWT_SECRET=MyJWTSecretKey1234567890ABCDEFGHIJ

# External URLs (replace with your domain)
API_EXTERNAL_URL=https://sb.example.com
SUPABASE_PUBLIC_URL=https://sb.example.com

# Dashboard Credentials
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=SecureAdminPassword123

# Security Keys
SECRET_KEY_BASE=SecretKey64CharactersLong123456789012345678901234567890!@#$
VAULT_ENC_KEY=VaultEncryptionKey32CharsLong123456

# File Upload Limit (0 = no limit, value in bytes)
FILE_SIZE_LIMIT=0

# Keep existing ANON_KEY and SERVICE_ROLE_KEY as they are

Password Requirements:

  • POSTGRES_PASSWORD: Minimum 25 characters, any characters allowed
  • JWT_SECRET: Minimum 32 characters, letters and numbers only
  • SECRET_KEY_BASE: Minimum 64 characters, any characters allowed
  • VAULT_ENC_KEY: Minimum 32 characters, letters and numbers only

4.3 Fix Docker Compose Port Mapping

Edit docker-compose.yml to expose Studio port:

nano docker-compose.yml

Find the studio: section (around line 12) and add ports:

  studio:
    container_name: supabase-studio
    image: supabase/studio:2025.05.19-sha-3487831
    restart: unless-stopped
    ports:
      - "3000:3000"  # ADD THIS LINE
    healthcheck:
      test:
        [
          "CMD",
          "node",
          "-e",
          "fetch('http://studio:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})"
        ]

⚠️ Important: Add the ports: section exactly between restart: unless-stopped and healthcheck:. Why this port mapping is required: By default, Docker containers only expose ports internally within the Docker network. Supabase Studio runs on port 3000 inside the container, but without the "3000:3000" mapping, this port is only accessible to other containers. Nginx needs to access Studio on localhost:3000 to proxy web requests. Without this mapping, Nginx cannot reach the Studio interface, resulting in a non-functional web dashboard.

4.4 Start Supabase

sudo docker-compose up -d

This will take 10-15 minutes on the first run.

4.5 Verify Installation

sudo docker ps

All containers should show "Up" status. Some may show "Restarting" or "Unhealthy" - this is normal for optional services like pooler and realtime.

πŸ”’ Step 5: SSL Certificate Setup

5.1 Get SSL Certificate

sudo certbot certonly --nginx -d sb.example.com

Follow the prompts:

  • Enter your email address
  • Accept terms of service
  • Choose whether to share email with EFF

Certificate files will be stored at:

  • Certificate: /etc/letsencrypt/live/sb.example.com/fullchain.pem
  • Private Key: /etc/letsencrypt/live/sb.example.com/privkey.pem

🌐 Step 6: Nginx Configuration

6.1 Create Nginx Configuration

sudo nano /etc/nginx/sites-available/supabase.conf

Add this configuration (replace sb.example.com with your subdomain):

server {
    server_name sb.example.com;
    
    gzip on;

    # REST API
    location ~ ^/rest/v1/(.*)$ {
        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_pass http://localhost:8000;
        proxy_redirect off;
    }

    # Authentication
    location ~ ^/auth/v1/(.*)$ {
        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_pass http://localhost:8000;
        proxy_redirect off;
    }

    # Realtime
    location ~ ^/realtime/v1/(.*)$ {
        proxy_redirect off;
        proxy_pass http://localhost:8000;
        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;
    }

    # Supabase Studio (Dashboard)
    location / {
        proxy_pass http://localhost:3000;
        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 86400;
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/sb.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sb.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    if ($host = sb.example.com) {
        return 301 https://$host$request_uri;
    }

    listen 80;
    server_name sb.example.com;
    return 404;
}

6.2 Enable Configuration

sudo ln -s /etc/nginx/sites-available/supabase.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

βœ… Step 7: Access Your Supabase Instance

Open your browser and navigate to: https://sb.example.com

You should see the Supabase Studio login page.

Login with:

  • Username: admin (or whatever you set as DASHBOARD_USERNAME)
  • Password: Your DASHBOARD_PASSWORD

πŸ”„ Step 8: Automatic Updates & Auto-Start

8.1 Create Auto-Update Script

Create an optimized update script that only backs up configurations:

cd ~
nano update_supabase.sh

Add this content:

#!/bin/bash

LOG_FILE="/var/log/supabase_update.log"
BACKUP_DATE=$(date +'%Y-%m-%d_%H-%M-%S')

echo "=== Supabase Update Started: $(date) ===" >> $LOG_FILE

# Backup only important configs (NOT the database!)
echo "Creating config backup..." >> $LOG_FILE
mkdir -p ~/supabase_config_backup_$BACKUP_DATE
cp ~/supabase/docker/.env ~/supabase_config_backup_$BACKUP_DATE/
cp ~/supabase/docker/docker-compose.yml ~/supabase_config_backup_$BACKUP_DATE/
cp -r ~/supabase/docker/volumes/api ~/supabase_config_backup_$BACKUP_DATE/

# Navigate to supabase directory
cd ~/supabase/docker

# Stop current services
echo "Stopping Supabase services..." >> $LOG_FILE
sudo docker-compose down >> $LOG_FILE 2>&1

# Pull latest changes from repository
echo "Pulling latest Supabase updates..." >> $LOG_FILE
git pull origin master >> $LOG_FILE 2>&1

# Pull latest Docker images
echo "Pulling latest Docker images..." >> $LOG_FILE
sudo docker-compose pull >> $LOG_FILE 2>&1

# Start services with updated images
echo "Starting updated Supabase services..." >> $LOG_FILE
sudo docker-compose up -d >> $LOG_FILE 2>&1

# Wait for services to start
sleep 60

# Check if services are running
echo "Checking service status..." >> $LOG_FILE
sudo docker ps >> $LOG_FILE 2>&1

# Clean up old Docker images to save space
echo "Cleaning up old Docker images..." >> $LOG_FILE
sudo docker image prune -af >> $LOG_FILE 2>&1

# Remove old config backups (keep only last 2)
echo "Cleaning up old config backups..." >> $LOG_FILE
find ~/supabase_config_backup_* -maxdepth 0 -type d | sort | head -n -2 | xargs rm -rf

echo "=== Supabase Update Completed: $(date) ===" >> $LOG_FILE
echo "" >> $LOG_FILE

8.2 Make Script Executable & Setup Log

chmod +x ~/update_supabase.sh
sudo touch /var/log/supabase_update.log
sudo chmod 666 /var/log/supabase_update.log

8.3 Set Up Cronjobs

Open crontab:

sudo crontab -e

Add these lines:

# Auto-start Supabase after VM reboot
@reboot sleep 30 && cd /home/ubuntu/supabase/docker && /usr/local/bin/docker-compose up -d

# Supabase auto-update every Sunday at 3 AM German time
0 1 * * 0 /bin/bash /home/ubuntu/update_supabase.sh >/dev/null 2>&1

Note: 0 1 * * 0 = 1 AM UTC = 3 AM German time (CEST)

8.4 Verify Cronjobs

sudo crontab -l

πŸ”§ Port Overview

Your Supabase instance uses these ports:

  • Port 3000: Supabase Studio (Dashboard)
  • Port 8000: Kong API Gateway (REST API, Auth, Realtime)
  • Port 5432: PostgreSQL Database (internal)
  • Port 4000: Analytics/Logflare (internal)

πŸ› οΈ Managing Your Instance

Manual Commands

# Stop Supabase
cd ~/supabase/docker
sudo docker-compose down

# Start Supabase
cd ~/supabase/docker
sudo docker-compose up -d

# View Logs
cd ~/supabase/docker
sudo docker-compose logs -f

# Manual Update
sudo bash ~/update_supabase.sh

Monitor Updates

# Check update logs
tail -n 50 /var/log/supabase_update.log

# List config backups
ls -la ~/supabase_config_backup_*

# Check running containers
sudo docker ps

πŸ“Š Auto-Update Features

  • Automatic updates every Sunday at 3 AM German time
  • Auto-start after VM reboot (30 second delay)
  • Config-only backups (fast, space-efficient)
  • Keeps only 2 latest backups (prevents disk filling)
  • Database data preserved (never copied, always persists)
  • Detailed logging of all operations
  • Automatic cleanup of old Docker images

πŸ” Data Safety

What Gets Backed Up (Fast & Small)

  • βœ… .env file (passwords, keys, configuration)
  • βœ… docker-compose.yml (port mappings, service config)
  • βœ… volumes/api/kong.yml (API gateway configuration)

What Stays Safe (Never Copied)

  • πŸ’Ύ Database data: ./volumes/db/data/ (persists on disk)
  • πŸ’Ύ File storage: ./volumes/storage/ (your uploaded files)
  • πŸ’Ύ All user data: Tables, users, API keys remain intact

πŸ› οΈ Troubleshooting

502 Bad Gateway

  1. Check if containers are running: sudo docker ps
  2. Verify ports are accessible: sudo netstat -tlnp | grep :3000 and sudo netstat -tlnp | grep :8000
  3. Check Nginx logs: sudo tail -f /var/log/nginx/error.log

Container Restart Issues

Some containers (pooler, realtime) may show "Restarting" or "Unhealthy" status. This is normal and doesn't affect core functionality.

SSL Certificate Renewal

Certificates auto-renew. To manually renew:

sudo certbot renew
sudo systemctl reload nginx

Restore from Config Backup (if needed)

cd ~/supabase/docker
sudo docker-compose down
cp ~/supabase_config_backup_YYYY-MM-DD_HH-MM-SS/.env .
cp ~/supabase_config_backup_YYYY-MM-DD_HH-MM-SS/docker-compose.yml .
cp -r ~/supabase_config_backup_YYYY-MM-DD_HH-MM-SS/api ./volumes/
sudo docker-compose up -d

πŸ” Security Considerations

  1. Change default passwords in .env file
  2. Use strong, unique passwords for all services
  3. Regularly monitor update logs
  4. Keep your domain DNS secure
  5. Monitor Oracle Cloud billing (shouldn't exceed free tier)

πŸ“š API Usage

Once installed, your Supabase API will be available at:

  • REST API: https://sb.example.com/rest/v1/
  • Auth API: https://sb.example.com/auth/v1/
  • Realtime: https://sb.example.com/realtime/v1/

Use your ANON_KEY and SERVICE_ROLE_KEY from the .env file for API authentication.


πŸ”— Integrating with n8n (Self-Hosted)

If you're running a self-hosted n8n instance and want to connect it to your self-hosted Supabase, you'll need additional configuration due to authentication header conflicts between n8n and Kong (Supabase's API gateway).

Prerequisites for n8n Integration

  1. Additional Firewall Ports: Add these to your Oracle Cloud Security Rules:
    • Port 8000 (Supabase Kong API Gateway): Source 0.0.0.0/0
    • Port 5432 (PostgreSQL - optional for direct DB access): Source 0.0.0.0/0

Fix Kong Authorization Header Conflict

The n8n Supabase node sends both apikey and Authorization headers simultaneously. Kong prioritizes the Authorization header and ignores the apikey header, causing "Unauthorized" errors.

Solution: Configure Kong to remove the Authorization header before processing the request.

  1. Edit Kong configuration:
nano ~/supabase/docker/volumes/api/kong.yml
  1. Find the rest-v1 service (around line 93) and add the request-transformer plugin:
  ## Secure REST routes
  - name: rest-v1
    _comment: 'PostgREST: /rest/v1/* -> http://rest:3000/*'
    url: http://rest:3000/
    routes:
      - name: rest-v1-all
        strip_path: true
        paths:
          - /rest/v1/
    plugins:
      - name: cors
      - name: key-auth
        config:
          hide_credentials: true
      - name: request-transformer    # ADD THIS PLUGIN
        config:
          remove:
            headers:
              - Authorization        # REMOVE Authorization HEADER
      - name: acl
        config:
          hide_groups_header: true
          allow:
            - admin
            - anon
  1. Ensure the request-transformer plugin is enabled in docker-compose.yml:
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
  1. Restart Kong:
sudo docker-compose restart kong

Configure n8n Supabase Credentials

  1. In n8n, create new Supabase credentials with:

    • Host: sb.example.com (without https:// prefix or /rest/v1/ suffix)
    • Service Role Secret: Your SERVICE_ROLE_KEY from the .env file
  2. Test the connection - it should now show "Connection tested successfully".

Understanding the Fix

  • n8n Behavior: The n8n Supabase node automatically sends both authentication headers
  • Kong Priority: Kong processes the Authorization header first and ignores the apikey
  • Header Removal: The request-transformer plugin removes the problematic Authorization header
  • Result: Kong only sees the apikey header and authentication succeeds

API Keys and Security

Your self-hosted Supabase instance provides two types of API keys:

  • Client API Key (anon): For frontend applications with limited permissions controlled by Row Level Security (RLS)
  • Service Role Key: For backend services like n8n with full administrative access

Both keys are private to your installation and not publicly accessible. They are generated based on your JWT_SECRET in the .env file.

Row Level Security (RLS)

By default, Supabase tables created through the Table Editor have RLS enabled. This means:

  • The anon key can only access data according to your RLS policies
  • The service_role key bypasses RLS and has full access (used by n8n)

To create policies for controlled access with the anon key, use the SQL Editor in Supabase Studio.

Alternative: Direct PostgreSQL Connection

If you prefer direct database access instead of using the Supabase API, you can connect n8n directly to PostgreSQL:

  • Host: sb.example.com
  • Port: 5432
  • Database: postgres
  • User: postgres
  • Password: Your POSTGRES_PASSWORD from the .env file
  • SSL: Enable if connecting from external networks

This bypasses all Supabase API features but provides direct database access for simple CRUD operations.


πŸŽ‰ Success!

You now have a fully automated, self-hosted Supabase instance that:

  • βœ… Runs 24/7 on Oracle Cloud free tier
  • βœ… Auto-updates every Sunday at 3 AM German time
  • βœ… Auto-starts after VM reboots
  • βœ… Keeps your data safe during updates
  • βœ… Manages disk space automatically
  • βœ… Uses SSL encryption with automatic renewal
  • βœ… Accessible via custom domain
  • βœ… Integrates with n8n for workflow automation

πŸ”Œ Connection Methods Summary Your self-hosted Supabase supports two integration approaches: Method 1: Supabase API (Recommended)

Requirements: Host (sb.example.com) + SERVICE_ROLE_KEY Security: Very secure - keys are private, not publicly accessible Access: Only you (keys generated from your JWT_SECRET) Features: Full Supabase functionality, Row Level Security, real-time subscriptions

Method 2: Direct PostgreSQL

Requirements: Host + Port 5432 + postgres user + POSTGRES_PASSWORD Security: Less secure - bypasses all Supabase security features Access: Anyone with IP + port + credentials Features: Basic CRUD operations only

Recommendation: Use Supabase API method and close port 5432 in firewall for maximum security.


Need help? Check the official Supabase documentation or Docker self-hosting guide.

About

Complete guide to self-host Supabase on Oracle Cloud free tier with Docker, Nginx, SSL, and automatic updates. Includes auto-start after reboots and optimized backup system.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published