Deploy anywhere

Hybrid: Supabase plus your cloud

Keep the Supabase database and move files, email, secrets, keys or sign-in to AWS, Google Cloud or Azure — three worked examples with switch commands.

Supabase keeps the database, and usually auth and realtime too. Some areas move to your own cloud account: files to your bucket, email to your domain's provider, credentials to your secret store, sign-in to your identity provider. This is the most common end state. It keeps the low operating cost of Supabase and puts the areas your security team cares about under your own account.

You get here from a Supabase-only install by switching one area at a time. The app keeps running throughout. Below are three worked examples, one per cloud. Mix them as you need: each area is independent of the others, within the rules.

Example 1: Supabase + AWS

Supabase keeps the database, auth, realtime and pg_cron. Files go to S3, email to SES, credentials to Secrets Manager, and optionally the encryption key to AWS KMS.

Hybrid: Supabase + AWS

People and clients

Browser · REST API · MCP · CLI

UserTasks

Web, API and worker · Unchanged: wherever the app runs today

Built in

  • CacheMemory or Postgres

Supabase projectSupabase

  • DatabaseSupabase Postgres
  • AuthSupabase Auth
  • RealtimeSupabase Realtime
  • Schedulerpg_cron

AWS accountAWS

  • File storageAmazon S3
  • EmailAmazon SES
  • SecretsSecrets Manager
  • KMSAWS KMS (optional)
Supabase keeps the database, auth, realtime and pg_cron. Files move to Amazon S3, email to Amazon SES and provider credentials to AWS Secrets Manager, optionally with AWS KMS envelope encryption.
AreaServiceProvider idNative or generic
Database, auth, realtime, schedulerSupabasesupabase, pg_cron—
StorageAmazon S3s3Native: the AWS default credential chain (task role, instance profile, IRSA)
EmailAmazon SES (API)sesNative: IAM role with ses:SendEmail
SecretsAWS Secrets Manageraws_secrets_managerNative: secretsmanager:GetSecretValue
KMS (optional)AWS KMSaws_kmsNative: kms:Encrypt and kms:Decrypt on the key

Create the buckets first, because the app doesn't create them at runtime. If the app runs outside AWS (Vercel, for example), give it an IAM user's access keys (S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY, SES_ACCESS_KEY_ID / SES_SECRET_ACCESS_KEY), stored as --secret values or as secret:// references.

bash
usertasks-admin switch storage --to s3 --set region=eu-west-1 \
  --set bucket_map='{"task-files":"acme-ut-files","logos":"acme-ut-public/logos","brand-fonts":"acme-ut-public/fonts"}' --dry-run
usertasks-admin switch storage --to s3 --set region=eu-west-1 --set bucket_map='…' --yes
usertasks-admin switch email --to ses --set ses_region=eu-west-1 \
  --set from='UserTasks <tasks@corp.example>' --test-to you@corp.example --yes
# Create each credential in Secrets Manager, replace the value with secret://<name>, then:
usertasks-admin switch secrets --to aws_secrets_manager --set aws_region=eu-west-1 --yes
# Optional: envelope encryption under a KMS key
usertasks-admin switch kms --to aws_kms --set aws_kms_key_id=alias/usertasks --yes
usertasks-admin kms rewrap --yes

The same settings as environment variables (the environment wins over the switch's instance settings):

bash
STORAGE_PROVIDER=s3
S3_REGION=eu-west-1
STORAGE_BUCKET_MAP={"task-files":"acme-ut-files","logos":"acme-ut-public/logos","brand-fonts":"acme-ut-public/fonts"}
EMAIL_PROVIDER=ses
SES_REGION=eu-west-1
EMAIL_FROM="UserTasks <tasks@corp.example>"
SECRETS_PROVIDER=aws_secrets_manager
AWS_SECRETS_MANAGER_REGION=eu-west-1
KMS_PROVIDER=aws_kms
AWS_KMS_KEY_ID=alias/usertasks

Example 2: Supabase + Google Cloud

Supabase keeps the database, auth and realtime. Files go to Cloud Storage, Cloud Scheduler drives the minute tick with an OIDC token instead of pg_cron, and credentials live in Secret Manager. Email stays on Resend.

Hybrid: Supabase + Google Cloud

People and clients

Browser · REST API · MCP · CLI

UserTasks

Web, API and worker · Unchanged, or Cloud Run

Supabase projectSupabase

  • DatabaseSupabase Postgres
  • AuthSupabase Auth
  • RealtimeSupabase Realtime

Google Cloud projectGoogle Cloud

  • File storageCloud Storage
  • SchedulerCloud Scheduler (OIDC)
  • SecretsSecret Manager

Email APISaaS

  • EmailResend
Supabase keeps the database, auth and realtime. Files move to Google Cloud Storage, Cloud Scheduler drives the minute tick with an OIDC token, and credentials live in Secret Manager. Email stays on Resend.
AreaServiceProvider idNative or generic
Database, auth, realtimeSupabasesupabase—
StorageGoogle Cloud StoragegcsNative: Application Default Credentials (Cloud Run, GKE workload identity) or a key in GCS_CREDENTIALS_JSON
SchedulerCloud Scheduler HTTP job with an OIDC tokenexternalNative: the app verifies Google's signature, the audience and the calling service account
SecretsGoogle Secret Managergcp_secret_managerNative: roles/secretmanager.secretAccessor
EmailResendresend—
bash
usertasks-admin switch storage --to gcs --set gcs_project_id=acme-prod \
  --set bucket_map='{"task-files":"acme-ut-files","logos":"acme-ut-logos","brand-fonts":"acme-ut-fonts"}' --yes
# Create the Cloud Scheduler job (see the Scheduler service page), then:
usertasks-admin switch scheduler --to external \
  --set oidc_service_accounts=usertasks-scheduler@acme-prod.iam.gserviceaccount.com --yes
usertasks-admin switch secrets --to gcp_secret_manager --set gcp_project_id=acme-prod --yes

switch scheduler removes the pg_cron jobs. Until the Cloud Scheduler job runs, nothing ticks, so create the job first. Every job catches up on the next tick.

Example 3: Supabase + Azure

Supabase keeps the database, realtime and pg_cron. People sign in with Microsoft Entra ID through built-in auth. Files go to Blob Storage, email to Azure Communication Services, credentials to Key Vault.

Hybrid: Supabase + Azure

People and clients

Browser · REST API · MCP · CLI

UserTasks

Web, API and worker · Unchanged, or Azure Container Apps

Supabase projectSupabase

  • DatabaseSupabase Postgres
  • RealtimeSupabase Realtime
  • Schedulerpg_cron

Azure subscriptionAzure

  • AuthEntra ID (OIDC)
  • File storageBlob Storage
  • EmailCommunication Services
  • SecretsKey Vault
Supabase keeps the database, realtime and pg_cron. People sign in with Microsoft Entra ID through built-in auth, files move to Azure Blob Storage, email to Azure Communication Services and credentials to Key Vault.
AreaServiceProvider idNative or generic
Database, realtime, schedulerSupabasesupabase, pg_cron—
AuthBuilt-in, with Entra ID over OIDCbuiltin (or oidc for SSO only)Native preset entra
StorageAzure Blob Storageazure_blobNative: managed identity (Storage Blob Data Contributor) or a connection string
EmailAzure Communication Services Emailazure_communicationNative: managed identity or a connection string
SecretsAzure Key Vaultazure_key_vaultNative: Key Vault Secrets User

Auth off Supabase while the database stays there needs two settings: SUPABASE_JWT_SECRET (the project's JWT secret, so PostgREST and Supabase Realtime accept built-in sessions) and DATABASE_URL (a direct or session-pooler connection, for the app_auth store).

bash
# 1. Environment for built-in auth with Entra ID, then restart the app
SUPABASE_JWT_SECRET=…
DATABASE_URL=postgresql://postgres.<ref>:…@aws-0-<region>.pooler.supabase.com:5432/postgres
AUTH_SSO_PROVIDERS=[{"id":"entra","type":"oidc","preset":"entra","tenantId":"contoso.onmicrosoft.com","clientId":"…","clientSecret":"secret://entra-client-secret","allowedDomains":["contoso.com"]}]

# 2. The switches
usertasks-admin switch email --to azure_communication --set acs_endpoint=https://my-acs.communication.azure.com \
  --set from=DoNotReply@<guid>.azurecomm.net --test-to you@contoso.com --yes
usertasks-admin switch auth --to builtin --map azure=oidc:entra --dry-run
usertasks-admin switch auth --to builtin --map azure=oidc:entra --yes     # everyone signs in again
usertasks-admin switch storage --to azure_blob --set azure_account_name=acmeusertasks --set bucket_prefix=usertasks- --yes
usertasks-admin switch secrets --to azure_key_vault --set azure_key_vault_url=https://acme-ut.vault.azure.net --yes

Switch email before auth: with built-in auth, confirmation and reset emails go through your email provider. Register the redirect URI that switch auth prints (<APP_URL>/auth/sso/entra/callback) in the Entra app registration.

Trade-offs

  • Cost: the Supabase plan stays. You add the moved services' own pricing: storage and requests, email volume, secret and key API calls. Downloads served through /files count as egress from both the store and the app; STORAGE_PUBLIC_BASE_URL serves public files straight from the bucket or a CDN instead.
  • Operations: each moved area is a managed service with its own IAM, alerts and quotas. The database is still Supabase's to run.
  • Latency: most requests touch only the database, so they don't change. File uploads and downloads, email and secret lookups (cached for 5 minutes) follow the moved service's region. Keep it in the Supabase region.
  • Compliance: files, credentials and keys sit in your account, under your audit logs (CloudTrail, Azure Monitor, Cloud Audit Logs), while the database stays under Supabase's controls.

Data residency

Create the buckets, the email resource, the secret store and the key in the Supabase project's region. Tasks, data tables and run history stay in Supabase. Files, email copies and credentials follow the region you chose for each moved area.

Failure domains

Supabase plus one cloud. If the cloud side fails, only the moved areas stop: file access, email, or resolving secrets once their cache expires. If Supabase fails, everything stops. Put the app in the cloud you depend on most, or next to Supabase.

Going further

  • More areas in the same cloud, up to the database: see AWS, Azure or Google Cloud.
  • Rolling back any step: usertasks-admin history, then usertasks-admin rollback <switch id> --yes.