Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Manoj-nathwani/31f767b55853e2bfc20f736fb3fa1b96 to your computer and use it in GitHub Desktop.

Select an option

Save Manoj-nathwani/31f767b55853e2bfc20f736fb3fa1b96 to your computer and use it in GitHub Desktop.

Ejecting a Lovable App to Cloudflare + Supabase

How to move a Lovable app from Lovable's managed hosting to your own Supabase project and Cloudflare Workers — while keeping Lovable as an editor.

Why eject?

Lovable is great for building. But its managed Supabase instance and auto-deploy pipeline mean you don't control the database, auth config, email templates, or hosting. Ejecting gives you:

  • Your own Supabase project — full control over schema, RLS policies, auth settings, edge functions, and email templates, all managed as code
  • Cloudflare Workers hosting — static asset serving with custom domains, no build pipeline to maintain
  • Lovable still works — keep lovable-tagger in your dev dependencies and Lovable can still push code changes to your repo

What you'll end up with

Concern Before (Lovable-managed) After (ejected)
Database Lovable's shared Supabase project Your own Supabase project
Auth config Dashboard clicks config.toml (as code)
Email templates React TSX via auth-email-hook edge function Simple HTML templates
Edge functions Deployed by Lovable supabase functions deploy
Hosting Lovable auto-deploy Cloudflare Workers via wrangler
Code editing Lovable UI Lovable UI + local IDE (both work)

Prerequisites

  • Node.js (18+)
  • Supabase CLI (npm i -g supabase)
  • Wrangler CLI (npm i -g wrangler)
  • A Cloudflare account (free tier works)
  • A Supabase account (free tier works)

Step 1: Create your Supabase project

Go to supabase.com and create a new project. Grab these values:

  • Project ref — the subdomain in the API URL (e.g. abcdefghijklmnop)
  • Publishable key — Dashboard → API Keys
  • Service role key — Dashboard → API Keys (keep this secret)
  • Database password — the one you set during project creation

Step 2: Export your schema

You need to get your current schema out of Lovable's Supabase and into a migration file.

Connect to Lovable's Supabase project (the old one) and dump the schema:

supabase link --project-ref <lovable-project-ref>
supabase db dump -f supabase/migrations/00000000000000_initial_schema.sql

Review the dump. It will include Lovable's internal tables and extensions you may not need. Clean it up:

  • Keep your public schema tables, RLS policies, functions, and triggers
  • Keep any auth schema seed data you want (e.g. a first admin user)
  • Remove Lovable-specific artifacts
  • Remove CREATE EXTENSION statements for extensions your app doesn't use

The goal is a single, clean SQL file that creates your entire schema from scratch.

Step 3: Set up the supabase directory

Structure your supabase/ directory:

supabase/
├── config.toml
├── .env                  # gitignored — DB password
├── .env.example
├── migrations/
│   └── 00000000000000_initial_schema.sql
├── templates/
│   ├── invite.html
│   └── magic_link.html
└── functions/
    └── (your edge functions)

config.toml

Replace Lovable's config with your own. This is the key file — it controls auth settings, email templates, and edge function config:

project_id = "<your-project-ref>"

[auth]
site_url = "https://yourdomain.com"
additional_redirect_urls = ["https://yourdomain.com", "http://localhost:3000"]
enable_signup = false          # or true, depending on your app

[auth.email]
enable_signup = false
enable_confirmations = true

[auth.email.template.magic_link]
subject = "Your sign-in link"
content_path = "./supabase/templates/magic_link.html"

[auth.email.template.invite]
subject = "You've been invited"
content_path = "./supabase/templates/invite.html"

[functions.your-function-name]
verify_jwt = false             # or true — set per function

Email templates

Lovable uses a auth-email-hook edge function with React (TSX) email templates. You don't need any of that. Supabase supports simple HTML templates with Go template variables natively.

Create supabase/templates/magic_link.html:

<html>
<body style="font-family: system-ui, sans-serif; padding: 32px;">
  <h2>Sign in</h2>
  <p>Click the button below to sign in.</p>
  <p><a href="{{ .ConfirmationURL }}" style="display:inline-block; background:#1a1a2e; color:#fff; padding:12px 24px; border-radius:8px; text-decoration:none;">Sign In</a></p>
  <p style="color:#888; font-size:12px;">If you didn't request this, ignore this email.</p>
