Deploy Next.js Supabase with Docker
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:
- Docker installed locally
- Set up Supabase project
- Generate environment variables
Step 1: Generate the Dockerfile
MakerKit provides a generator that creates an optimized Dockerfile and configures Next.js for standalone output:
pnpm run turbo gen dockerThis command:
- Creates a
Dockerfilein the project root - Sets
output: "standalone"innext.config.mjs - Installs platform-specific dependencies for Tailwind CSS
Architecture-specific dependencies
The generator detects your CPU architecture (ARM64 or x64) and installs the correct Tailwind CSS and LightningCSS binaries for Linux builds.
Step 2: Configure Environment Variables
Create the Production Environment File
Generate your environment variables:
pnpm turbo gen envCopy 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):
NEXT_PUBLIC_SITE_URL=https://yourdomain.comNEXT_PUBLIC_PRODUCT_NAME=MyAppNEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.coNEXT_PUBLIC_SUPABASE_PUBLIC_KEY=eyJ...NEXT_PUBLIC_BILLING_PROVIDER=stripeNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...Runtime secrets (add only when running container):
SUPABASE_SECRET_KEY=eyJ...STRIPE_SECRET_KEY=sk_live_...STRIPE_WEBHOOK_SECRET=whsec_...SUPABASE_DB_WEBHOOK_SECRET=your-secretRESEND_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:
- Open
apps/web/.env.production.local - Comment out or remove these lines:# SUPABASE_SECRET_KEY=...# STRIPE_SECRET_KEY=...# STRIPE_WEBHOOK_SECRET=...# SUPABASE_DB_WEBHOOK_SECRET=...# RESEND_API_KEY=...# CAPTCHA_SECRET_TOKEN=...
- Save the file
- 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)
docker buildx build --platform linux/amd64 -t myapp:latest .For ARM64 (Apple Silicon, AWS Graviton)
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:
SUPABASE_SECRET_KEY=eyJ...STRIPE_SECRET_KEY=sk_live_...STRIPE_WEBHOOK_SECRET=whsec_...SUPABASE_DB_WEBHOOK_SECRET=your-secretRESEND_API_KEY=re_...CAPTCHA_SECRET_TOKEN=...Step 5: Run the Container
Local Testing
Test the image locally:
docker run -d \ -p 3000:3000 \ --env-file apps/web/.env.production.local \ myapp:latestAccess 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
- Create a Personal Access Token with
write:packagesscope - Login:docker login ghcr.io -u YOUR_USERNAME
- Tag your image:docker tag myapp:latest ghcr.io/YOUR_USERNAME/myapp:latest
- Push:docker push ghcr.io/YOUR_USERNAME/myapp:latest
DockerHub
- Login:docker login
- Tag your image:docker tag myapp:latest YOUR_USERNAME/myapp:latest
- Push:docker push YOUR_USERNAME/myapp:latest
Step 7: Deploy to Production
Pull and Run on Your Server
SSH into your server and run:
# Login to registry (GitHub example)docker login ghcr.io# Pull the imagedocker pull ghcr.io/YOUR_USERNAME/myapp:latest# Run the containerdocker run -d \ -p 3000:3000 \ --env-file .env.production.local \ --name myapp \ --restart unless-stopped \ ghcr.io/YOUR_USERNAME/myapp:latestUsing Docker Compose
Create docker-compose.yml:
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: 3Run with:
docker compose up -dCI/CD with GitHub Actions
Automate builds and deployments with GitHub Actions:
# .github/workflows/docker.ymlname: Build and Push Docker Imageon: 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=maxProduction Considerations
Health Checks
MakerKit includes a health check endpoint. Use it for monitoring:
curl http://localhost:3000/api/healthcheckResource Limits
Set memory and CPU limits in production:
docker run -d \ -p 3000:3000 \ --memory="512m" \ --cpus="1.0" \ --env-file .env.production.local \ myapp:latestLogging
View container logs:
# Follow logsdocker logs -f myapp# Last 100 linesdocker logs --tail 100 myappTroubleshooting
Build fails with memory error
Increase Docker's memory allocation or use a more powerful build machine:
docker build --memory=4g -t myapp:latest .Container exits immediately
Check logs for errors:
docker logs myappCommon causes:
- Missing environment variables
- Port already in use
- Invalid configuration
Image too large
The standalone output mode creates smaller images. If still too large:
- Ensure you're using the generated Dockerfile (not a custom one)
- Check for unnecessary files in your project
- Use
.dockerignoreto exclude development files
Environment variables not working
- Verify the env file path is correct
- Check file permissions
- Ensure no syntax errors in the env file
- For
NEXT_PUBLIC_variables, rebuild the image (they're embedded at build time)
Next Steps
- VPS Deployment: Deploy Docker containers to Digital Ocean, Hetzner, or Linode
- Environment Variables: Complete variable reference with secrets management
- Monitoring Setup: Add Sentry or PostHog for error tracking