Why migrate to Already CH?
Lovable gets you to a working prototype fast. The moment you need billing in CHF, payments via TWINT, data residency in Switzerland for your Swiss customers, or any kind of nFADP audit trail — Lovable's US-hosted defaults stop working for you.
Already CH is the same Supabase + Next.js stack Lovable uses, with every US service swapped for a Swiss or European equivalent. Stripe becomes Payrexx. Resend becomes Infomaniak. Vercel becomes Exoscale + Coolify. PostHog becomes Matomo. The app stays the same. The data moves home.
What carries over without changes
- Your users and sessions. Lovable uses Supabase Auth. Already CH uses Supabase Auth. Point Already CH at your existing Supabase project (or migrate it to self-hosted Supabase on Exoscale) and sessions are untouched.
- Your UI components. Both use shadcn/ui and Tailwind CSS. Buttons, forms, and layouts paste straight into Already CH's component directory.
- Your database schema. Your Supabase tables stay exactly where they are. You'll add Drizzle schema files that mirror them, but the data doesn't move until you migrate Supabase itself to Exoscale.
What needs migration work
- Supabase hosting. Lovable uses Supabase Cloud (US/EU multi-region). Already CH ships a self-hosted Supabase recipe on Exoscale (Geneva or Zurich). Same wire protocol, same SDKs — your code does not change.
- Billing. Lovable steers you toward Stripe. Already CH ships with Payrexx wired in — credit cards, TWINT, PostFinance, CHF, 8.1% VAT, idempotent webhooks. The plan/seat model is identical. You re-create your products inside Payrexx.
- Routing. Lovable tends to use a flat
app/structure. Remap to Already CH's(app)/(auth)/(public)/(billing)/(admin)route groups. - Data fetching. Lovable generates
useEffect-based fetching in client components. Already CH uses Server Components — see the before/after below. - Env vars. Lovable uses
VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY. Already CH usesNEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_ANON_KEY, plus Payrexx, Infomaniak SMTP, Matomo, and GlitchTip keys. - Analytics. If you have PostHog or another US analytics tool, swap to the self-hosted Matomo instance that ships with Already CH. Cookie-free mode means no consent banner under nFADP.
- Lovable-specific packages. Remove
lovable-taggerand any@lovable-dev/*packages — editor tooling, not application code.
Stripe → Payrexx mapping
This is the biggest functional change, and it's the whole point. Payrexx is a Swiss payment gateway that supports CHF, TWINT, PostFinance, credit cards, and Swiss VAT out of the box. There is no Customer object — orgs are identified by referenceId at checkout, and the plan key is derived from the transaction amount (so keep your plan amounts unique).
# Lovable (.env.local — Stripe)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
# Already CH (.env.local — Payrexx)
PAYREXX_INSTANCE=your-instance
PAYREXX_API_SECRET=...
PAYREXX_WEBHOOK_SIGNING_SECRET=...
BILLING_CURRENCY=CHF
BILLING_VAT_RATE=0.081
Env var mapping
# Lovable (remove these)
VITE_SUPABASE_URL=https://xxxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGci...
# Already CH (Swiss-hosted Supabase on Exoscale)
NEXT_PUBLIC_SUPABASE_URL=https://supabase.your-domain.ch
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci... # macOS Keychain, never .env
# Swiss stack
INFOMANIAK_SMTP_HOST=mail.infomaniak.com
INFOMANIAK_SMTP_USER=already-ch@your-domain.ch
MATOMO_URL=https://matomo.your-domain.ch
GLITCHTIP_DSN=https://...@glitchtip.your-domain.ch/1
FRIENDLY_CAPTCHA_SITEKEY=FCM...
Data fetching: before / after
Lovable generates client components with useEffect for data fetching. Already CH uses Server Components — data arrives at render, no loading state, no client bundle weight.
// Lovable — client component with useEffect
'use client'
export default function ProjectList() {
const [projects, setProjects] = useState([])
useEffect(() => {
supabase.from('projects').select('*').then(({ data }) => setProjects(data))
}, [])
return <ul>{projects.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
// Already CH — Server Component with org-scoped query
import { db } from '@/lib/db'
import { projects } from '@/db/schema'
import { withOrgScope } from '@/lib/org'
export default async function ProjectList({ orgId }) {
const rows = await db.query.projects.findMany(withOrgScope({ orgId }))
return <ul>{rows.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
Step-by-step migration
Already CH ships a Coolify service seed and an Exoscale Marketplace QCOW2 build for self-hosted Supabase. Spin it up in Geneva or Zurich. Then dump your Lovable Supabase database (pg_dump) and restore it. Your users, RLS policies, and tables move intact — Swiss-resident from boot one.
Remove lovable-tagger and any @lovable-dev/* packages from your package.json. They are Lovable editor tooling — they serve no purpose outside Lovable.
Both use shadcn/ui + Tailwind. Copy your components/ directory into Already CH's components/. Tokens and variants paste across.
cp -r components/ ../your-already-ch-project/components/
Lovable: app/dashboard/page.tsx
Already CH: app/(app)/dashboard/page.tsx
Lovable: app/page.tsx (marketing)
Already CH: app/(public)/page.tsx
Lovable: app/billing/page.tsx
Already CH: app/(billing)/billing/page.tsx (requires plan)
Find every useEffect that fetches from Supabase and convert it to the Server Component pattern above. Add withOrgScope() to every tenant-scoped query — RLS is the safety net, not the only layer.
Log into your Payrexx instance, create one gateway per plan, set the amounts (unique per plan), and copy the gateway IDs into config/billing.ts. Wire your webhook URL to /api/webhooks/payrexx. Idempotency is already handled via the payment_events table.
If your Lovable app calls PostHog, replace those calls with the Matomo tracker that ships with Already CH (components/providers/analytics-provider.tsx). Cookie-free mode is the default — no consent banner needed.
The included CLI automates env var mapping, route inventory, schema introspection, and a structured TODO report for what still needs manual review.
pnpm Already CH migrate lovable --source ../my-lovable-app
Common questions
Will my users get logged out?
No. Supabase Auth is preserved across the move. If you migrate the Supabase instance itself (Lovable's Supabase Cloud → self-hosted Exoscale), users see no change — sessions and tokens remain valid because the JWT signing key is migrated with the project.
Do I have to use TWINT?
No, but it's enabled by default in Payrexx and your Swiss users will expect it. You can also enable PostFinance, credit cards, Apple Pay, Google Pay — whatever your Payrexx contract allows.
What about my existing Stripe subscriptions?
If you have live Stripe subscriptions, you can either run Stripe in parallel until everyone renews, or use Payrexx's import workflow to migrate active subscriptions. The included migration recipe in docs/recipes/stripe-to-payrexx.md walks through both paths.
How long does a migration take?
A simple Lovable app (5–10 routes, basic auth, no billing) migrates in 2–4 hours. With Stripe → Payrexx and Supabase Cloud → self-hosted Exoscale, plan a day or two. The CLI cuts the mechanical work roughly in half.