</body>
</html>

Create supabase/templates/invite.html:

<html>
<body style="font-family: system-ui, sans-serif; padding: 32px;">
  <h2>You've been invited</h2>
  <p>Click the button below to accept your invitation.</p>
  <p><a href="{{ .ConfirmationURL }}" style="display:inline-block; background:#1a1a2e; color:#fff; padding:12px 24px; border-radius:8px; text-decoration:none;">Accept Invitation</a></p>
</body>
</html>

You can now delete the auth-email-hook edge function and its _shared/email-templates/ directory entirely.

.env files

supabase/.env (gitignored):

SUPABASE_DB_PASSWORD=<your-db-password>

.env (app root, gitignored):

VITE_SUPABASE_PROJECT_ID="<project-ref>"
VITE_SUPABASE_PUBLISHABLE_KEY="<publishable-key>"
VITE_SUPABASE_URL="https://<project-ref>.supabase.co"

.env.example (committed, no secrets):

VITE_SUPABASE_PROJECT_ID=
VITE_SUPABASE_PUBLISHABLE_KEY=
VITE_SUPABASE_URL=

Step 4: Update your .gitignore

Add these if not already present:

supabase/.env
supabase/.branches

Step 5: Fix edge function env vars

Lovable's Supabase project may inject env vars with non-standard names. The self-hosted Supabase runtime provides these automatically:

  • SUPABASE_URL
  • SUPABASE_ANON_KEY
  • SUPABASE_SERVICE_ROLE_KEY

If your edge functions reference SUPABASE_PUBLISHABLE_KEY or any other Lovable-specific name, rename them to SUPABASE_ANON_KEY:

// Before (Lovable)
const anonKey = Deno.env.get("SUPABASE_PUBLISHABLE_KEY")!;

// After (self-hosted)
const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;

Step 6: Push everything to your Supabase project

Link the CLI to your new project and push:

supabase link --project-ref <project-ref>
supabase db push                    # apply migrations
supabase config push                # auth settings + email templates
supabase functions deploy <fn-name> # repeat for each edge function

Or add npm scripts to make this repeatable:

{
  "scripts": {
    "db:push": "supabase db push && npm run db:types",
    "db:types": "supabase gen types typescript --linked > src/integrations/supabase/types.ts",
    "functions:deploy": "supabase functions deploy fn-one && supabase functions deploy fn-two",
    "supabase:push": "supabase db push && supabase config push && npm run functions:deploy && npm run db:types"
  }
}

Enable the Email auth provider

This is required after every supabase config push — the CLI resets this setting and there's no config.toml key for it.

Go to Supabase Dashboard → Authentication → Providers → Email and make sure Enable Email Provider is toggled ON. Without this, magic link sign-in fails with "Email logins are disabled."

Step 7: Set up Cloudflare Workers hosting

Move your domain to Cloudflare DNS

Cloudflare Workers custom domains require your domain's DNS to be managed by Cloudflare. If it isn't already:

  1. In the Cloudflare dashboard, click Add a site and enter your domain
  2. Select the Free plan
  3. Cloudflare will scan your existing DNS records and import them. Review the list — make sure nothing is missing (especially MX records for email)
  4. Cloudflare gives you two nameservers (e.g. anna.ns.cloudflare.com, bob.ns.cloudflare.com). Go to your domain registrar and replace the existing nameservers with these
  5. Wait for propagation — usually a few minutes, can take up to 24 hours. Cloudflare will email you when it's active

If you're using a subdomain (e.g. app.yourdomain.com), the root domain must be on Cloudflare. You don't need to move other services off your old DNS provider — just make sure you import all existing records during setup.

Install wrangler

npm i -D wrangler
npx wrangler login

Create wrangler.jsonc

{
  "name": "your-app-name",
  "compatibility_date": "2026-02-16",
  "workers_dev": false,
  "routes": [{ "pattern": "yourdomain.com", "custom_domain": true }],
  "assets": {
    "directory": "./dist",
    "not_found_handling": "single-page-application",
    "html_handling": "auto-trailing-slash"
  }
}

Key settings:

  • not_found_handling: "single-page-application" — serves index.html for all unknown paths, which is what a Vite/React SPA needs
  • custom_domain: true — Wrangler creates the DNS record pointing your domain to the Worker and provisions TLS automatically (requires your domain on Cloudflare DNS — see above)
  • workers_dev: false — disables the *.workers.dev subdomain

Add a deploy script

{
  "scripts": {
    "deploy": "vite build && npx wrangler deploy"
  }
}

Deploy

npm run deploy

That's it. Wrangler builds nothing — it just uploads the dist/ directory that Vite already built. On first deploy it creates the Worker and configures the custom domain.

Step 8: Keep Lovable working

The trick is: don't remove lovable-tagger. Keep it as a dev dependency and in your Vite config:

import { componentTagger } from "lovable-tagger";

export default defineConfig(({ mode }) => ({
  plugins: [
    react(),
    mode === "development" && componentTagger(),
  ].filter(Boolean),
  // ...
}));

Lovable reads from and pushes to your Git repo. As long as the tagger is present and the repo is connected, you can still use Lovable's visual editor to make changes. Those changes land in your repo as commits — you then deploy to Cloudflare yourself with npm run deploy.

Lovable just can't deploy for you anymore, and that's the point.

Step 9: Set up email delivery with Resend

Supabase's built-in SMTP is rate-limited to a few emails per hour — fine for testing, not for real users. Resend is the easiest upgrade: generous free tier (3,000 emails/month), takes about 5 minutes to set up.

Create a Resend account and API key

  1. Sign up at resend.com
  2. Go to API KeysCreate API Key
  3. Give it a name (e.g. "Supabase"), set permission to Sending access only, and copy the key

Verify your domain

In the Resend dashboard → DomainsAdd Domain, enter the domain you want to send from (e.g. yourdomain.com). Resend will give you DNS records to add — since your domain is now on Cloudflare (step 7), add them in the Cloudflare dashboard → your domain → DNSRecords:

Type Name Value
MX send.yourdomain.com feedback-smtp.us-east-1.amazonses.com
TXT send.yourdomain.com v=spf1 include:amazonses.com ~all
TXT resend._domainkey.yourdomain.com (DKIM key — long string from Resend)

Make sure the Cloudflare proxy toggle is off (grey cloud, DNS only) for the MX record — proxying mail records breaks email delivery. TXT records are always DNS-only. Wait for verification in the Resend dashboard — usually a few minutes.

Configure Supabase SMTP

In the Supabase Dashboard → Project SettingsAuthenticationSMTP Settings, toggle Enable Custom SMTP and fill in:

Field Value
Host smtp.resend.com
Port 465
Username resend
Password your Resend API key
Sender email noreply@yourdomain.com (must be on your verified domain)
Sender name Your App Name

The sender email must use the domain you verified in Resend. The username is literally the string resend, not your email.

Test it

Trigger a magic link sign-in. The email should arrive within seconds, sent from your domain instead of Supabase's default noreply@mail.app.supabase.io.

Note on config.toml

SMTP settings are configured in the dashboard only — there's no config.toml support for custom SMTP. This means supabase config push won't overwrite your SMTP settings, but it also means SMTP config isn't in your repo. Document the settings in your project README so they can be recreated for a new deployment.

Step 10: Clean up Lovable artifacts

You can safely delete:

  • supabase/functions/auth-email-hook/ — replaced by HTML templates
  • supabase/functions/_shared/email-templates/ — the React TSX templates
  • supabase/functions/_shared/siteConfig.ts — if it only existed for email rendering
  • Any Lovable-specific migrations — you've consolidated into one clean migration

Recap

After ejecting you have:

  • Supabase as code — schema, auth, templates, and edge functions all in your repo, deployable with one command
  • Cloudflare hosting — static assets served from the edge, custom domain with auto-TLS
  • Resend for email — magic links and invites sent from your own domain, no rate limit headaches
  • Lovable still works — for visual editing and code generation, just not for deployment
  • No CI pipeline needednpm run deploy from your laptop

The whole setup is simple enough to replicate for multiple deployments (e.g. one per client or region) by swapping out the Supabase project ref, Cloudflare worker name, and domain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment