Skip to content

Instantly share code, notes, and snippets.

@xmlking
Created November 8, 2025 19:43
Show Gist options
  • Select an option

  • Save xmlking/4d519bc1cf9b203f526bd04e467d2041 to your computer and use it in GitHub Desktop.

Select an option

Save xmlking/4d519bc1cf9b203f526bd04e467d2041 to your computer and use it in GitHub Desktop.
NextJS turborepo bun Dockerfile
FROM oven/bun:1-alpine AS base
# Install turbo globally
RUN bun add -g turbo@canary
# Prune stage - create a subset of monorepo for this app
FROM base AS pruner
WORKDIR /app
COPY . .
RUN turbo prune web --docker
# Install dependencies and build
FROM base AS builder
WORKDIR /app
# First install dependencies (as they change less often)
COPY --from=pruner /app/out/json/ .
RUN --mount=type=cache,target=/root/.bun/install/cache bun ci --ignore-scripts
# Copy pruned source code and build
COPY --from=pruner /app/out/full/ .
## Uncomment and use build args to enable remote caching
ARG TURBO_TEAM
ENV TURBO_TEAM=$TURBO_TEAM
ARG TURBO_TOKEN
ENV TURBO_TOKEN=$TURBO_TOKEN
ENV TURBO_CACHE_DIR=/root/.cache/turbo
ENV NEXT_TELEMETRY_DISABLED=1
RUN --mount=type=cache,target=${TURBO_CACHE_DIR} \
turbo build --filter=web
# Production stage
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
NEXT_MANUAL_SIG_HANDLE=true \
PORT=3000 \
HOSTNAME="0.0.0.0"
# Copy built application
COPY --from=builder /app/apps/web/.next/standalone ./
COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder /app/apps/web/public ./apps/web/public
USER bun
EXPOSE 3000/tcp
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:3000 || exit 1
ENTRYPOINT [ "bun", "apps/web/server.js" ]
@xmlking
Copy link
Author

xmlking commented Nov 8, 2025

for graceful shutdown

apps/web/src/instrumentation.ts

import { registerOTel } from "@vercel/otel";

export function register() {
  registerOTel("myapp");

  // HINT: // https://github.com/vercel/next.js/issues/51404
  // Make sure commands gracefully respect termination signals (e.g. from Docker)
  // Allow the graceful termination to be manually configurable
  if (
    process.env.NEXT_RUNTIME === "nodejs" &&
    process.env.NEXT_MANUAL_SIG_HANDLE === "true"
  ) {
    process.on("SIGTERM", async () => {
      console.info("Received SIGTERM: ", "starting graceful shutdown");
      // TODO: gracefully shutdown any resources
      await shutdown();
    });
    process.on("SIGINT", async () => {
      console.info("Received SIGINT: ", "starting graceful shutdown");
      await shutdown();
    });
  }
}

async function shutdown() {
  const fs = await import("node:fs");
  fs.writeFileSync("/tmp/shutdown", "1");

  setTimeout(() => {
    process.exit(0);
  }, 20 * 1000); // 20 secs shutdown timeout
}

@xmlking
Copy link
Author

xmlking commented Nov 8, 2025

Skip authentication for /healthz

apps/web/src/proxy.ts

import { type NextRequest, NextResponse } from "next/server";

const publicRoutes = /^\/($|about$|terms$|privacy$|blog(\/.*)?$)/;
 
 
export async function proxy(request: NextRequest) {
  const { pathname, hostname, search } = request.nextUrl;

  // Do not implement middleware for the public path
  if (publicRoutes.test(pathname)) {
    return NextResponse.next();
  }

  // Check if the request is for the health endpoint and from localhost
  if (
    pathname.startsWith("/healthz") &&
    (hostname === "localhost" || hostname === "127.0.0.1")
  ) {
    return NextResponse.next(); // Skip authentication
  }

  ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment