Next.js Supabase V3 (#463)

Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -0,0 +1,309 @@
---
status: "published"
title: "Configure Supabase Authentication Email Templates"
label: "Authentication Emails"
description: "Configure custom authentication email templates for your MakerKit application. Fix PKCE issues and customize branding for confirmation, magic link, and password reset emails."
order: 4
---
MakerKit's email templates use token_hash URLs instead of Supabase's default PKCE flow, fixing the common issue where users can't authenticate when clicking email links on different devices. This is required for reliable production authentication since many users check email on mobile but sign up on desktop.
Copy MakerKit's templates from `apps/web/supabase/templates/` to your Supabase Dashboard to enable cross-browser authentication.
## Why Custom Email Templates Matter
### The PKCE Problem
Supabase uses PKCE (Proof Key for Code Exchange) for secure authentication. Here's how it works:
1. User signs up in Chrome on their laptop
2. Supabase stores a PKCE verifier in Chrome's session
3. User receives confirmation email on their phone
4. User clicks link, opens in Safari
5. **Authentication fails** because Safari doesn't have the PKCE verifier
This affects:
- Email confirmations
- Magic link login
- Password reset flows
- Email change confirmations
### The Solution
MakerKit's email templates use **token hash URLs** instead of PKCE. This approach:
1. Includes authentication tokens directly in the URL
2. Works regardless of which browser/device opens the link
3. Maintains security through server-side verification
---
## Step 1: Locate MakerKit Templates
MakerKit provides pre-designed email templates in your project:
```
apps/web/supabase/templates/
├── confirm-email.html # Email confirmation
├── magic-link.html # Magic link login
├── reset-password.html # Password reset
├── change-email-address.html # Email change confirmation
├── invite-user.html # Team invitation
└── otp.html # One-time password
```
These templates:
- Use the token hash URL strategy
- Have modern, clean designs
- Are customizable with your branding
---
## Step 2: Update Templates in Supabase
### Navigate to Email Templates
1. Open your [Supabase Dashboard](https://supabase.com/dashboard)
2. Select your project
3. Go to **Authentication > Email Templates**
### Update Each Template
For each email type, copy the corresponding template from your MakerKit project and paste it into Supabase.
#### Confirm Signup Email
The confirmation email uses this URL format:
```html
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">
Confirm your email
</a>
```
Key elements:
- `{{ .SiteURL }}`: Your application URL
- `{{ .TokenHash }}`: Secure token for verification
- `type=email`: Specifies the confirmation type
#### Magic Link Email
```html
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink">
Sign in to your account
</a>
```
#### Password Recovery Email
```html
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=recovery">
Reset your password
</a>
```
#### Email Change Email
```html
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email_change">
Confirm email change
</a>
```
---
## Step 3: Customize Templates
### Branding Elements
Update these elements in each template:
```html
<!-- Logo -->
<img src="https://yourdomain.com/logo.png" alt="Your App" />
<!-- Company name -->
<p>Thanks for signing up for <strong>Your App Name</strong>!</p>
<!-- Footer -->
<p>&copy; 2026 Your Company Name. All rights reserved.</p>
```
### Email Styling
MakerKit templates use inline CSS for email client compatibility:
```html
<div style="
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
">
<!-- Content -->
</div>
```
### Template Variables
Supabase provides these variables:
| Variable | Description |
|----------|-------------|
| `{{ .SiteURL }}` | Your Site URL from Supabase settings |
| `{{ .TokenHash }}` | Secure authentication token |
| `{{ .Email }}` | User's email address |
| `{{ .ConfirmationURL }}` | Full confirmation URL (PKCE-based, avoid) |
{% alert type="warning" title="Avoid ConfirmationURL" %}
Don't use `{{ .ConfirmationURL }}` as it uses the PKCE flow that causes cross-browser issues. Use the token hash approach instead.
{% /alert %}
---
## Step 4: Advanced Customization
### Using the Email Starter Repository
For more advanced customization, use MakerKit's email starter:
1. Clone the repository:
- Run the following command: `git clone https://github.com/makerkit/makerkit-emails-starter`
2. Install dependencies:
- Run the following command: `cd makerkit-emails-starter && npm install`
3. Customize templates using [React Email](https://react.email/)
4. Export templates:
- Run the following command: `npm run export`
5. Copy exported HTML to Supabase Dashboard
- Copy the exported HTML files to the `apps/web/supabase/templates/` folder in your Supabase Dashboard.
### Benefits of React Email
- **Component-based**: Reuse header, footer, and button components
- **Preview**: Live preview while developing
- **TypeScript**: Type-safe email templates
- **Responsive**: Built-in responsive design utilities
---
## Template Reference
### Minimal Confirmation Template
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
<div style="max-width: 600px; margin: 0 auto;">
<h1 style="color: #333;">Confirm your email</h1>
<p style="color: #666; line-height: 1.6;">
Thanks for signing up! Click the button below to confirm your email address.
</p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
style="display: inline-block; background: #000; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; margin: 20px 0;">
Confirm Email
</a>
<p style="color: #999; font-size: 14px;">
If you didn't sign up, you can ignore this email.
</p>
</div>
</body>
</html>
```
### Minimal Magic Link Template
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
<div style="max-width: 600px; margin: 0 auto;">
<h1 style="color: #333;">Sign in to Your App</h1>
<p style="color: #666; line-height: 1.6;">
Click the button below to sign in. This link expires in 1 hour.
</p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink"
style="display: inline-block; background: #000; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; margin: 20px 0;">
Sign In
</a>
<p style="color: #999; font-size: 14px;">
If you didn't request this, you can ignore this email.
</p>
</div>
</body>
</html>
```
---
## Troubleshooting
### "Invalid token" errors
1. Verify you're using `token_hash` not `confirmation_url`
2. Check the URL includes the correct `type` parameter
3. Ensure `{{ .SiteURL }}` matches your Supabase Site URL setting
### Emails going to spam
1. Configure custom SMTP in Supabase
2. Set up SPF, DKIM, and DMARC records for your domain
3. Use a reputable email provider (Resend, SendGrid, Postmark)
### Template not updating
1. Clear browser cache and try again
2. Wait a few minutes for changes to propagate
3. Test with a fresh email address
### Links not working
1. Verify Site URL is correctly set in Supabase
2. Check your application has the `/auth/confirm` route
3. Ensure your app is deployed and accessible
---
## Testing Email Templates
### Test in Development
1. Use [Inbucket](http://localhost:54324) locally (included with Supabase local development)
2. Sign up with a test email
3. Check Inbucket for the email
4. Verify the link works correctly
### Test in Production
1. Sign up with a real email address
2. Check the email formatting
3. Click the link from a different device/browser
4. Verify successful authentication
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Which email templates do I need to update?", "answer": "Update all authentication-related templates: Confirm signup, Magic Link, Reset Password, and Change Email Address. MakerKit provides all of these in apps/web/supabase/templates/. Copy each one to the corresponding template in Supabase Dashboard."},
{"question": "How do I test email templates?", "answer": "Use Supabase's local development with Inbucket (http://localhost:54324). Sign up with a test email, check Inbucket for the email, verify formatting, and click links to test the full flow. For production, test with a real email address before launch."},
{"question": "Can I customize the email design?", "answer": "Yes. The templates in apps/web/supabase/templates/ are starting points. Update colors, logos, copy, and layout. Use inline CSS for email client compatibility. For advanced customization, use the MakerKit emails starter repo with React Email for component-based templates."},
{"question": "Do I need to update templates for both local and production?", "answer": "Templates in your codebase are for reference and local development. For production, copy the templates to Supabase Dashboard > Authentication > Email Templates. Dashboard templates override any local configuration."}
]
/%}
---
## Next Steps
- [Authentication Configuration](/docs/next-supabase-turbo/going-to-production/authentication): Configure OAuth providers and SMTP
- [Supabase Deployment](/docs/next-supabase-turbo/going-to-production/supabase): Full Supabase configuration
- [Email Configuration](/docs/next-supabase-turbo/emails/email-configuration): Application transactional emails setup

View File

@@ -0,0 +1,326 @@
---
status: "published"
title: "Configure Supabase Authentication for Production"
label: "Authentication"
description: "Configure Supabase authentication settings for production deployment. Covers URL configuration, SMTP setup, and third-party OAuth providers."
order: 3
---
Configure Supabase authentication for production with proper redirect URLs, SMTP email delivery, and OAuth providers like Google. MakerKit automatically detects enabled providers and displays the appropriate login buttons.
{% alert type="warning" title="Required for login to work" %}
Skipping these steps will cause authentication failures. Users will not be able to sign up, log in, or reset passwords without proper configuration.
{% /alert %}
## Overview
| Configuration | Purpose | Where |
|--------------|---------|-------|
| Site URL | Base URL for your application | Supabase Dashboard |
| Redirect URLs | Allowed callback URLs after auth | Supabase Dashboard |
| Custom SMTP | Reliable email delivery | Supabase Dashboard |
| OAuth Providers | Google, GitHub, etc. login | Provider + Supabase |
---
## Authentication URLs
Configure redirect URLs so Supabase knows where to send users after authentication.
### Navigate to Settings
1. Open your [Supabase Dashboard](https://app.supabase.io/)
2. Select your project
3. Go to **Authentication > URL Configuration**
### Site URL
Set this to your production domain:
```
https://yourdomain.com
```
This is the base URL Supabase uses for all authentication-related redirects.
### Redirect URLs
Add your callback URL with a wildcard pattern:
```
https://yourdomain.com/auth/callback**
```
The `**` wildcard allows MakerKit's various authentication flows to work:
- `/auth/callback` - Standard OAuth callback
- `/auth/callback/confirm` - Email confirmation
- `/auth/callback/password-reset` - Password reset flow
{% alert type="default" title="Multiple environments" %}
Add redirect URLs for each environment:
- Production: `https://yourdomain.com/auth/callback**`
- Staging: `https://staging.yourdomain.com/auth/callback**`
- Vercel previews: `https://*-yourproject.vercel.app/auth/callback**`
{% /alert %}
---
## Domain Matching
A common authentication issue occurs when domains don't match exactly.
### The Rule
Your Site URL, Redirect URLs, and actual application URL must match exactly, including:
- Protocol (`https://`)
- Subdomain (`www.` or no `www`)
- Domain name
- No trailing slash
### Examples
| Site URL | Application URL | Result |
|----------|-----------------|--------|
| `https://example.com` | `https://example.com` | Works |
| `https://example.com` | `https://www.example.com` | Fails |
| `https://www.example.com` | `https://www.example.com` | Works |
| `https://example.com/` | `https://example.com` | May fail |
### Fix Domain Mismatches
If users report login issues:
1. Check what URL appears in the browser when users visit your app
2. Ensure Site URL in Supabase matches exactly
3. Update Redirect URLs to match
4. Configure your hosting provider to redirect to a canonical URL
---
## Custom SMTP
Supabase's default email service has severe limitations:
- **Rate limit**: 4 emails per hour
- **Deliverability**: Low (often lands in spam)
- **Branding**: Generic Supabase branding
Configure a real SMTP provider for production.
### Navigate to SMTP Settings
1. Go to **Project Settings > Authentication**
2. Scroll to **SMTP Settings**
3. Toggle **Enable Custom SMTP**
### Configuration
| Field | Description |
|-------|-------------|
| Host | Your SMTP server hostname |
| Port | Usually 465 (SSL) or 587 (TLS) |
| Username | SMTP authentication username |
| Password | SMTP authentication password or API key |
| Sender email | The "from" address for emails |
| Sender name | Display name for the sender |
### Provider Examples
#### Resend
```
Host: smtp.resend.com
Port: 465
Username: resend
Password: re_your_api_key
```
#### SendGrid
```
Host: smtp.sendgrid.net
Port: 587
Username: apikey
Password: SG.your_api_key
```
#### Mailgun
```
Host: smtp.mailgun.org
Port: 587
Username: postmaster@your-domain.mailgun.org
Password: your_api_key
```
### Verify Configuration
After saving SMTP settings:
1. Create a test user with a real email address
2. Check that the confirmation email arrives
3. Verify the email doesn't land in spam
4. Confirm links in the email work correctly
---
## Third-Party Providers
MakerKit supports OAuth login through Supabase. Configure providers in both the provider's developer console and Supabase.
### How It Works
1. You enable a provider in Supabase Dashboard
2. MakerKit automatically shows the login button in the UI
3. No code changes required
### Supported Providers
MakerKit displays login buttons for any provider you enable in Supabase:
- Google
- GitHub
- Apple
- Microsoft
- Discord
- Twitter/X
- And others supported by Supabase
### General Setup Process
For each provider:
1. **Create OAuth App**: Register an application in the provider's developer console
2. **Get Credentials**: Copy the Client ID and Client Secret
3. **Set Callback URL**: Add the Supabase callback URL to your OAuth app
4. **Configure Supabase**: Enter credentials in **Authentication > Providers**
### Google Setup
Google is the most common OAuth provider. Here's the setup:
#### In Google Cloud Console
1. Go to [console.cloud.google.com](https://console.cloud.google.com)
2. Create a new project or select existing
3. Navigate to **APIs & Services > Credentials**
4. Click **Create Credentials > OAuth client ID**
5. Select **Web application**
6. Add authorized redirect URI (from Supabase)
#### In Supabase Dashboard
1. Go to **Authentication > Providers**
2. Click **Google**
3. Toggle **Enable Sign in with Google**
4. Enter your Client ID and Client Secret
5. Copy the **Callback URL** to Google Cloud
For detailed instructions, see [Supabase Google Auth documentation](https://supabase.com/docs/guides/auth/social-login/auth-google).
---
## Email Templates
MakerKit provides custom email templates that fix a common authentication issue with Supabase.
### The PKCE Problem
Supabase uses PKCE (Proof Key for Code Exchange) for secure authentication. The PKCE verifier is stored in the browser that initiated the authentication.
When a user:
1. Signs up on their laptop
2. Receives confirmation email on their phone
3. Clicks the link on their phone
Authentication fails because the phone doesn't have the PKCE verifier.
### The Solution
MakerKit's email templates use token hash URLs instead of PKCE, which work regardless of which device opens the link.
See the [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails) guide for setup instructions.
---
## Troubleshooting
### "Redirect URL not allowed" error
The callback URL doesn't match any configured Redirect URLs in Supabase.
1. Check the exact URL in the error message
2. Add it to Redirect URLs in Supabase
3. Include the `**` wildcard for flexibility
### Users can't log in after email confirmation
Usually a domain mismatch issue. Verify:
1. Site URL matches your application URL exactly
2. Redirect URLs match your domain
3. No trailing slashes causing mismatches
### OAuth login fails silently
Check browser console for errors. Common issues:
1. Callback URL in provider doesn't match Supabase
2. OAuth credentials incorrect
3. Provider not properly enabled in Supabase
### Emails not received
1. Check spam/junk folders
2. Verify SMTP settings in Supabase
3. Check your email provider's dashboard for delivery logs
4. Ensure sender domain has proper DNS records (SPF, DKIM)
### "Invalid PKCE verifier" error
Users clicking email links from different browsers/devices. Update to MakerKit's email templates. See [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails).
---
## Security Considerations
### Protect Your Credentials
- Never expose the Supabase Service Role Key in client code
- Store OAuth credentials securely (use environment variables)
- Rotate credentials if they're ever exposed
### Configure Rate Limiting
Supabase has built-in rate limiting for authentication. Review settings in **Project Settings > Auth** to prevent abuse.
### Monitor Authentication Events
Enable audit logging in Supabase to track:
- Failed login attempts
- Unusual activity patterns
- Password reset requests
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Why does authentication fail when users click email links?", "answer": "Most likely a PKCE issue. Supabase's default email templates use PKCE which fails if the user opens the link in a different browser. Use MakerKit's custom email templates with token_hash URLs instead. See the Authentication Emails guide."},
{"question": "How do I add Google login?", "answer": "Enable Google in Supabase Dashboard > Authentication > Providers. Enter your Google Cloud OAuth credentials (Client ID and Secret). Copy the Supabase callback URL to your Google Cloud OAuth app. MakerKit automatically shows the Google login button."},
{"question": "Can I use multiple OAuth providers?", "answer": "Yes. Enable as many providers as you want in Supabase. MakerKit displays login buttons for all enabled providers automatically. Each provider needs its own OAuth app configured with the Supabase callback URL."},
{"question": "Why are my authentication emails going to spam?", "answer": "Supabase's default email service has poor deliverability. Configure a real SMTP provider like Resend, SendGrid, or Postmark. Also set up SPF, DKIM, and DMARC DNS records for your sending domain."},
{"question": "What's the redirect URL wildcard for?", "answer": "The ** wildcard in redirect URLs matches any path. MakerKit uses different callback paths for different flows: /auth/callback for OAuth, /auth/callback/confirm for email confirmation, /auth/callback/password-reset for password recovery. The wildcard covers all of them."},
{"question": "How do I test authentication locally?", "answer": "Supabase local development uses Inbucket for emails at http://localhost:54324. Sign up with any email, check Inbucket for the confirmation link, and click it. For OAuth, you need to configure providers with localhost callback URLs."}
]
/%}
---
## Next Steps
- [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails): Configure email templates with token_hash URLs
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference
- [Supabase Configuration](/docs/next-supabase-turbo/going-to-production/supabase): Full Supabase setup guide

View File

@@ -0,0 +1,338 @@
---
status: "published"
title: "Production Deployment Checklist for Next.js Supabase SaaS"
label: "Checklist"
description: "Complete checklist for deploying your MakerKit Next.js Supabase application to production. Follow these steps in order to ensure a successful deployment."
order: 0
---
Deploy your MakerKit Next.js Supabase Turbo application to production by completing this checklist in order. This guide covers Supabase configuration, authentication setup, billing webhooks, and hosting deployment for Next.js 16 with React 19.
{% alert type="warning" title="Complete all steps before testing" %}
Your application will not function correctly if you skip steps. Budget 2-3 hours for your first deployment. The most common failure we see is missing environment variables, which MakerKit catches at build time with clear error messages.
{% /alert %}
## Quick Reference
| Step | What | Where | Time |
|------|------|-------|------|
| 1 | Create Supabase project | [supabase.com](https://supabase.com) | 5 min |
| 2 | Push database migrations | Terminal | 2 min |
| 3 | Configure auth URLs | Supabase Dashboard | 5 min |
| 4 | Set up OAuth providers | Supabase + Provider | 15-30 min |
| 5 | Update auth email templates | Supabase Dashboard | 10 min |
| 6 | Deploy to hosting provider | Vercel/Cloudflare/VPS | 10-20 min |
| 7 | Set environment variables | Hosting provider | 10 min |
| 8 | Configure database webhooks | Supabase Dashboard | 10 min |
| 9 | Set up SMTP for emails | Supabase + Email provider | 15 min |
| 10 | Configure billing provider | Stripe/Lemon Squeezy | 20-30 min |
---
## Pre-Deployment Requirements
Before starting, ensure you have accounts and API keys for:
- **Supabase**: Database, authentication, and storage
- **Billing provider**: Stripe or Lemon Squeezy with API keys and webhook secrets
- **Email provider**: Resend, SendGrid, Mailgun, or another SMTP service
- **Hosting provider**: Vercel, Cloudflare, or a VPS
---
## Step 1: Create and Link Supabase Project
Create a new project in the [Supabase Dashboard](https://supabase.com/dashboard). Save your database password securely as you will need it for the CLI.
Link your local project to the remote Supabase instance:
```bash
pnpm --filter web supabase login
pnpm --filter web supabase link
```
The CLI will prompt you to select your project and enter the database password.
**Verification**: Run `supabase projects list` to confirm the link.
---
## Step 2: Push Database Migrations
Push MakerKit's database schema to your production Supabase instance:
```bash
pnpm --filter web supabase db push
```
Review the migrations when prompted. You should see the core MakerKit tables (accounts, subscriptions, invitations, etc.).
**Verification**: Check the Supabase Dashboard Table Editor to confirm tables exist.
---
## Step 3: Configure Authentication URLs
In the Supabase Dashboard, navigate to **Authentication > URL Configuration**.
Set these values:
| Field | Value |
|-------|-------|
| Site URL | `https://yourdomain.com` |
| Redirect URLs | `https://yourdomain.com/auth/callback**` |
{% alert type="default" title="No URL yet?" %}
If you haven't deployed yet, skip this step and return after deployment. Your first deploy will fail without these URLs, which is expected.
{% /alert %}
**Important**: The redirect URL must include the `**` wildcard to handle all callback paths.
For detailed instructions, see the [Authentication Configuration](/docs/next-supabase-turbo/going-to-production/authentication) guide.
---
## Step 4: Set Up OAuth Providers (Optional)
If you want social login (Google, GitHub, etc.), configure providers in **Authentication > Providers** in the Supabase Dashboard.
Each provider requires:
1. Creating an OAuth app in the provider's developer console
2. Copying the Client ID and Secret to Supabase
3. Setting the callback URL from Supabase in the provider's console
MakerKit automatically displays configured providers in the login UI. No code changes needed.
For Google Auth setup, see the [Supabase Google Auth guide](https://supabase.com/docs/guides/auth/social-login/auth-google).
---
## Step 5: Update Authentication Email Templates
MakerKit provides custom email templates that fix PKCE flow issues when users click email links from different browsers or devices.
{% alert type="warning" title="Required for reliable authentication" %}
Using Supabase's default email templates will cause authentication failures when users open email links in a different browser.
{% /alert %}
Update your email templates in **Authentication > Email Templates** in the Supabase Dashboard. Use the templates from `apps/web/supabase/templates/` as your starting point.
For detailed instructions, see the [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails) guide.
---
## Step 6: Deploy Your Application
Choose your deployment platform:
| Platform | Best For | Guide |
|----------|----------|-------|
| [Vercel](/docs/next-supabase-turbo/going-to-production/vercel) | Easiest setup, automatic deployments | Recommended |
| [Cloudflare](/docs/next-supabase-turbo/going-to-production/cloudflare) | Edge runtime, lower costs | Requires config changes |
| [sherpa.sh](/docs/next-supabase-turbo/going-to-production/sherpa) | Cost-effective alternative | Good support |
| [Docker/VPS](/docs/next-supabase-turbo/going-to-production/docker) | Full control, self-hosted | More setup required |
### Which platform should I choose?
**Use Vercel when**: You want the simplest setup with preview deployments, need commercial use rights (Pro tier), or are new to deployment. Works out of the box with MakerKit.
**Use Cloudflare when**: You need zero cold starts for global users, want lower costs at scale, or are comfortable making Edge runtime configuration changes.
**Use VPS when**: You need full infrastructure control, want predictable costs regardless of traffic, or have compliance requirements for data location.
**If unsure**: Start with Vercel. You can migrate later since MakerKit supports all platforms.
**Expected**: Your first deployment will likely fail if you haven't set all environment variables. This is normal. Continue to the next step.
---
## Step 7: Set Environment Variables
Generate your production environment variables using the built-in tool:
```bash
pnpm turbo gen env
```
This interactive command creates a `.env.local` file at `turbo/generators/templates/env/.env.local` with all required variables.
Copy these variables to your hosting provider's environment settings.
**Required variables include**:
- `NEXT_PUBLIC_SITE_URL`: Your production URL
- `NEXT_PUBLIC_SUPABASE_URL`: From Supabase Dashboard > Settings > API
- `NEXT_PUBLIC_SUPABASE_PUBLIC_KEY`: Supabase anon key
- `SUPABASE_SECRET_KEY`: Supabase service role key
- Billing provider keys (Stripe or Lemon Squeezy)
- Email provider configuration
For the complete list, see [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables).
After setting variables, redeploy your application.
---
## Step 8: Configure Database Webhooks
MakerKit uses database webhooks to handle events like subscription cancellations when accounts are deleted.
### Generate a Webhook Secret
Create a strong, random secret for `SUPABASE_DB_WEBHOOK_SECRET`:
```bash
openssl rand -base64 32
```
Add this to your environment variables on your hosting provider.
### Create the Webhook in Supabase
In Supabase Dashboard, go to **Database > Webhooks**:
1. Click **Enable Webhooks** if not already enabled
2. Click **Create a new hook**
3. Configure:
- **Table**: `public.subscriptions`
- **Events**: `DELETE`
- **URL**: `https://yourdomain.com/api/db/webhook`
- **Headers**: Add `X-Supabase-Event-Signature` with your webhook secret value
- **Timeout**: `5000`
{% alert type="warning" title="Use a public URL" %}
The webhook URL must be publicly accessible. Vercel preview URLs are private and will not receive webhooks.
{% /alert %}
For detailed webhook setup, see the [Supabase Deployment](/docs/next-supabase-turbo/going-to-production/supabase) guide.
---
## Step 9: Configure Email Service (SMTP)
Supabase's built-in email service has low rate limits and poor deliverability. Configure a real SMTP provider for production.
### In Your Environment Variables
Set the mailer configuration:
```bash
MAILER_PROVIDER=resend # or nodemailer
EMAIL_SENDER=noreply@yourdomain.com
RESEND_API_KEY=re_xxxxx # if using Resend
```
### In Supabase Dashboard
Navigate to **Project Settings > Authentication > SMTP Settings** and configure your provider's SMTP credentials.
Recommended providers: [Resend](https://resend.com), [SendGrid](https://sendgrid.com), [Mailgun](https://mailgun.com)
---
## Step 10: Configure Billing Provider
### Stripe Setup
1. Create products and prices in the [Stripe Dashboard](https://dashboard.stripe.com)
2. Set environment variables:
```bash
NEXT_PUBLIC_BILLING_PROVIDER=stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
```
3. Create a webhook endpoint in Stripe pointing to `https://yourdomain.com/api/billing/webhook`
4. Select events: `checkout.session.completed`, `customer.subscription.*`, `invoice.*`
### Lemon Squeezy Setup
1. Create products in [Lemon Squeezy](https://lemonsqueezy.com)
2. Set environment variables:
```bash
NEXT_PUBLIC_BILLING_PROVIDER=lemon-squeezy
LEMON_SQUEEZY_SECRET_KEY=xxx
LEMON_SQUEEZY_STORE_ID=xxx
LEMON_SQUEEZY_SIGNING_SECRET=xxx
```
3. Configure webhooks in Lemon Squeezy pointing to `https://yourdomain.com/api/billing/webhook`
---
## Post-Deployment Tasks
After completing the main deployment:
{% sequence title="Final Setup Tasks" description="Complete these tasks to finalize your production deployment." %}
Update legal pages (Privacy Policy, Terms of Service) with your company information at `apps/web/app/[locale]/(marketing)/(legal)/`.
Replace placeholder blog and documentation content in `apps/web/content/` with your own.
Update favicon and logo in `apps/web/public/` with your branding.
Review and update FAQ content on marketing pages.
Set up monitoring with [Sentry](/docs/next-supabase-turbo/monitoring/sentry) or another error tracking service.
Configure analytics with [PostHog](/docs/next-supabase-turbo/analytics/posthog-analytics-provider) or your preferred provider.
{% /sequence %}
---
## Optional: Clear Expired OTPs
MakerKit stores one-time passwords for various flows. Clean these up periodically by running:
```sql
SELECT kit.cleanup_expired_nonces();
```
You can run this manually from the Supabase SQL Editor or set up a [pg_cron](https://supabase.com/docs/guides/database/extensions/pg_cron) job to run it automatically.
---
## Troubleshooting
### Build fails with missing environment variables
MakerKit validates environment variables at build time using Zod. Check the build logs to see which variables are missing, then add them to your hosting provider.
### Authentication redirects fail
Ensure your Site URL and Redirect URLs in Supabase exactly match your domain, including `www` if you use it.
### Webhooks not received
1. Verify the URL is publicly accessible (test in incognito mode)
2. Check the `X-Supabase-Event-Signature` header matches your secret
3. Review webhook logs in Supabase Dashboard > Database > Webhooks
### Emails not sending
1. Confirm SMTP settings in both environment variables and Supabase Dashboard
2. Check your email provider's logs for delivery issues
3. Verify your domain's DNS records (SPF, DKIM) are configured
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "How long does the full deployment process take?", "answer": "Plan for 2-3 hours for your first deployment. This includes setting up Supabase, configuring authentication, setting environment variables, and deploying to your hosting provider. Subsequent deployments are much faster since most configuration is one-time."},
{"question": "Can I deploy to multiple environments (staging, production)?", "answer": "Yes. Create separate Supabase projects for each environment, generate environment variables for each, and configure your hosting provider with environment-specific settings. Most providers like Vercel support automatic preview deployments for pull requests."},
{"question": "What if my first deployment fails?", "answer": "First deployments commonly fail due to missing environment variables. Check the build logs for specific error messages from Zod validation, add the missing variables in your hosting provider, and redeploy. MakerKit validates all required variables at build time."},
{"question": "Do I need to configure webhooks before the first deployment?", "answer": "Database webhooks require a publicly accessible URL, so you need to deploy first, then configure webhooks pointing to your production URL. Your app will work without webhooks initially, but subscription cancellation on account deletion won't function until webhooks are set up."},
{"question": "Can I use a different billing provider later?", "answer": "Yes. MakerKit supports Stripe and Lemon Squeezy. Switching requires updating environment variables and reconfiguring webhooks. Existing subscription data won't migrate automatically between providers."}
]
/%}
---
## Next Steps
- [Supabase Production Setup](/docs/next-supabase-turbo/going-to-production/supabase): Configure your Supabase project with migrations, RLS policies, and webhooks
- [Vercel Deployment](/docs/next-supabase-turbo/going-to-production/vercel): Deploy to Vercel with automatic CI/CD
- [Cloudflare Deployment](/docs/next-supabase-turbo/going-to-production/cloudflare): Deploy to Cloudflare Pages with Edge runtime
- [Docker Deployment](/docs/next-supabase-turbo/going-to-production/docker): Self-host with Docker containers
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference with Zod validation

View File

@@ -0,0 +1,342 @@
---
status: "published"
title: "Deploy Next.js Supabase to Cloudflare"
label: "Deploy to Cloudflare"
order: 6
description: "Deploy your MakerKit Next.js Supabase application to Cloudflare Pages using the Edge runtime. Covers configuration changes, OpenNext setup, and deployment."
---
Deploy your MakerKit Next.js 16 application to Cloudflare Pages using OpenNext for Edge runtime deployment. Cloudflare offers zero cold starts, global distribution, and cost-effective pricing for high-traffic applications.
## Prerequisites
Before deploying to Cloudflare:
1. **Cloudflare Workers Paid Plan**: Required due to bundle size limits on the free tier (starts at $5/month)
2. [Set up Supabase](/docs/next-supabase-turbo/going-to-production/supabase) project
3. [Generate environment variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables)
---
## Edge Runtime Considerations
Cloudflare uses the Edge runtime, which differs from Node.js. Before proceeding, understand these limitations:
### What Works Differently
| Feature | Node.js | Edge Runtime | Solution |
|---------|---------|--------------|----------|
| File system | Full access | No access | Use remote CMS |
| Nodemailer | Supported | Not supported | Use Resend or HTTP mailer |
| Pino logger | Supported | Not supported | Use console logger |
| Stripe SDK | Default config | Needs fetch client | Add `httpClient` option |
| Database latency | Low | Potentially higher | Choose region wisely |
### Benefits
- Zero cold starts
- Global edge deployment (runs close to users)
- Lower costs for high-traffic applications
- Excellent caching capabilities
---
## Step 1: Run the Cloudflare Generator
MakerKit provides a generator that scaffolds all required Cloudflare configuration:
```bash
pnpm run turbo gen cloudflare
```
This command:
1. Creates `wrangler.jsonc` configuration file
2. Creates `open-next.config.ts` for OpenNext
3. Creates `.dev.vars` for local development variables
4. Adds OpenNext and Wrangler dependencies
5. Updates `next.config.mjs` with OpenNext initialization
6. Adds deployment scripts to `package.json`
---
## Step 2: Switch to Console Logger
Pino logger uses Node.js APIs unavailable in Edge runtime. Switch to console logging:
```bash
LOGGER=console
```
Add this to both your `.env` file and Cloudflare environment variables.
---
## Step 3: Update Stripe Client
The default Stripe SDK configuration uses Node.js HTTP which doesn't work in Edge runtime. You need to modify the Stripe client to use the fetch-based HTTP client instead.
Open `packages/billing/stripe/src/services/stripe-sdk.ts` and add the `httpClient` option to the Stripe constructor:
```typescript
return new Stripe(stripeServerEnv.secretKey, {
apiVersion: STRIPE_API_VERSION,
httpClient: Stripe.createFetchHttpClient(), // ADD THIS LINE
});
```
{% alert type="warning" title="Manual change required" %}
This modification is not included in MakerKit by default. You must add the `httpClient: Stripe.createFetchHttpClient()` line yourself when deploying to Edge runtime (Cloudflare or Vercel Edge Functions).
{% /alert %}
The `httpClient` option tells Stripe to use the Fetch API instead of Node.js HTTP, making it compatible with Edge runtime.
---
## Step 4: Rename proxy.ts to middleware.ts
Rename `apps/web/proxy.ts` to `apps/web/middleware.ts`.
This is required until OpenNext supports the new `proxy.ts` convention. See [this Github issue](https://github.com/opennextjs/opennextjs-cloudflare/issues/1082) for more details.
## Step 5: Switch to HTTP-Based Mailer
Nodemailer relies on Node.js networking APIs. Use Resend instead, which uses the Fetch API:
```bash
MAILER_PROVIDER=resend
RESEND_API_KEY=re_your_api_key
EMAIL_SENDER=noreply@yourdomain.com
```
If you need a different email provider, implement a custom mailer using the abstract class in `packages/mailers`. Ensure your implementation uses only Fetch API for HTTP requests.
---
## Step 6: Switch CMS Provider
Keystatic's local mode reads from the file system, which isn't available in Edge runtime. Choose one of these alternatives:
### Option A: WordPress
Set your CMS to WordPress:
```bash
CMS_CLIENT=wordpress
```
Configure your WordPress instance as the content source. See the [WordPress CMS documentation](/docs/next-supabase-turbo/content/wordpress) for setup.
### Option B: Keystatic GitHub Mode
Keep Keystatic but use GitHub as the storage backend instead of local files:
1. Configure Keystatic for GitHub mode in your `keystatic.config.ts`
2. Set up GitHub App or Personal Access Token
3. Content is stored in your GitHub repository
See the [Keystatic documentation](/docs/next-supabase-turbo/content/keystatic) for GitHub mode setup.
---
## Step 7: Configure Environment Variables
### For Local Development
Add variables to `apps/web/.dev.vars`:
```bash
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...
SUPABASE_SECRET_KEY=eyJ...
LOGGER=console
MAILER_PROVIDER=resend
RESEND_API_KEY=re_...
# ... other variables
```
### For Production
You'll set these during deployment or via the Cloudflare Dashboard.
---
## Step 8: Preview Locally
Test your application in a Cloudflare-like environment before deploying:
```bash
pnpm --filter web run preview
```
This builds the application with OpenNext and runs it in Wrangler's local development server. Test all critical paths:
- Authentication flows
- Billing checkout
- Email sending
- Database operations
{% alert type="warning" title="Test thoroughly" %}
Edge runtime differences may cause unexpected issues. Test your entire application flow before deploying to production.
{% /alert %}
---
## Step 9: Deploy to Cloudflare
Deploy your application:
```bash
pnpm --filter web run deploy
```
This command:
1. Builds your Next.js application with OpenNext
2. Uploads to Cloudflare Pages
3. Deploys to the edge network
{% alert type="default" title="Dashboard deployment not supported" %}
At the time of writing, Cloudflare's Dashboard doesn't support OpenNext deployments. Use the CLI command instead.
{% /alert %}
---
## Additional Commands
### Generate TypeScript Types
Generate types for Cloudflare environment bindings:
```bash
pnpm --filter web run cf-typegen
```
This creates `cloudflare-env.d.ts` with type definitions for your Cloudflare environment.
### View Deployment Logs
Check your Cloudflare Dashboard under **Workers & Pages** for deployment logs and analytics.
---
## Production Configuration
### Custom Domain
1. Go to Cloudflare Dashboard > **Workers & Pages**
2. Select your project
3. Go to **Custom Domains**
4. Add your domain
Cloudflare automatically provisions SSL certificates.
### Environment Variables in Dashboard
Add production secrets via the Cloudflare Dashboard:
1. Go to **Workers & Pages** > Your Project > **Settings**
2. Click **Variables**
3. Add each secret variable
Or use Wrangler CLI:
```bash
wrangler secret put STRIPE_SECRET_KEY
```
### Caching Strategy
Cloudflare's edge caching works well with Next.js ISR. Configure cache headers in your `next.config.mjs` for optimal performance.
---
## Troubleshooting
### "Script size exceeds limit"
Your bundle exceeds Cloudflare's free tier limit. You need the Workers Paid plan ($5/month).
### "Cannot find module 'fs'"
You're using a library that requires Node.js file system APIs. Options:
1. Find an Edge-compatible alternative
2. Use dynamic imports with fallbacks
3. Move the functionality to an external API
### "fetch is not defined"
Ensure you're using the Fetch API correctly. In Edge runtime, `fetch` is globally available without importing.
### Stripe errors
Verify you've added `httpClient: Stripe.createFetchHttpClient()` to your Stripe configuration.
### Email sending fails
Confirm:
1. `MAILER_PROVIDER=resend` is set
2. `RESEND_API_KEY` is configured
3. You're not accidentally importing nodemailer
### Database timeouts
Edge functions may have higher latency to your database. Consider:
1. Placing your Supabase project in a region close to your edge deployment
2. Using connection pooling
3. Optimizing query performance
### Build fails with OpenNext errors
1. Ensure all dependencies are installed: `pnpm install`
2. Clear build caches: `rm -rf .next .open-next`
3. Check for Node.js-specific code in your pages
---
## Performance Optimization
### Regional Deployment
By default, Cloudflare deploys globally. If your users are concentrated in a region, consider:
1. Deploying Supabase in the same region
2. Using Cloudflare's Smart Placement feature
### Cache Optimization
Leverage Cloudflare's caching:
```typescript
// In your API routes
export const runtime = 'edge';
export const revalidate = 3600; // Cache for 1 hour
```
### Bundle Size
Keep your bundle small for faster cold starts:
1. Use dynamic imports for large components
2. Avoid importing entire libraries when you only need specific functions
3. Check your bundle with `next build --analyze`
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Why do I need the Workers Paid plan?", "answer": "The free tier has a 1MB script size limit, which MakerKit exceeds after bundling. The Workers Paid plan ($5/month) increases this limit and includes more requests. Most production apps need the paid tier regardless."},
{"question": "Can I use Keystatic with Cloudflare?", "answer": "Not in local file mode. Keystatic's local mode requires file system access, which Edge runtime doesn't support. Use Keystatic's GitHub mode (stores content in your repo) or switch to WordPress as your CMS provider."},
{"question": "Why isn't nodemailer working?", "answer": "Nodemailer uses Node.js networking APIs unavailable in Edge runtime. Switch to Resend (MAILER_PROVIDER=resend) which uses the Fetch API. This is a one-line environment variable change plus adding your Resend API key."},
{"question": "How do I debug Edge runtime issues?", "answer": "Run 'pnpm --filter web run preview' locally to test in a Cloudflare-like environment before deploying. Check the Wrangler logs for errors. Common issues are importing Node.js-only modules or using file system APIs."}
]
/%}
---
## Next Steps
- [Vercel Deployment](/docs/next-supabase-turbo/going-to-production/vercel): Alternative with full Node.js support
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference
- [CMS Configuration](/docs/next-supabase-turbo/content/cms): Set up WordPress or Keystatic GitHub mode

View File

@@ -0,0 +1,388 @@
---
status: "published"
title: "Deploy Next.js Supabase with Docker"
label: "Deploy with Docker"
order: 10
description: "Deploy your MakerKit Next.js Supabase application using Docker. Covers Dockerfile generation, image building, container registry, and production deployment."
---
Deploy your MakerKit Next.js 16 application using Docker containers for full infrastructure control. This guide covers the standalone build output, multi-stage Dockerfiles, container registries, and production deployment with Docker Compose.
## Overview
| Step | Purpose |
|------|---------|
| Generate Dockerfile | Create optimized Docker configuration |
| Configure environment | Set up production variables |
| Build image | Create the container image |
| Push to registry | Upload to DockerHub or GitHub Container Registry |
| Deploy | Run on your server or cloud platform |
---
## Prerequisites
Before starting:
1. [Docker](https://docs.docker.com/get-docker/) installed locally
2. [Set up Supabase](/docs/next-supabase-turbo/going-to-production/supabase) project
3. [Generate environment variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables)
---
## Step 1: Generate the Dockerfile
MakerKit provides a generator that creates an optimized Dockerfile and configures Next.js for standalone output:
```bash
pnpm run turbo gen docker
```
This command:
1. Creates a `Dockerfile` in the project root
2. Sets `output: "standalone"` in `next.config.mjs`
3. Installs platform-specific dependencies for Tailwind CSS
{% alert type="default" title="Architecture-specific dependencies" %}
The generator detects your CPU architecture (ARM64 or x64) and installs the correct Tailwind CSS and LightningCSS binaries for Linux builds.
{% /alert %}
---
## Step 2: Configure Environment Variables
### Create the Production Environment File
Generate your environment variables:
```bash
pnpm turbo gen env
```
Copy the generated file to `apps/web/.env.production.local`.
### Separate Build-Time and Runtime Secrets
Docker images should not contain secrets. Separate your variables into two groups:
**Build-time variables** (safe to include in image):
```bash
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NEXT_PUBLIC_PRODUCT_NAME=MyApp
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...
NEXT_PUBLIC_BILLING_PROVIDER=stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
```
**Runtime secrets** (add only when running container):
```bash
SUPABASE_SECRET_KEY=eyJ...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
SUPABASE_DB_WEBHOOK_SECRET=your-secret
RESEND_API_KEY=re_...
CAPTCHA_SECRET_TOKEN=...
```
### Prepare for Build
Before building, temporarily remove secrets from your env file to avoid baking them into the image:
1. Open `apps/web/.env.production.local`
2. Comment out or remove these lines:
```bash
# SUPABASE_SECRET_KEY=...
# STRIPE_SECRET_KEY=...
# STRIPE_WEBHOOK_SECRET=...
# SUPABASE_DB_WEBHOOK_SECRET=...
# RESEND_API_KEY=...
# CAPTCHA_SECRET_TOKEN=...
```
3. Save the file
4. Keep the secrets somewhere safe for later
---
## Step 3: Build the Docker Image
Build the image for your target architecture:
### For AMD64 (most cloud servers)
```bash
docker buildx build --platform linux/amd64 -t myapp:latest .
```
### For ARM64 (Apple Silicon, AWS Graviton)
```bash
docker buildx build --platform linux/arm64 -t myapp:latest .
```
### Build Options
| Flag | Purpose |
|------|---------|
| `--platform` | Target architecture |
| `-t` | Image name and tag |
| `--no-cache` | Force fresh build |
| `--progress=plain` | Show detailed build output |
Build typically completes in 3-10 minutes depending on your machine.
---
## Step 4: Add Runtime Secrets
After building, restore the secrets to your environment file:
```bash
SUPABASE_SECRET_KEY=eyJ...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
SUPABASE_DB_WEBHOOK_SECRET=your-secret
RESEND_API_KEY=re_...
CAPTCHA_SECRET_TOKEN=...
```
---
## Step 5: Run the Container
### Local Testing
Test the image locally:
```bash
docker run -d \
-p 3000:3000 \
--env-file apps/web/.env.production.local \
myapp:latest
```
Access your app at `http://localhost:3000`.
### Run Options
| Flag | Purpose |
|------|---------|
| `-d` | Run in background (detached) |
| `-p 3000:3000` | Map port 3000 |
| `--env-file` | Load environment variables from file |
| `--name myapp` | Name the container |
| `--restart unless-stopped` | Auto-restart on failure |
---
## Step 6: Push to Container Registry
### GitHub Container Registry
1. Create a Personal Access Token with `write:packages` scope
2. Login:
```bash
docker login ghcr.io -u YOUR_USERNAME
```
3. Tag your image:
```bash
docker tag myapp:latest ghcr.io/YOUR_USERNAME/myapp:latest
```
4. Push:
```bash
docker push ghcr.io/YOUR_USERNAME/myapp:latest
```
### DockerHub
1. Login:
```bash
docker login
```
2. Tag your image:
```bash
docker tag myapp:latest YOUR_USERNAME/myapp:latest
```
3. Push:
```bash
docker push YOUR_USERNAME/myapp:latest
```
---
## Step 7: Deploy to Production
### Pull and Run on Your Server
SSH into your server and run:
```bash
# Login to registry (GitHub example)
docker login ghcr.io
# Pull the image
docker pull ghcr.io/YOUR_USERNAME/myapp:latest
# Run the container
docker run -d \
-p 3000:3000 \
--env-file .env.production.local \
--name myapp \
--restart unless-stopped \
ghcr.io/YOUR_USERNAME/myapp:latest
```
### Using Docker Compose
Create `docker-compose.yml`:
```yaml
services:
web:
image: ghcr.io/YOUR_USERNAME/myapp:latest
ports:
- "3000:3000"
env_file:
- .env.production.local
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthcheck"]
interval: 30s
timeout: 10s
retries: 3
```
Run with:
```bash
docker compose up -d
```
---
## CI/CD with GitHub Actions
Automate builds and deployments with GitHub Actions:
```yaml
# .github/workflows/docker.yml
name: Build and Push Docker Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
```
---
## Production Considerations
### Health Checks
MakerKit includes a health check endpoint. Use it for monitoring:
```bash
curl http://localhost:3000/api/healthcheck
```
### Resource Limits
Set memory and CPU limits in production:
```bash
docker run -d \
-p 3000:3000 \
--memory="512m" \
--cpus="1.0" \
--env-file .env.production.local \
myapp:latest
```
### Logging
View container logs:
```bash
# Follow logs
docker logs -f myapp
# Last 100 lines
docker logs --tail 100 myapp
```
---
## Troubleshooting
### Build fails with memory error
Increase Docker's memory allocation or use a more powerful build machine:
```bash
docker build --memory=4g -t myapp:latest .
```
### Container exits immediately
Check logs for errors:
```bash
docker logs myapp
```
Common causes:
- Missing environment variables
- Port already in use
- Invalid configuration
### Image too large
The standalone output mode creates smaller images. If still too large:
1. Ensure you're using the generated Dockerfile (not a custom one)
2. Check for unnecessary files in your project
3. Use `.dockerignore` to exclude development files
### Environment variables not working
1. Verify the env file path is correct
2. Check file permissions
3. Ensure no syntax errors in the env file
4. For `NEXT_PUBLIC_` variables, rebuild the image (they're embedded at build time)
---
## Next Steps
- [VPS Deployment](/docs/next-supabase-turbo/going-to-production/vps): Deploy Docker containers to Digital Ocean, Hetzner, or Linode
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference with secrets management
- [Monitoring Setup](/docs/next-supabase-turbo/monitoring/overview): Add Sentry or PostHog for error tracking

View File

@@ -0,0 +1,369 @@
---
status: "published"
title: "Production Environment Variables for Next.js Supabase SaaS"
label: "Environment Variables"
description: "Complete reference for generating and configuring production environment variables in your MakerKit Next.js Supabase application."
order: 2
---
Generate and configure environment variables for your MakerKit Next.js Supabase Turbo production deployment. MakerKit uses Zod schemas to validate all variables at build time, catching configuration errors before deployment.
## Generate Environment Variables
MakerKit provides an interactive generator that walks you through each required variable:
```bash
pnpm turbo gen env
```
This command:
1. Prompts you for each variable value
2. Uses defaults from your existing `.env` files when available
3. Creates a file at `turbo/generators/templates/env/.env.local`
Copy the contents of this file to your hosting provider's environment variable settings.
{% alert type="warning" title="Never commit this file" %}
The generated `.env.local` contains secrets. It's git-ignored by default, but verify it's not being tracked.
{% /alert %}
---
## Validate Environment Variables
After generating or manually setting variables, validate them:
```bash
turbo gen validate-env
```
This checks that all required variables are present and correctly formatted.
---
## Required Variables Reference
### Application Settings
| Variable | Description | Example |
|----------|-------------|---------|
| `NEXT_PUBLIC_SITE_URL` | Your production URL (no trailing slash) | `https://yourdomain.com` |
| `NEXT_PUBLIC_PRODUCT_NAME` | Product name shown in UI | `MyApp` |
| `NEXT_PUBLIC_SITE_TITLE` | Browser tab title | `MyApp - Build faster` |
| `NEXT_PUBLIC_SITE_DESCRIPTION` | Meta description for SEO | `The fastest way to build SaaS` |
| `NEXT_PUBLIC_DEFAULT_THEME_MODE` | Default theme | `light`, `dark`, or `system` |
| `NEXT_PUBLIC_DEFAULT_LOCALE` | Default language | `en` |
### Supabase Configuration
| Variable | Description | Where to Find |
|----------|-------------|---------------|
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL | Dashboard > Settings > API |
| `NEXT_PUBLIC_SUPABASE_PUBLIC_KEY` | Supabase anon key | Dashboard > Settings > API |
| `SUPABASE_SECRET_KEY` | Supabase service role key | Dashboard > Settings > API |
| `SUPABASE_DB_WEBHOOK_SECRET` | Secret for webhook authentication | Generate with `openssl rand -base64 32` |
{% alert type="warning" title="Keep secrets private" %}
`SUPABASE_SECRET_KEY` bypasses Row Level Security. Never expose it in client-side code.
{% /alert %}
### Authentication Settings
| Variable | Description | Default |
|----------|-------------|---------|
| `NEXT_PUBLIC_AUTH_PASSWORD` | Enable email/password login | `true` |
| `NEXT_PUBLIC_AUTH_MAGIC_LINK` | Enable magic link login | `false` |
### Feature Flags
| Variable | Description | Default |
|----------|-------------|---------|
| `NEXT_PUBLIC_ENABLE_THEME_TOGGLE` | Show theme switcher in UI | `true` |
| `NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION` | Allow users to delete their account | `false` |
| `NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING` | Enable billing for personal accounts | `false` |
| `NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS` | Enable team/organization accounts | `true` |
| `NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION` | Allow team deletion | `false` |
| `NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING` | Enable billing for teams | `false` |
| `NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION` | Allow creating new teams | `true` |
| `NEXT_PUBLIC_ENABLE_NOTIFICATIONS` | Show notification bell in UI | `true` |
| `NEXT_PUBLIC_REALTIME_NOTIFICATIONS` | Use Supabase realtime for notifications | `false` |
| `NEXT_PUBLIC_ENABLE_VERSION_UPDATER` | Show version update popup | `false` |
### Billing Configuration
#### Stripe
| Variable | Description | Where to Find |
|----------|-------------|---------------|
| `NEXT_PUBLIC_BILLING_PROVIDER` | Set to `stripe` | - |
| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | Stripe Dashboard > API Keys |
| `STRIPE_SECRET_KEY` | Stripe secret key | Stripe Dashboard > API Keys |
| `STRIPE_WEBHOOK_SECRET` | Webhook signing secret | Stripe Dashboard > Webhooks |
#### Lemon Squeezy
| Variable | Description | Where to Find |
|----------|-------------|---------------|
| `NEXT_PUBLIC_BILLING_PROVIDER` | Set to `lemon-squeezy` | - |
| `LEMON_SQUEEZY_SECRET_KEY` | API key | Lemon Squeezy > Settings > API |
| `LEMON_SQUEEZY_STORE_ID` | Your store ID | Lemon Squeezy > Settings > Store |
| `LEMON_SQUEEZY_SIGNING_SECRET` | Webhook signing secret | Lemon Squeezy > Settings > Webhooks |
### Email Configuration
#### Using Resend
| Variable | Value |
|----------|-------|
| `MAILER_PROVIDER` | `resend` |
| `EMAIL_SENDER` | `noreply@yourdomain.com` |
| `RESEND_API_KEY` | Your Resend API key |
#### Using Nodemailer (SMTP)
| Variable | Description |
|----------|-------------|
| `MAILER_PROVIDER` | `nodemailer` |
| `EMAIL_SENDER` | `noreply@yourdomain.com` |
| `EMAIL_HOST` | SMTP host (e.g., `smtp.sendgrid.net`) |
| `EMAIL_PORT` | SMTP port (usually `587` or `465`) |
| `EMAIL_USER` | SMTP username |
| `EMAIL_PASSWORD` | SMTP password or API key |
| `EMAIL_TLS` | Enable TLS (`true` or `false`) |
### CMS Configuration
| Variable | Description |
|----------|-------------|
| `CMS_CLIENT` | CMS provider: `keystatic` or `wordpress` |
### Captcha Protection (Optional)
| Variable | Description |
|----------|-------------|
| `NEXT_PUBLIC_CAPTCHA_SITE_KEY` | Cloudflare Turnstile site key |
| `CAPTCHA_SECRET_TOKEN` | Cloudflare Turnstile secret key |
### Monitoring (Optional)
| Variable | Description |
|----------|-------------|
| `CONTACT_EMAIL` | Email for receiving contact form submissions |
| `LOGGER` | Logger type: `pino` (default) or `console` |
---
## Environment Variable Groups
### Minimum Required for Deployment
These are the absolute minimum variables needed for a working deployment:
```bash
# Application
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NEXT_PUBLIC_PRODUCT_NAME=MyApp
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...
SUPABASE_SECRET_KEY=eyJ...
# Billing (Stripe example)
NEXT_PUBLIC_BILLING_PROVIDER=stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Email
MAILER_PROVIDER=resend
EMAIL_SENDER=noreply@yourdomain.com
RESEND_API_KEY=re_...
# Webhooks
SUPABASE_DB_WEBHOOK_SECRET=your-secret
```
### Full Production Configuration
Here's a complete example with all common variables:
```bash
# ============================================
# APPLICATION
# ============================================
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NEXT_PUBLIC_PRODUCT_NAME=MyApp
NEXT_PUBLIC_SITE_TITLE=MyApp - Build SaaS Faster
NEXT_PUBLIC_SITE_DESCRIPTION=The complete SaaS starter kit
NEXT_PUBLIC_DEFAULT_THEME_MODE=light
NEXT_PUBLIC_DEFAULT_LOCALE=en
# ============================================
# AUTHENTICATION
# ============================================
NEXT_PUBLIC_AUTH_PASSWORD=true
NEXT_PUBLIC_AUTH_MAGIC_LINK=false
# ============================================
# FEATURES
# ============================================
NEXT_PUBLIC_ENABLE_THEME_TOGGLE=true
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION=true
NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=true
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS=true
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_DELETION=true
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_BILLING=true
NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_CREATION=true
NEXT_PUBLIC_ENABLE_NOTIFICATIONS=true
NEXT_PUBLIC_REALTIME_NOTIFICATIONS=false
# ============================================
# SUPABASE
# ============================================
NEXT_PUBLIC_SUPABASE_URL=https://yourproject.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJhbGciOiJI...
SUPABASE_SECRET_KEY=eyJhbGciOiJI...
SUPABASE_DB_WEBHOOK_SECRET=your-webhook-secret
# ============================================
# BILLING (Stripe)
# ============================================
NEXT_PUBLIC_BILLING_PROVIDER=stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# ============================================
# EMAIL
# ============================================
MAILER_PROVIDER=resend
EMAIL_SENDER=noreply@yourdomain.com
RESEND_API_KEY=re_...
# ============================================
# CMS
# ============================================
CMS_CLIENT=keystatic
# ============================================
# OPTIONAL
# ============================================
CONTACT_EMAIL=support@yourdomain.com
LOGGER=pino
```
---
## Build-Time vs Runtime Variables
MakerKit uses Zod schemas to validate environment variables. Understanding when validation occurs helps debug issues:
### Build-Time Validation
Variables prefixed with `NEXT_PUBLIC_` are embedded at build time. If missing:
- Build fails with a clear error message
- You must add the variable and rebuild
### Runtime Validation
Server-only variables (without `NEXT_PUBLIC_` prefix) are validated when the server starts. If missing:
- The application may start but fail when accessing certain features
- Check server logs for validation errors
---
## Secrets Management
### What Counts as a Secret
These variables contain sensitive data and must be protected:
- `SUPABASE_SECRET_KEY`
- `STRIPE_SECRET_KEY`
- `STRIPE_WEBHOOK_SECRET`
- `LEMON_SQUEEZY_SECRET_KEY`
- `LEMON_SQUEEZY_SIGNING_SECRET`
- `SUPABASE_DB_WEBHOOK_SECRET`
- `RESEND_API_KEY`
- `EMAIL_PASSWORD`
- `CAPTCHA_SECRET_TOKEN`
### Best Practices
1. **Never commit secrets**: Use `.gitignore` to exclude `.env*.local` files
2. **Use hosting provider secrets**: Vercel, Cloudflare, etc. have secure environment variable storage
3. **Rotate compromised secrets**: If a secret is exposed, regenerate it immediately
4. **Limit access**: Only give team members access to secrets they need
---
## Platform-Specific Notes
### Vercel
- Add variables in **Project Settings > Environment Variables**
- Separate variables by environment (Production, Preview, Development)
- Use Vercel's sensitive variable feature for secrets
### Cloudflare
- Add variables to `.dev.vars` for local development
- Use Wrangler secrets for production: `wrangler secret put VARIABLE_NAME`
- Some variables may need to be in `wrangler.toml` for build-time access
### Docker
- Pass variables via `--env-file` flag
- Never bake secrets into Docker images
- Use Docker secrets or external secret managers for production
---
## Troubleshooting
### "Required environment variable X is missing"
The variable isn't set in your hosting provider. Add it and redeploy.
### "Invalid value for environment variable X"
The variable value doesn't match the expected format. Check:
- URLs should start with `https://` and have no trailing slash
- Boolean values should be `true` or `false` (not `"true"`)
- Provider names must match exactly (`stripe`, not `Stripe`)
### Variables work locally but not in production
1. Verify variables are set in your hosting provider (not just `.env.local`)
2. Check for typos in variable names
3. Ensure `NEXT_PUBLIC_` prefix is correct for client-side variables
4. Redeploy after adding variables (Vercel caches builds)
### Secrets appearing in client-side code
Only variables with `NEXT_PUBLIC_` prefix should be in client code. If server-only secrets appear:
1. Check you're not importing server modules in client components
2. Verify the variable name doesn't have `NEXT_PUBLIC_` prefix
3. Review your bundle with `next build --analyze`
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Why does MakerKit validate environment variables at build time?", "answer": "Build-time validation catches configuration errors before deployment. Missing a critical variable like STRIPE_SECRET_KEY would otherwise cause runtime errors that are harder to debug. Zod schemas ensure all required variables are present and correctly formatted."},
{"question": "What's the difference between NEXT_PUBLIC_ and regular variables?", "answer": "Variables prefixed with NEXT_PUBLIC_ are embedded in the client-side JavaScript bundle and visible to users. Never use this prefix for secrets. Regular variables are only available server-side and stay secure. This is a Next.js convention."},
{"question": "How do I add a new environment variable?", "answer": "Add the variable to your hosting provider's settings, then redeploy. For client-side variables (NEXT_PUBLIC_), you must rebuild since they're embedded at build time. For server-only variables, a restart is usually sufficient."},
{"question": "Can I use different variables for staging and production?", "answer": "Yes. Most hosting providers support environment-specific variables. Create separate Supabase projects for each environment, generate variables for each, and configure your hosting provider to use the right set based on the deployment environment."},
{"question": "What if I accidentally commit secrets to Git?", "answer": "Immediately rotate all exposed credentials. Generate new API keys for Supabase, Stripe, and any other affected services. Consider using git-secrets or similar tools to prevent future accidental commits. Review Git history and consider rewriting it if the repo is private."}
]
/%}
---
## Next Steps
- [Deployment Checklist](/docs/next-supabase-turbo/going-to-production/checklist): Complete deployment steps
- [Vercel Deployment](/docs/next-supabase-turbo/going-to-production/vercel): Deploy to Vercel with CI/CD
- [Supabase Configuration](/docs/next-supabase-turbo/going-to-production/supabase): Set up Supabase with migrations and RLS

View File

@@ -0,0 +1,348 @@
---
status: "published"
title: "Deploy Supabase to Production"
label: "Deploy Supabase"
order: 1
description: "Complete guide to configuring your Supabase project for production deployment with MakerKit. Covers project setup, migrations, authentication, SMTP, and database webhooks."
---
Configure your Supabase project for production with Postgres database, Row Level Security (RLS) policies, authentication, and webhooks. This guide covers the complete setup for your MakerKit Next.js Supabase Turbo application.
{% alert type="warning" title="Complete all steps" %}
Skipping steps will cause authentication failures, missing data, or broken webhooks. Follow this guide completely before testing your application.
{% /alert %}
## Overview
| Task | Purpose |
|------|---------|
| Create project | Set up cloud database and auth |
| Push migrations | Create MakerKit database schema |
| Configure auth URLs | Enable OAuth redirects |
| Set up SMTP | Reliable email delivery |
| Update email templates | Fix PKCE authentication issues |
| Link project locally | Enable CLI deployments |
| Configure webhooks | Handle database events |
---
## Create a Supabase Project
If you're not self-hosting Supabase, create a project at [supabase.com](https://supabase.com).
1. Sign in to the [Supabase Dashboard](https://supabase.com/dashboard)
2. Click **New Project**
3. Choose your organization
4. Enter a project name and generate a database password
5. Select a region close to your users
{% alert type="default" title="Save your database password" %}
Copy the database password immediately. You cannot retrieve it later and will need it to link your local project.
{% /alert %}
---
## Retrieve API Credentials
Navigate to **Project Settings > API** to find your credentials:
| Credential | Environment Variable | Usage |
|------------|---------------------|-------|
| Project URL | `NEXT_PUBLIC_SUPABASE_URL` | Client and server connections |
| Anon (public) key | `NEXT_PUBLIC_SUPABASE_PUBLIC_KEY` | Client-side requests |
| Service role key | `SUPABASE_SECRET_KEY` | Server-side admin operations |
{% img src="/assets/courses/next-turbo/supabase-api-settings.webp" width="2500" height="1262" /%}
{% alert type="warning" title="Keep the service role key secret" %}
The service role key bypasses Row Level Security. Never expose it in client-side code or commit it to version control.
{% /alert %}
---
## Configure Authentication URLs
Set up redirect URLs so authentication flows work correctly.
Navigate to **Authentication > URL Configuration** and configure:
### Site URL
Your production domain:
```
https://yourdomain.com
```
### Redirect URLs
Add this pattern to allow all auth callbacks:
```
https://yourdomain.com/auth/callback**
```
The `**` wildcard matches any path after `/auth/callback`, which MakerKit uses for different auth flows.
{% alert type="default" title="Domain matching" %}
If your production URL includes `www`, use `www` in both the Site URL and Redirect URLs. Mismatched domains cause authentication failures.
{% /alert %}
---
## Configure SMTP
Supabase's built-in email service has strict rate limits (4 emails per hour) and low deliverability. Configure a real SMTP provider for production.
Navigate to **Project Settings > Authentication > SMTP Settings**:
1. Toggle **Enable Custom SMTP**
2. Enter your provider's credentials:
| Field | Example (Resend) |
|-------|------------------|
| Host | `smtp.resend.com` |
| Port | `465` |
| Username | `resend` |
| Password | Your API key |
| Sender email | `noreply@yourdomain.com` |
| Sender name | Your App Name |
Recommended SMTP providers:
- [Resend](https://resend.com): MakerKit has native integration, simple setup
- [SendGrid](https://sendgrid.com): High volume, good deliverability
- [Mailgun](https://mailgun.com): Developer-friendly, detailed analytics
- [Postmark](https://postmarkapp.com): Excellent deliverability, transactional focus
---
## Update Email Templates
MakerKit provides custom email templates that solve a common Supabase authentication issue.
### The Problem
Supabase uses PKCE (Proof Key for Code Exchange) for authentication. When a user clicks a confirmation link in their email and opens it in a different browser than where they signed up, authentication fails because the PKCE verifier is stored in the original browser.
### The Solution
MakerKit's templates use token hash URLs instead of PKCE, which work regardless of which browser opens the link.
### How to Update
1. Find the templates in your project at `apps/web/supabase/templates/`
2. In Supabase Dashboard, go to **Authentication > Email Templates**
3. For each email type (Confirm signup, Magic Link, etc.), replace the default template with MakerKit's version
4. Customize the templates with your branding
For detailed instructions, see the [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails) guide.
---
## Link Your Local Project
Connect your local development environment to your Supabase project using the CLI.
### Login to Supabase
```bash
pnpm --filter web supabase login
```
Follow the browser prompts to authenticate.
### Link the Project
```bash
pnpm --filter web supabase link
```
Select your project from the list and enter your database password when prompted.
**Verification**: Run `supabase projects list` to confirm the connection.
---
## Push Database Migrations
Deploy MakerKit's database schema to your production Supabase instance:
```bash
pnpm --filter web supabase db push
```
The CLI displays a list of migrations to apply. Review them and confirm.
**Expected tables**: After pushing, you should see these tables in your Supabase Dashboard Table Editor:
- `accounts`: Team and personal accounts
- `accounts_memberships`: User-account relationships
- `subscriptions`: Billing subscriptions
- `subscription_items`: Line items for subscriptions
- `invitations`: Team invitations
- `roles`: Custom role definitions
- `role_permissions`: Permission assignments
{% img src="/assets/courses/next-turbo/supabase-webhooks.webp" width="2062" height="876" /%}
---
## Configure Database Webhooks
MakerKit uses database webhooks to respond to data changes. The primary webhook handles subscription cleanup when accounts are deleted.
### Generate a Webhook Secret
Create a strong secret for authenticating webhook requests:
```bash
openssl rand -base64 32
```
Save this as `SUPABASE_DB_WEBHOOK_SECRET` in your hosting provider's environment variables.
### Why Webhooks Matter
When a user deletes their account, MakerKit needs to:
1. Cancel their subscription with the billing provider
2. Clean up related data
The webhook triggers this cleanup automatically by calling your application's `/api/db/webhook` endpoint.
### Create the Webhook
In Supabase Dashboard, navigate to **Database > Webhooks**:
1. Click **Enable Webhooks** if prompted
2. Click **Create a new hook**
3. Configure the webhook:
| Setting | Value |
|---------|-------|
| Name | `subscriptions_delete` |
| Table | `public.subscriptions` |
| Events | `DELETE` |
| Type | `HTTP Request` |
| Method | `POST` |
| URL | `https://yourdomain.com/api/db/webhook` |
| Timeout | `5000` |
4. Add a header for authentication:
- **Name**: `X-Supabase-Event-Signature`
- **Value**: Your `SUPABASE_DB_WEBHOOK_SECRET` value
{% alert type="warning" title="Use your production URL" %}
The webhook URL must be publicly accessible. Do not use:
- `localhost` or `127.0.0.1`
- Vercel preview URLs (they require authentication)
- Private network addresses
Test accessibility by visiting the URL in an incognito browser window.
{% /alert %}
### Webhook Configuration Reference
For reference, this is equivalent to the SQL trigger used in local development (from `seed.sql`):
```sql
create trigger "subscriptions_delete"
after delete
on "public"."subscriptions"
for each row
execute function "supabase_functions"."http_request"(
'https://yourdomain.com/api/db/webhook',
'POST',
'{"Content-Type":"application/json", "X-Supabase-Event-Signature":"YOUR_SECRET"}',
'{}',
'5000'
);
```
### Webhooks for Older Versions
If you're using MakerKit version 2.17.1 or earlier, you need additional webhooks:
| Table | Event | Purpose |
|-------|-------|---------|
| `public.accounts` | `DELETE` | Clean up account data |
| `public.subscriptions` | `DELETE` | Cancel billing subscription |
| `public.invitations` | `INSERT` | Send invitation emails |
Version 2.17.2+ handles invitations through server actions, so only the subscriptions webhook is required.
---
## Set Up Google Auth (Optional)
If you want Google login, configure it in both Google Cloud and Supabase.
### In Google Cloud Console
1. Create a project at [console.cloud.google.com](https://console.cloud.google.com)
2. Navigate to **APIs & Services > Credentials**
3. Click **Create Credentials > OAuth client ID**
4. Select **Web application**
5. Add authorized redirect URI from Supabase (found in **Authentication > Providers > Google**)
### In Supabase Dashboard
1. Go to **Authentication > Providers**
2. Enable **Google**
3. Enter your Client ID and Client Secret from Google Cloud
For detailed setup, see the [Supabase Google Auth documentation](https://supabase.com/docs/guides/auth/social-login/auth-google).
MakerKit automatically shows Google login when you enable it in Supabase. No code changes needed.
---
## Troubleshooting
### "Invalid PKCE verifier" error
Users see this when clicking email links from a different browser. Update your email templates to use MakerKit's token hash approach. See [Authentication Emails](/docs/next-supabase-turbo/going-to-production/authentication-emails).
### Webhooks not triggering
1. Verify the URL is publicly accessible
2. Check the `X-Supabase-Event-Signature` header matches your environment variable
3. Review logs in **Database > Webhooks** for error messages
4. Ensure your application is deployed and running
### Authentication redirect fails
1. Confirm Site URL matches your exact domain (including `www` if used)
2. Verify Redirect URL includes the `**` wildcard
3. Check browser console for specific error messages
### Emails not delivered
1. Verify SMTP settings in Supabase Dashboard
2. Check your email provider's dashboard for delivery logs
3. Confirm your sending domain has proper DNS records (SPF, DKIM, DMARC)
### Database password lost
If you forgot your database password, reset it in **Project Settings > Database > Database Password**. You'll need to re-link your local project after resetting.
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Can I use Supabase's free tier for production?", "answer": "The free tier works for early-stage apps with low traffic. It includes 500MB database storage, 1GB bandwidth, and 2GB file storage. For production apps expecting traffic, upgrade to the Pro plan ($25/month) for better performance, daily backups, and no pausing after inactivity."},
{"question": "How do I migrate from local development to production Supabase?", "answer": "Run 'pnpm --filter web supabase link' to connect your local project to the production instance, then 'pnpm --filter web supabase db push' to apply migrations. The CLI handles schema differences and shows you exactly what will change before applying."},
{"question": "Do I need to manually create RLS policies?", "answer": "No. MakerKit's migrations include all necessary RLS policies for the core tables (accounts, subscriptions, invitations, etc.). The policies are applied automatically when you push migrations. You only need to add policies for custom tables you create."},
{"question": "Why do I need database webhooks?", "answer": "Webhooks notify your application when database events occur. MakerKit uses them to cancel billing subscriptions when accounts are deleted."},
{"question": "Can I self-host Supabase instead of using their cloud?", "answer": "Yes. Supabase is open source and can be self-hosted. See the Supabase self-hosting documentation for Docker and Kubernetes options. You'll need to manage backups, updates, and infrastructure yourself."}
]
/%}
---
## Next Steps
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference with Zod schemas
- [Vercel Deployment](/docs/next-supabase-turbo/going-to-production/vercel): Deploy your Next.js application to Vercel
- [Authentication Configuration](/docs/next-supabase-turbo/going-to-production/authentication): Configure OAuth providers and SMTP

View File

@@ -0,0 +1,309 @@
---
status: "published"
title: "Deploy Next.js Supabase to Vercel"
label: "Deploy to Vercel"
order: 5
description: "Deploy your MakerKit Next.js Supabase application to Vercel. Covers project setup, environment variables, monorepo configuration, and Edge Functions deployment."
---
Deploy your MakerKit Next.js 16 application to Vercel with automatic CI/CD, preview deployments, and serverless functions. Vercel is the recommended hosting platform for Next.js apps due to its native support for App Router, Server Actions, and ISR caching.
## Prerequisites
Before deploying, complete these steps:
1. [Set up your Supabase project](/docs/next-supabase-turbo/going-to-production/supabase)
2. [Generate environment variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables)
3. Push your code to a Git repository (GitHub, GitLab, or Bitbucket)
---
## Connect Your Repository
1. Sign in to [Vercel](https://vercel.com)
2. Click **Add New Project**
3. Import your Git repository
4. Configure the project settings:
{% img src="/assets/images/docs/vercel-turbo-preset.webp" width="1744" height="854" /%}
### Required Settings
| Setting | Value |
|---------|-------|
| Framework Preset | Next.js |
| Root Directory | `apps/web` |
| Build Command | (leave default) |
| Output Directory | (leave default) |
{% alert type="warning" title="Set the root directory" %}
MakerKit uses a monorepo structure. You must set the root directory to `apps/web` or the build will fail.
{% /alert %}
---
## Configure Environment Variables
Add your production environment variables in the Vercel project settings.
### Required Variables
Generate these using `pnpm turbo gen env` in your local project:
```bash
# Application
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
NEXT_PUBLIC_PRODUCT_NAME=Your App Name
NEXT_PUBLIC_SITE_TITLE=Your App Title
NEXT_PUBLIC_SITE_DESCRIPTION=Your app description
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://yourproject.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJhbGciOiJI...
SUPABASE_SECRET_KEY=eyJhbGciOiJI...
# Billing (Stripe example)
NEXT_PUBLIC_BILLING_PROVIDER=stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Email
MAILER_PROVIDER=resend
EMAIL_SENDER=noreply@yourdomain.com
RESEND_API_KEY=re_...
# Webhooks
SUPABASE_DB_WEBHOOK_SECRET=your-webhook-secret
```
{% img src="/assets/images/docs/vercel-env-variables-turbo.webp" width="1694" height="1874" /%}
### First Deployment Note
Your first deployment will likely fail if you set `NEXT_PUBLIC_SITE_URL` to a custom domain you haven't configured yet. This is expected. Options:
1. **Use the Vercel URL first**: Set `NEXT_PUBLIC_SITE_URL` to `https://your-project.vercel.app`, deploy, then update to your custom domain later
2. **Accept the failure**: Deploy once to get the Vercel URL, then update the environment variable and redeploy
---
## Deploy
Click **Deploy** in Vercel. The build process:
1. Installs dependencies with pnpm
2. Builds the Next.js application
3. Validates environment variables (using Zod schemas)
4. Deploys to Vercel's edge network
### Build Validation
MakerKit validates environment variables at build time. If variables are missing, the build fails with specific error messages:
```
Error: Required environment variable STRIPE_SECRET_KEY is missing
```
Check the build logs to identify missing variables, add them in Vercel settings, and redeploy.
---
## Post-Deployment Setup
After successful deployment:
### 1. Update Supabase URLs
In your Supabase Dashboard (**Authentication > URL Configuration**):
| Field | Value |
|-------|-------|
| Site URL | `https://yourdomain.com` |
| Redirect URLs | `https://yourdomain.com/auth/callback**` |
### 2. Configure Billing Webhooks
Point your billing provider's webhooks to your Vercel deployment:
- **Stripe**: `https://yourdomain.com/api/billing/webhook`
- **Lemon Squeezy**: `https://yourdomain.com/api/billing/webhook`
### 3. Set Up Database Webhooks
Configure the Supabase database webhook to point to:
```
https://yourdomain.com/api/db/webhook
```
See the [Supabase deployment guide](/docs/next-supabase-turbo/going-to-production/supabase#configure-database-webhooks) for details.
---
## Custom Domain
To use a custom domain:
1. Go to your Vercel project **Settings > Domains**
2. Add your domain
3. Configure DNS records as instructed by Vercel
4. Update `NEXT_PUBLIC_SITE_URL` to your custom domain
5. Update Supabase Site URL and Redirect URLs
---
## Edge Functions Deployment (Optional)
Vercel supports Edge Functions for faster cold starts and lower latency. This requires some configuration changes.
### When to Use Edge Functions
**Benefits**:
- Zero cold starts
- Lower latency (runs closer to users)
- Lower costs for high-traffic applications
**Trade-offs**:
- Limited Node.js API support
- Potentially higher database latency (depends on region setup)
- Requires HTTP-based mailer (nodemailer not supported)
- Requires remote CMS (local Keystatic not supported)
### Configuration Changes
Apply the same changes as [Cloudflare deployment](/docs/next-supabase-turbo/going-to-production/cloudflare):
#### 1. Switch to HTTP-Based Mailer
The default nodemailer uses Node.js APIs unavailable in Edge runtime. Use Resend instead:
```bash
MAILER_PROVIDER=resend
RESEND_API_KEY=re_...
```
#### 2. Switch CMS Mode
Keystatic's local mode uses the file system, which isn't available in Edge runtime. Options:
- **WordPress**: Set `CMS_CLIENT=wordpress`
- **Keystatic GitHub mode**: Configure Keystatic to use GitHub as the data source
See the [CMS documentation](/docs/next-supabase-turbo/content/cms) for setup instructions.
#### 3. Update Stripe Client (if using Stripe)
Open `packages/billing/stripe/src/services/stripe-sdk.ts` and add the `httpClient` option to the Stripe constructor:
```typescript
return new Stripe(stripeServerEnv.secretKey, {
apiVersion: STRIPE_API_VERSION,
httpClient: Stripe.createFetchHttpClient(), // ADD THIS LINE
});
```
{% alert type="warning" title="Manual change required" %}
This modification is not included in MakerKit by default. Add the `httpClient` line when deploying to Vercel Edge Functions.
{% /alert %}
#### 4. Use Console Logger
Pino logger isn't compatible with Edge runtime:
```bash
LOGGER=console
```
---
## Multiple Apps Deployment
If you have multiple apps in your monorepo, Vercel automatically deploys the `web` app.
For additional apps, customize the build command in Vercel project settings:
```bash
cd ../.. && turbo run build --filter=<app-name>
```
Replace `<app-name>` with your app's package name (from its `package.json`).
Set the root directory to your app's path (e.g., `apps/admin`).
For more details, see the [Vercel Turborepo documentation](https://vercel.com/docs/monorepos/turborepo).
---
## Troubleshooting
### Build fails with "command not found: pnpm"
Vercel may default to npm. In your project settings, explicitly set:
- **Install Command**: `pnpm i`
- **Build Command**: `pnpm run build`
### Build fails with missing dependencies
Ensure your `package.json` dependencies are correctly listed. Turborepo should handle monorepo dependencies automatically.
### Environment variable validation fails
MakerKit uses Zod to validate environment variables. The error message shows which variable is missing or invalid. Add or correct it in Vercel settings.
### Preview deployments have authentication issues
Vercel preview deployments use unique URLs that may not match your Supabase Redirect URLs. Options:
1. Add a wildcard pattern to Supabase Redirect URLs: `https://*-your-project.vercel.app/auth/callback**`
2. Disable authentication features in preview environments
### Webhooks not received on preview deployments
Preview deployment URLs are not publicly accessible by default. Database and billing webhooks will only work on production deployments with public URLs.
---
## Performance Optimization
### Enable ISR Caching
MakerKit supports Incremental Static Regeneration for marketing pages. This is configured by default in the `next.config.mjs`.
### Configure Regions
In `vercel.json` (create in project root if needed):
```json
{
"regions": ["iad1"]
}
```
Choose a region close to your Supabase database for lower latency.
### Monitor with Vercel Analytics
Enable Vercel Analytics in your project settings for performance monitoring. MakerKit is compatible with Vercel's built-in analytics.
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "What's the cost of hosting on Vercel?", "answer": "Vercel's Hobby tier is free and works for personal projects and low-traffic apps. The Pro tier ($20/month) adds team features, more bandwidth, and commercial use rights. Most MakerKit apps start on Hobby and upgrade when they get traction."},
{"question": "How do I handle preview deployments with Supabase?", "answer": "Preview deployments get unique URLs that won't match your Supabase redirect URLs. Add a wildcard pattern like 'https://*-yourproject.vercel.app/auth/callback**' to your Supabase Redirect URLs, or create a separate Supabase project for staging."},
{"question": "Why is my build failing with environment variable errors?", "answer": "MakerKit validates environment variables at build time using Zod schemas. Check the build logs for the specific variable name, add it in Vercel's Environment Variables settings, and redeploy. Variables prefixed with NEXT_PUBLIC_ must be set before building."},
{"question": "How do I deploy multiple apps from the monorepo?", "answer": "Create separate Vercel projects for each app. Set the Root Directory to the app's path (e.g., 'apps/admin') and customize the build command to target that specific app with Turborepo's filter flag."}
]
/%}
---
## Next Steps
- [Environment Variables Reference](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable list with Zod validation
- [Supabase Configuration](/docs/next-supabase-turbo/going-to-production/supabase): Database, RLS policies, and webhook setup
- [Cloudflare Deployment](/docs/next-supabase-turbo/going-to-production/cloudflare): Alternative deployment with Edge runtime
- [Monitoring Setup](/docs/next-supabase-turbo/monitoring/overview): Error tracking with Sentry or PostHog

View File

@@ -0,0 +1,418 @@
---
status: "published"
title: "Deploy Next.js Supabase to a VPS"
label: "Deploy to VPS"
order: 9
description: "Deploy your MakerKit Next.js Supabase application to a VPS like Digital Ocean, Hetzner, or Linode. Covers server setup, Docker deployment, Nginx, and SSL configuration."
---
Deploy your MakerKit Next.js 16 application to a Virtual Private Server (VPS) for full infrastructure control and predictable costs. This guide covers Ubuntu server setup, Docker deployment, Nginx reverse proxy, and Let's Encrypt SSL. The steps work with Digital Ocean, Hetzner, Linode, Vultr, and other VPS providers.
## Overview
| Step | Purpose |
|------|---------|
| Create VPS | Provision your server |
| Install dependencies | Docker, Node.js, Nginx |
| Deploy application | Using Docker or direct build |
| Configure Nginx | Reverse proxy and SSL |
| Set up SSL | HTTPS with Let's Encrypt |
---
## Prerequisites
Before starting:
1. [Set up Supabase](/docs/next-supabase-turbo/going-to-production/supabase) project
2. [Generate environment variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables)
3. Domain name pointing to your VPS IP address
---
## Step 1: Create Your VPS
### Digital Ocean
1. Go to [Digital Ocean](https://www.digitalocean.com/)
2. Click **Create Droplet**
3. Choose:
- **OS**: Ubuntu 24.04 LTS
- **Plan**: Basic ($12/month minimum recommended for building)
- **Region**: Close to your users and Supabase instance
4. Add your SSH key for secure access
5. Create the Droplet
### Recommended Specifications
| Use Case | RAM | CPU | Storage |
|----------|-----|-----|---------|
| Building on VPS | 4GB+ | 2 vCPU | 50GB |
| Running only (pre-built image) | 2GB | 1 vCPU | 25GB |
| Production with traffic | 4GB+ | 2 vCPU | 50GB |
---
## Step 2: Initial Server Setup
SSH into your server:
```bash
ssh root@your-server-ip
```
### Update System
```bash
apt update && apt upgrade -y
```
### Install Docker
Follow the [official Docker installation guide](https://docs.docker.com/engine/install/ubuntu/) or run:
```bash
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Set up repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```
### Configure Firewall
```bash
# Allow SSH
ufw allow 22
# Allow HTTP and HTTPS
ufw allow 80
ufw allow 443
# Allow app port (if not using Nginx)
ufw allow 3000
# Enable firewall
ufw enable
```
---
## Step 3: Deploy Your Application
Choose one of two approaches:
### Option A: Pull Pre-Built Docker Image (Recommended)
If you built and pushed your image to a container registry (see [Docker guide](/docs/next-supabase-turbo/going-to-production/docker)):
```bash
# Login to registry
docker login ghcr.io
# Pull your image
docker pull ghcr.io/YOUR_USERNAME/myapp:latest
# Create env file
nano .env.production.local
# Paste your environment variables
# Run container
docker run -d \
-p 3000:3000 \
--env-file .env.production.local \
--name myapp \
--restart unless-stopped \
ghcr.io/YOUR_USERNAME/myapp:latest
```
### Option B: Build on VPS
For VPS with enough resources (4GB+ RAM):
#### Install Node.js and pnpm
```bash
# Install nvm (check https://github.com/nvm-sh/nvm for latest version)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Load nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# Install Node.js (LTS version)
nvm install --lts
# Install pnpm
npm install -g pnpm
```
#### Clone and Build
```bash
# Create Personal Access Token on GitHub with repo access
# Clone repository
git clone https://<YOUR_GITHUB_PAT>@github.com/YOUR_USERNAME/your-repo.git
cd your-repo
# Install dependencies
pnpm install
# Generate Dockerfile
pnpm run turbo gen docker
# Create env file
cp turbo/generators/templates/env/.env.local apps/web/.env.production.local
nano apps/web/.env.production.local
# Edit with your production values
# Build Docker image
docker build -t myapp:latest .
# Run container
docker run -d \
-p 3000:3000 \
--env-file apps/web/.env.production.local \
--name myapp \
--restart unless-stopped \
myapp:latest
```
{% alert type="warning" title="Memory during build" %}
If the build fails with memory errors, increase your VPS size temporarily or build locally and push to a registry.
{% /alert %}
---
## Step 4: Configure Nginx
Install Nginx as a reverse proxy:
```bash
apt install -y nginx
```
### Create Nginx Configuration
```bash
nano /etc/nginx/sites-available/myapp
```
Add:
```
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400;
}
}
```
### Enable the Site
```bash
# Create symlink
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
# Remove default site
rm /etc/nginx/sites-enabled/default
# Test configuration
nginx -t
# Restart Nginx
systemctl restart nginx
```
---
## Step 5: Set Up SSL with Let's Encrypt
Install Certbot:
```bash
apt install -y certbot python3-certbot-nginx
```
Obtain SSL certificate:
```bash
certbot --nginx -d yourdomain.com -d www.yourdomain.com
```
Certbot automatically:
1. Obtains the certificate
2. Updates Nginx configuration
3. Sets up auto-renewal
Verify auto-renewal:
```bash
certbot renew --dry-run
```
---
## Step 6: Post-Deployment Configuration
### Update Supabase URLs
In Supabase Dashboard (**Authentication > URL Configuration**):
| Field | Value |
|-------|-------|
| Site URL | `https://yourdomain.com` |
| Redirect URLs | `https://yourdomain.com/auth/callback**` |
### Configure Webhooks
Point your webhooks to your new domain:
- **Supabase DB webhook**: `https://yourdomain.com/api/db/webhook`
- **Stripe webhook**: `https://yourdomain.com/api/billing/webhook`
- **Lemon Squeezy webhook**: `https://yourdomain.com/api/billing/webhook`
---
## Monitoring and Maintenance
### View Logs
```bash
# Docker logs
docker logs -f myapp
# Nginx access logs
tail -f /var/log/nginx/access.log
# Nginx error logs
tail -f /var/log/nginx/error.log
```
### Restart Application
```bash
docker restart myapp
```
### Update Application
```bash
# Pull new image
docker pull ghcr.io/YOUR_USERNAME/myapp:latest
# Stop old container
docker stop myapp
docker rm myapp
# Start new container
docker run -d \
-p 3000:3000 \
--env-file .env.production.local \
--name myapp \
--restart unless-stopped \
ghcr.io/YOUR_USERNAME/myapp:latest
```
### Automated Updates with Watchtower (Optional)
Auto-update containers when new images are pushed:
```bash
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--interval 300 \
myapp
```
---
## Troubleshooting
### Application not accessible
1. Check Docker container is running: `docker ps`
2. Check firewall allows port 3000: `ufw status`
3. Check Nginx is running: `systemctl status nginx`
4. Check Nginx config: `nginx -t`
### SSL certificate issues
1. Ensure DNS is properly configured
2. Wait for DNS propagation (up to 48 hours)
3. Check Certbot logs: `cat /var/log/letsencrypt/letsencrypt.log`
### Container keeps restarting
Check logs for errors:
```bash
docker logs myapp
```
Common causes:
- Missing environment variables
- Database connection issues
- Port conflicts
### High memory usage
Monitor with:
```bash
docker stats
```
Consider:
1. Increasing VPS size
2. Configuring memory limits on container
3. Enabling swap space
---
## Cost Comparison
| Provider | Basic VPS | Notes |
|----------|-----------|-------|
| Digital Ocean | $12/month | Good documentation |
| Hetzner | $4/month | Best value, EU-based |
| Linode | $12/month | Owned by Akamai |
| Vultr | $12/month | Good global coverage |
---
{% faq
title="Frequently Asked Questions"
items=[
{"question": "Which VPS provider should I choose?", "answer": "Hetzner offers the best value at $4-5/month for a capable server. Digital Ocean has better documentation and a simpler interface at $12/month. Choose based on your region needs and whether you value cost or convenience."},
{"question": "How much RAM do I need?", "answer": "2GB RAM is minimum for running a pre-built Docker container. 4GB+ is needed if building on the VPS itself. For production with traffic, 4GB provides headroom for spikes. Monitor usage and scale up if you see memory pressure."},
{"question": "Do I need Nginx if I'm using Docker?", "answer": "Yes, for production. Nginx handles SSL termination, serves static files efficiently, and provides a buffer between the internet and your app. It also enables zero-downtime deployments by proxying to new containers while the old ones drain."},
{"question": "Is VPS cheaper than Vercel?", "answer": "For low traffic, Vercel's free tier is cheaper. For high traffic or predictable workloads, VPS is often cheaper. A $12/month Digital Ocean droplet handles more requests than Vercel's Pro tier at $20/month, but you manage everything yourself."}
]
/%}
---
## Next Steps
- [Docker Deployment](/docs/next-supabase-turbo/going-to-production/docker): Build and push Docker images with CI/CD
- [Monitoring Setup](/docs/next-supabase-turbo/monitoring/overview): Add Sentry or PostHog for error tracking
- [Environment Variables](/docs/next-supabase-turbo/going-to-production/production-environment-variables): Complete variable reference