//------------------------------------------------------------------- //-------------------------------------------------------------------
Configure OAuth Self-Hosted Supabase

Configure HTTPS, OAuth, and Backups for Self-Hosted Supabase

If you run your own backend, deploying Supabase is just step one. The real work starts when you need to configure HTTPS, point a real domain, configure OAuth self-hosted Supabase to work with providers like Google or GitHub, and put a backup plan in place so you do not lose user data. This guide covers all of that.

If you haven’t set up Supabase yet, you can check this guide on Self-hosting Supabase with Docker Compose and then follow the steps below to handle HTTPS, OAuth, and backups.

What You Need Before You Start

Before you configure OAuth self-hosted Supabase, make sure you have all of this ready:

  • A Linux server running Ubuntu 22.04 or newer with Docker and Docker Compose installed.
  • A domain name with an A record pointing to your server’s public IP.
  • Ports 80 and 443 are open in your firewall.
  • Your Supabase Docker stack is pulled and ready in a folder, for example, /opt/supabase.

For a stable setup, your server should have at least 4 GB of RAM and fast disks. Running Supabase, PostgreSQL, GoTrue Auth, and Kong on a shared VPS can cause bottlenecks under real traffic. A reliable dedicated server gives you full CPU and RAM resources with no noisy neighbors, making it a good choice once you move to production.

Step 1: DNS Setup

You must point your domain to the server before anything else. Go to your domain registrar’s DNS panel and create an A record:

FieldValue
Nameapi or @ for root
TypeA
ValueYour server’s public IP
TTL300 (5 minutes)

Wait a few minutes, then verify it works:

# Check DNS resolution
dig api.yourdomain.com +short

# Or use ping
ping api.yourdomain.com

You should see your server IP in the output. Do not move forward until DNS resolves correctly; Caddy and Certbot both need it to issue an SSL certificate.

A simple domain registration service like PerLod lets you search, buy, and manage DNS records in one place, which makes the rest of this guide easier to follow.

Step 2: Configure HTTPS for Supabase

You can easily configure HTTPS with Caddy and Nginx. Here we will show you both options:

Option A: Use Caddy for HTTPS Configuration

Supabase ships with a ready-made Caddy override file. Caddy automatically handles Let’s Encrypt certificates, renewals, HTTPS redirects, and WebSocket upgrades with zero extra config.

First, you must update your .env file and edit the URL variables to use HTTPS and your real domain:

nano /opt/supabase/.env

Change these three lines:

SUPABASE_PUBLIC_URL=https://api.yourdomain.com
API_EXTERNAL_URL=https://api.yourdomain.com
SITE_URL=https://api.yourdomain.com

Then, start Caddy with Docker Compose:

cd /opt/supabase
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d

Caddy reads the Caddyfile in volumes/proxy/caddy/ and automatically issues a certificate for your domain.

Verify HTTPS is working:

# Should return HTTP/2 401, this confirms you connected to Auth over HTTPS.
curl -I https://api.yourdomain.com/auth/v1/

# Check Caddy logs if something is wrong.
docker logs supabase-caddy

A 401 response is expected and correct here. It means HTTPS is working and the Auth service is responding.

Option B: Use Nginx for HTTPS Configuration

If you prefer Nginx over Caddy, you can use this method instead. First, set Nginx-specific variables in the .env file:

SUPABASE_PUBLIC_URL=https://api.yourdomain.com
API_EXTERNAL_URL=https://api.yourdomain.com
SITE_URL=https://api.yourdomain.com
PROXY_DOMAIN=api.yourdomain.com
CERTBOT_EMAIL=ad***@********in.com

Start with the Nginx override:

cd /opt/supabase
docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d

Verify it is working correctly:

curl -I https://api.yourdomain.com/auth/v1/
docker logs supabase-nginx

Step 3: Configure OAuth Self-Hosted Supabase with Google

When you configure OAuth self-hosted Supabase, you need three things: a correct callback URL registered with the provider, the right .env values, and the matching passthrough lines in docker-compose.yml.

First, create a Google OAuth app:

  • Go to Google Cloud Console.
  • Create or select a project.
  • Go to APIs & ServicesOAuth consent screen → click Get started → set app type to External.
  • Go to APIs & ServicesCredentialsCreate CredentialsOAuth client ID.
  • Set application type to Web application.
  • Under Authorized redirect URIs, add exactly:
    • https://api.yourdomain.com/auth/v1/callback
  • Click Create and copy your Client ID and Client Secret.

Then, add the credentials to the .env file:

nano /opt/supabase/.env

Uncomment or add these lines:

GOOGLE_ENABLED=true
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_SECRET=your-google-client-secret

You must also enable in docker-compose.yml:

nano /opt/supabase/docker-compose.yml

Find the auth: service block and uncomment or add these environment lines:

auth:
  environment:
    # ... existing variables ...
    GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED}
    GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
    GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_SECRET}
    GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback

Restart only the auth container:

cd /opt/supabase
docker compose up -d --force-recreate --no-deps auth

Verify Google is enabled:

curl -H 'apikey: your-anon-key' https://api.yourdomain.com/auth/v1/settings

You should see:

{
  "external": {
    "google": true
  }
}

Step 4: Configure OAuth Self-Hosted Supabase with GitHub

The pattern to configure OAuth self-hosted Supabase is the same for every provider. Here is GitHub as a second example.

First, create a GitHub OAuth app:

  • Go to GitHubSettingsDeveloper settingsOAuth AppsNew OAuth App.
  • Set Homepage URL: https://api.yourdomain.com.
  • Set Authorization callback URL: https://api.yourdomain.com/auth/v1/callback.
  • Click Register application and copy your Client ID and Client Secret.

Add to .env file:

GITHUB_ENABLED=true
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret

Add to docker-compose.yml:

auth:
  environment:
    GOTRUE_EXTERNAL_GITHUB_ENABLED: ${GITHUB_ENABLED}
    GOTRUE_EXTERNAL_GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
    GOTRUE_EXTERNAL_GITHUB_SECRET: ${GITHUB_SECRET}
    GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback

Restart auth:

docker compose up -d --force-recreate --no-deps auth

The same four steps work for every provider in the table below, just swap the prefix:

ProviderEnv prefix in .envExtra variable
GoogleGOOGLE_
GitHubGITHUB_URL for GitHub Enterprise
GitLabGITLAB_URL for self-hosted GitLab
DiscordDISCORD_
FacebookFACEBOOK_
AzureAZURE_URL (tenant URL)
LinkedInLINKEDIN_OIDC_
SlackSLACK_OIDC_

For a complete list of provider-specific environment variables and the latest examples, you can also check the official Supabase guide.

Step 5: Allow Frontend Redirect URLs

After login, Supabase redirects users back to your frontend. If your app lives on a different domain, for example, https://app.yourdomain.com, you must explicitly allow it.

In .env, add:

ADDITIONAL_REDIRECT_URLS=https://app.yourdomain.com,https://app.yourdomain.com/**

Then restart the auth service:

docker compose up -d --force-recreate --no-deps auth

Without this, users will hit a redirect error right after a successful OAuth login, one of the most common mistakes when you configure OAuth self-hosted Supabase.

Step 6: Common Misconfigurations That Break Auth

When you configure OAuth self-hosted Supabase for the first time, these are the mistakes that show up most often:

HTTP vs HTTPS mismatch

The callback URL in your OAuth app uses http:// but your server runs on https://. Most providers reject HTTP callbacks. Always use HTTPS when you configure OAuth self-hosted Supabase for any provider.

# Confirm your API_EXTERNAL_URL is HTTPS
grep API_EXTERNAL_URL /opt/supabase/.env

Wrong callback path

Some people register https://api.yourdomain.com/callback instead of https://api.yourdomain.com/auth/v1/callback. The /auth/v1/callback path is required; do not shorten it.

Variable added to .env but not passed to the container

The .env values are not automatically available inside containers. They must have a matching GOTRUE_EXTERNAL_* line in docker-compose.yml. Check what the auth container actually sees:

docker compose exec auth env | grep GOTRUE_EXTERNAL

SITE_URL not set or wrong

After OAuth finishes, GoTrue redirects to SITE_URL. If it is blank or points to localhost in production, users land on the wrong page.

grep SITE_URL /opt/supabase/.env

Provider still shows false in settings

Run this to confirm the variable reached the container:

docker compose exec auth env | grep GOOGLE

If nothing shows, your passthrough line in docker-compose.yml is missing or commented out.

Step 7: Database Backups

Once you configure OAuth self-hosted Supabase and real users start signing in, you need a backup plan. All user accounts, tokens, and data live in PostgreSQL. Losing the database means losing everything.

1. Manual backup with pg_dump:

# Replace 'postgres' with your DB user if different
docker compose exec db pg_dump -U postgres postgres > /opt/backups/supabase_$(date +%Y%m%d_%H%M%S).sql

2. Create a backup directory:

mkdir -p /opt/backups
chmod 700 /opt/backups

3. Automate with a daily cron job:

crontab -e

Add this line to run a backup every day at 2 AM:

0 2 * * * cd /opt/supabase && docker compose exec -T db pg_dump -U postgres postgres > /opt/backups/supabase_$(date +\%Y\%m\%d).sql

4. Remove backups older than 7 days: Add this line to the same crontab to keep disk usage under control:

0 3 * * * find /opt/backups -name "*.sql" -mtime +7 -delete

5. Copy backups off-server, for example, with rsync: Do not store all backups on the same machine. Push them to a remote server or object storage:

# Copy to remote server via rsync
rsync -avz /opt/backups/ user@backup-server:/remote/backups/supabase/

6. Restore from a backup:

# Stop the app but keep the DB running
docker compose stop kong auth rest realtime storage

# Restore
cat /opt/backups/supabase_20260529.sql | docker compose exec -T db psql -U postgres postgres

# Start everything again
docker compose start

Step 8: Backup Your Config Files

Your .env and docker-compose.yml files are just as important as the database. If your server dies, you need them to rebuild it.

# Copy config files to a safe location
cp /opt/supabase/.env /opt/backups/supabase_env_$(date +%Y%m%d).bak
cp /opt/supabase/docker-compose.yml /opt/backups/docker-compose_$(date +%Y%m%d).bak

Or push them to a private Git repository without secrets inside, use a secret manager, or environment injection in production.

Step 9: Check Logs for Issues

If login breaks or something is off, always start with the logs:

# Auth container logs (OAuth errors appear here)
docker compose logs auth

# All services
docker compose logs

# Follow logs in real time
docker compose logs -f auth

# Check if all containers are running
docker compose ps

For Nginx or Caddy proxy errors:

docker logs supabase-caddy
docker logs supabase-nginx

Test the Full Supabase OAuth Configuration

You can run a quick HTML test page to confirm everything works once you configure OAuth self-hosted Supabase. Create a test HTML file:

# Create a test HTML file
cat > /tmp/oauth-test.html <<'EOF'
<!doctype html>
<html>
<body>
  <h1>Supabase OAuth Test</h1>
  <button id="loginBtn">Sign in with Google</button>
  <pre id="result"></pre>
  <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const supabase = window.supabase.createClient(
        'https://api.yourdomain.com',
        'your-anon-key'
      )
      document.getElementById('loginBtn').addEventListener('click', async () => {
        const { error } = await supabase.auth.signInWithOAuth({ provider: 'google' })
        if (error) document.getElementById('result').textContent = JSON.stringify(error, null, 2)
      })
      supabase.auth.getSession().then(({ data }) => {
        if (data.session) {
          document.getElementById('result').textContent = 'Logged in as: ' + data.session.user.email
        }
      })
    })
  </script>
</body>
</html>
EOF

Then, serve it locally:

cd /tmp && python3 -m http.server 3000

Open http://localhost:3000/oauth-test.html in your browser. Click the button. If you land back on the page logged in, your setup is working.

Conclusion

Getting HTTPS, a real domain, and working social logins on a self-hosted Supabase instance requires getting each piece right in order. The most common failures when people configure OAuth self-hosted Supabase include HTTP vs HTTPS mismatches, the wrong callback URL, and missing passthrough lines in docker-compose.yml. Follow the steps above and check the logs when something breaks.

We hope you enjoy this guide.

FAQs

What is the correct OAuth callback URL for self-hosted Supabase?

It is always https://your-domain/auth/v1/callback. Register this exact URL in your OAuth provider console.

Why does my OAuth provider say the redirect URI is wrong?

The URL registered in your OAuth app and the one in API_EXTERNAL_URL must match exactly, same domain, same path, same HTTPS. A trailing slash or a wrong path breaks it.

Can I use Caddy and Nginx at the same time for Supabase?

No. Pick one. Caddy is simpler and recommended unless you already have Nginx deeply configured in your stack.

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway