Skip to main content

Command Palette

Search for a command to run...

Fix Build & Lint Issues in Docker: Deploying Next.js 15 with Supabase Safely

Published
3 min read
A

A Simple Web Developer & Designer

Deploying a modern Next.js app with Supabase can be tricky, especially when you want strict TypeScript and ESLint checks in your CI/CD pipeline. Here’s a guide based on real-world fixes that ensure production safety without skipping errors.


Common Problems

  1. Docker builds fail due to ESLint / TypeScript errors

    • Often developers skip build checks, hiding real issues.
  2. Supabase middleware auth issues

    • Using session objects directly can be insecure.

    • Deprecated packages like @supabase/auth-helpers-nextjs cause runtime errors.

  3. Static export errors

    • Errors like Objects are not valid as React child when rendering dynamic content.
  4. Native module build errors

    • Packages like sharp or bcrypt fail on Alpine images.

How to Fix

1. Enforce ESLint & TypeScript in Docker

# Install dependencies
RUN pnpm install --frozen-lockfile

# Run lint & type checks before build
RUN pnpm run lint
RUN pnpm exec tsc --noEmit

# Build Next.js app
RUN pnpm run build

Benefit: CI/CD fails early if there are errors, preventing runtime bugs.


2. Use Node 20 Bullseye Slim for Docker

FROM node:20-bullseye-slim AS builder
  • Ensures smooth builds for native modules like sharp and esbuild.

3. Force dynamic pages to avoid static export errors

// next.config.js
const nextConfig = {
  dynamicParams: true,
  dynamicIO: true,
  generateStaticParams: async () => [],
};
export default nextConfig;
  • Prevents common errors when prerendering pages with dynamic data.

4. Use secure Supabase server-side auth

import { NextResponse } from "next/server";
import { createServerClient } from "@supabase/ssr";

export async function middleware(req) {
  const res = NextResponse.next();
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    { cookies: { getAll: () => [], setAll: () => {} } }
  );

  const { data: { session } } = await supabase.auth.getSession();
  const role = session?.user.user_metadata?.role || "user";

  if (!session) return NextResponse.redirect("/login");
  if (role !== "admin" && req.nextUrl.pathname.startsWith("/admin")) {
    return NextResponse.redirect("/");
  }

  return res;
}

Benefit: Server-verified sessions and RBAC without insecure client hacks.


5. Clean Docker Production Image

  • Prune dev dependencies:
RUN pnpm prune --prod
  • Copy .next, public, and node_modules from builder to runner.

  • Use non-root user for security.


6. Deploy to Serverless Platform (Cloud Run Example)

gcloud run deploy my-next-app \
  --image <PROJECT-ID>/<REPO>/app:v1.0.0 \
  --platform managed \
  --allow-unauthenticated \
  --port 3000 \
  --set-env-vars NEXT_PUBLIC_SUPABASE_URL=...,NEXT_PUBLIC_SUPABASE_ANON_KEY=...
  • Fully managed, scalable and HTTPS-ready deployment.

Key Takeaways

  1. Do not skip TypeScript or ESLint checks — enforce them in Docker for safer production builds.

  2. Node 20 Bullseye Slim ensures native modules compile correctly.

  3. Supabase server-side auth avoids insecure session usage.

  4. Dynamic pages in Docker prevent static export errors.

  5. Clean production image improves CI/CD reliability and security.