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:
committed by
GitHub
parent
4912e402a3
commit
7ebff31475
309
docs/going-to-production/authentication-emails.mdoc
Normal file
309
docs/going-to-production/authentication-emails.mdoc
Normal 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>© 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
|
||||
326
docs/going-to-production/authentication.mdoc
Normal file
326
docs/going-to-production/authentication.mdoc
Normal 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
|
||||
338
docs/going-to-production/checklist.mdoc
Normal file
338
docs/going-to-production/checklist.mdoc
Normal 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
|
||||
342
docs/going-to-production/cloudflare.mdoc
Normal file
342
docs/going-to-production/cloudflare.mdoc
Normal 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
|
||||
388
docs/going-to-production/docker.mdoc
Normal file
388
docs/going-to-production/docker.mdoc
Normal 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
|
||||
369
docs/going-to-production/production-environment-variables.mdoc
Normal file
369
docs/going-to-production/production-environment-variables.mdoc
Normal 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
|
||||
348
docs/going-to-production/supabase.mdoc
Normal file
348
docs/going-to-production/supabase.mdoc
Normal 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
|
||||
309
docs/going-to-production/vercel.mdoc
Normal file
309
docs/going-to-production/vercel.mdoc
Normal 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
|
||||
418
docs/going-to-production/vps.mdoc
Normal file
418
docs/going-to-production/vps.mdoc
Normal 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
|
||||
Reference in New Issue
Block a user