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.
Table of Contents
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:
| Field | Value |
|---|---|
| Name | api or @ for root |
| Type | A |
| Value | Your server’s public IP |
| TTL | 300 (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 & Services → OAuth consent screen → click Get started → set app type to External.
- Go to APIs & Services → Credentials → Create Credentials → OAuth 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 GitHub → Settings → Developer settings → OAuth Apps → New 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:
| Provider | Env prefix in .env | Extra variable |
|---|---|---|
| GOOGLE_ | — | |
| GitHub | GITHUB_ | URL for GitHub Enterprise |
| GitLab | GITLAB_ | URL for self-hosted GitLab |
| Discord | DISCORD_ | — |
| FACEBOOK_ | — | |
| Azure | AZURE_ | URL (tenant URL) |
| LINKEDIN_OIDC_ | — | |
| Slack | SLACK_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.