Update Dockerfile - #401
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughDockerfile updated to pin Bun 1.3.5-alpine across deps, builder, and runner stages; multi-stage build preserved (deps → builder → runner). Builds Next.js standalone with Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||
There was a problem hiding this comment.
Key concerns are production reproducibility (the switch to oven/bun:alpine floating tags), and runtime robustness (removal of tini/init behavior). There’s also a compatibility risk in changing the runtime from node to bun without a clear guarantee that the generated server.js works identically under Bun. Finally, the new HEALTHCHECK hard-codes an endpoint that may not exist, which can cause restart loops in orchestration.
Summary of changes
Dockerfile updates
- Switched all stages from a pinned Bun base image (
oven/bun:1.1.3-alpine) to a floating tag (oven/bun:alpine). - Tightened dependency installation by using
bun install --frozen-lockfile. - Reworked the runtime stage to run as a non-root user (
nextjs) and removed the previousapk addofnodejs,tini, andcurl. - Adjusted artifact copying for a Next.js standalone output:
/.next/standalone→.//.next/static→./.next/static/public→./public
- Replaced the
curl-basedHEALTHCHECKwith a Bunfetch()check. - Changed the container start command from
node server.jstobun server.js.
| # Uses latest stable Bun with Alpine for smaller images and better compatibility (as of January 2026) | ||
|
|
||
| # Stage 1: Install dependencies | ||
| FROM oven/bun:alpine AS deps |
There was a problem hiding this comment.
Using a floating base tag (oven/bun:alpine) undermines the stated goal of reproducibility. A new Bun release (or Alpine refresh) can break builds or subtly change runtime behavior, and the comment suggests “stable as of January 2026,” which won’t remain true. This is especially risky for production images and CI/CD.
Suggestion
Pin the image to an explicit Bun (and ideally digest) version to make builds reproducible. For example:
FROM oven/bun:1.1.3-alpine@sha256:<digest> AS deps
...
FROM oven/bun:1.1.3-alpine@sha256:<digest> AS builder
...
FROM oven/bun:1.1.3-alpine@sha256:<digest> AS runnerIf you want “latest” in dev, consider making it a build arg (default pinned) so production stays stable.
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| # Stage 3: Production runtime (minimal image running with Bun) | ||
| FROM oven/bun:alpine AS runner | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install only necessary runtime dependencies | ||
| RUN apk add --no-cache \ | ||
| nodejs \ | ||
| tini \ | ||
| curl \ | ||
| && addgroup -g 1001 -S nodejs \ | ||
| # Create non-root user for security | ||
| RUN addgroup -g 1001 -S nodejs \ | ||
| && adduser -S nextjs -u 1001 | ||
|
|
||
| # Set production environment | ||
| # Environment variables | ||
| ENV NODE_ENV=production | ||
| ENV NEXT_TELEMETRY_DISABLED=1 | ||
| ENV PORT=3000 | ||
| ENV HOSTNAME="0.0.0.0" | ||
|
|
||
| # Copy built application from builder | ||
| # Copy standalone build output | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ | ||
| # Copy static assets | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static | ||
| # Copy public folder | ||
| COPY --from=builder --chown=nextjs:nodejs /app/public ./public | ||
|
|
||
| # Switch to non-root user for security | ||
| # Switch to non-root user | ||
| USER nextjs | ||
|
|
||
| # Expose the application port | ||
| # Expose port | ||
| EXPOSE 3000 | ||
|
|
||
| # Health check for container orchestration | ||
| # Health check (adjust path if your app has a different health endpoint) | ||
| HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ | ||
| CMD curl -f http://localhost:3000/api/health || exit 1 | ||
|
|
||
| # Use tini as init system to handle signals properly | ||
| ENTRYPOINT ["/sbin/tini", "--"] | ||
| CMD ["bun", "--eval", "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"] | ||
|
|
||
| # Start the application | ||
| CMD ["node", "server.js"] | ||
| # Run with Bun (excellent Node.js compatibility, faster cold starts) | ||
| CMD ["bun", "server.js"] |
There was a problem hiding this comment.
The runtime stage removed tini and the ENTRYPOINT that handled signal forwarding/reaping. Without an init process, PID 1 signal handling and zombie reaping can be problematic in Kubernetes/Cloud Run, leading to slow shutdowns or leaked processes. Bun/Node apps can still benefit from an init wrapper.
Suggestion
Reintroduce an init process. Simplest is Docker's built-in init at runtime (docker run --init), but for portability bake it in:
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["bun", "server.js"]Alternatively, if you deliberately don’t want tini, document why and ensure the app handles SIGTERM/SIGINT correctly.
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| # Run with Bun (excellent Node.js compatibility, faster cold starts) | ||
| CMD ["bun", "server.js"] |
There was a problem hiding this comment.
Switching the runtime command from node to bun may break if your standalone output/server expects Node semantics or relies on Node-only flags/behaviors. If this image is meant to run Next.js standalone output, the typical entry is node server.js; running with Bun should be validated explicitly (especially around process, streams, and edge cases in HTTP handling).
Suggestion
Either keep Node for the runtime command (most compatible):
# install node only in runner if required
RUN apk add --no-cache nodejs
CMD ["node", "server.js"]Or, if you want Bun runtime, add a short compatibility note and ensure CI includes a container smoke test hitting a route.
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| # Health check (adjust path if your app has a different health endpoint) | ||
| HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ | ||
| CMD curl -f http://localhost:3000/api/health || exit 1 | ||
|
|
||
| # Use tini as init system to handle signals properly | ||
| ENTRYPOINT ["/sbin/tini", "--"] | ||
| CMD ["bun", "--eval", "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"] |
There was a problem hiding this comment.
The new HEALTHCHECK assumes /api/health exists and that Bun’s fetch works in this environment. If the endpoint doesn’t exist, orchestration will mark the container unhealthy and potentially restart-loop. Also, this healthcheck no longer verifies that the actual app process is responding if server.js changes the port/hostname behavior.
Suggestion
Make the healthcheck endpoint configurable and/or default to / unless you guarantee /api/health exists. Example:
ENV HEALTHCHECK_PATH=/api/health
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD ["bun","--eval","fetch('http://127.0.0.1:'+process.env.PORT+process.env.HEALTHCHECK_PATH).then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| # Run with Bun (excellent Node.js compatibility, faster cold starts) | ||
| CMD ["bun", "server.js"] |
There was a problem hiding this comment.
CMD ["bun", "server.js"] assumes the standalone output contains server.js at the container workdir root. That’s true for some Next.js standalone outputs, but it’s easy to accidentally break if the build output changes (e.g., Next.js version/config changes) because the Dockerfile doesn’t validate the presence/path of the entrypoint.
Suggestion
Harden the runtime startup by either (a) explicitly copying and referencing the known entrypoint path, or (b) adding a small sanity check during build.
Option A (explicit, if your standalone output provides it):
CMD ["bun", "./server.js"]Option B (fail fast during image build):
RUN test -f ./server.jsReply with "@CharlieHelps yes please" if you'd like me to add a commit with a safe, fail-fast check for the entrypoint.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @Dockerfile:
- Around line 39-40: The Dockerfile creates the nodejs group and the nextjs user
but never adds nextjs to nodejs; update the user creation step so nextjs is a
member of nodejs (e.g., use adduser with the group flag or a separate
usermod/adduser to add to group) so subsequent chown to nextjs:nodejs grants the
intended group access; target the RUN lines that call addgroup -g 1001 -S nodejs
and adduser -S nextjs -u 1001 and modify the adduser invocation to include the
nodejs group membership.
- Around line 48-53: The Dockerfile is copying Next.js standalone output (the
COPY --from=builder ... /app/.next/standalone ./ and related static/public
COPYs) but then attempts to run it with Bun via CMD ["bun","server.js"], which
is incompatible; fix by either (A) removing the standalone copy and switching to
a Bun-friendly start (e.g., use bun run start and ensure build artifacts are
produced for Bun), or (B) change the runtime base image to a Node.js image and
keep the standalone bundle (so CMD runs with node server.js), or (C) adopt a
Bun-compatible deployment path; update the Dockerfile to implement one of these
options and adjust the COPY/ENTRYPOINT logic accordingly and re-test.
- Line 5: Pin the Bun base image to a fixed version for reproducible builds:
replace all occurrences of "FROM oven/bun:alpine" used across the three build
stages (including the stage using "AS deps" and the other two FROM lines around
lines 16 and 34) with the explicit tag "oven/bun:1.3.5-alpine" so every stage
uses the same pinned Bun v1.3.5-alpine base image.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
Dockerfile
🔇 Additional comments (4)
Dockerfile (4)
9-13: LGTM!Good use of
--frozen-lockfilefor reproducible dependency installation.
65-66: LGTM!Using
bun server.jsfor the standalone Next.js server is appropriate given the Bun-based image.
23-24: A.dockerignorefile already exists with comprehensive exclusions including dependencies, build artifacts, environment files, git files, and documentation. No action needed.
61-63: The/api/healthendpoint is already properly implemented and returns a 200 OK status, so the HEALTHCHECK command will work correctly. No changes needed.
| # Copy standalone build output | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ | ||
| # Copy static assets | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static | ||
| # Copy public folder | ||
| COPY --from=builder --chown=nextjs:nodejs /app/public ./public |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "Dockerfile" -type f | head -20Repository: QueueLab/QCX
Length of output: 68
🌐 Web query:
Bun compatibility with Next.js standalone output 2025
💡 Result:
Short answer: Partial — Bun can run Next.js (dev and some production flows) but Next.js "output: 'standalone'" is designed for Node and is not guaranteed to work directly under Bun without adjustments; there are community reports and issues when trying to run the .next/standalone server with Bun. Test and/or prefer officially supported Bun runtimes (e.g., Vercel’s Bun option) or run the standalone bundle on Node. [1][2][3][4]
What the sources show
- Bun’s docs/guides show official instructions to run Next.js with Bun (dev and production runs using Bun’s runtime). This demonstrates Bun-level support for Next.js workflows. [1]
- Next.js docs: the standalone output produces a minimal Node-oriented server (.next/standalone/server.js) and traced NFT.json files intended for Node deployments. That output assumes Node module/load/runtime semantics. [2]
- Community/issue reports show problems when invoking the standalone server in non-Node environments (errors about missing Node polyfills, deep path/tracing issues when built with Bun, and other runtime incompatibilities). [3][4]
Practical guidance
- If you want to use Bun: follow Bun’s Next.js guide and run with Bun’s runtime (bun run dev / bun run start) and verify your app (APIs, native modules, node: polyfills). Test thoroughly in staging. [1]
- If you’re deploying a Next.js standalone build (.next/standalone): treat it as a Node-targeted artifact — run it on Node for the most predictable result. [2]
- If deploying to Vercel and want Bun: Vercel has an official Bun runtime option (public beta / rollout) that lets Next.js run on Bun under their platform — this is the safest path to run Next on Bun in production without manually juggling the standalone artifact. [5]
If you want, I can:
- Check a specific Next.js version + your app’s config for known incompatibilities, or
- Try steps to run a simple standalone build under Bun and report exact errors and fixes.
🏁 Script executed:
cat -n ./DockerfileRepository: QueueLab/QCX
Length of output: 2350
Next.js standalone output is not compatible with Bun runtime. Lines 49–53 copy the Next.js standalone bundle and line 66 attempts to run it with Bun (CMD ["bun", "server.js"]), but Next.js standalone is designed for Node.js and has known incompatibilities when executed on Bun—including missing Node polyfills and runtime compatibility issues.
Either (1) switch to bun run start and remove the standalone copy pattern, (2) switch the runtime base image to Node.js, or (3) use a Bun-compatible deployment option (e.g., Vercel's official Bun runtime). Test thoroughly before deploying.
🤖 Prompt for AI Agents
In @Dockerfile around lines 48 - 53, The Dockerfile is copying Next.js
standalone output (the COPY --from=builder ... /app/.next/standalone ./ and
related static/public COPYs) but then attempts to run it with Bun via CMD
["bun","server.js"], which is incompatible; fix by either (A) removing the
standalone copy and switching to a Bun-friendly start (e.g., use bun run start
and ensure build artifacts are produced for Bun), or (B) change the runtime base
image to a Node.js image and keep the standalone bundle (so CMD runs with node
server.js), or (C) adopt a Bun-compatible deployment path; update the Dockerfile
to implement one of these options and adjust the COPY/ENTRYPOINT logic
accordingly and re-test.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @Dockerfile:
- Around line 1-2: Decide and make the version strategy consistent: either
update the PR description to state that the Dockerfile intentionally pins Bun to
"1.3.5-alpine" for reproducible builds, or change the FROM line to use the
floating tag "oven/bun:alpine" if you want to track the latest stable; mention
the exact tag text ("1.3.5-alpine" or "oven/bun:alpine") in your update so
reviewers know which approach you chose.
- Around line 65-66: The container runs Bun as PID 1 (CMD ["bun","server.js"])
but lacks graceful shutdown handling; either add SIGTERM/SIGINT handlers inside
server.js to close resources and call server.close() then process.exit(0)
(reference server.js, server.close, process.on("SIGTERM")/process.on("SIGINT")),
or modify the Dockerfile to install and use tini as init (add RUN to install
tini and set ENTRYPOINT ["/sbin/tini","--"] before CMD ["bun","server.js"]) so
signals are forwarded and shutdowns are graceful.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
Dockerfile
🔇 Additional comments (5)
Dockerfile (5)
38-40: LGTM: Non-root user configuration.Creating a non-root user with explicit UID/GID is a security best practice for container images.
48-53: Copy commands depend on standalone configuration.These COPY commands rely on
.next/standaloneexisting, which requiresoutput: 'standalone'innext.config.js(flagged separately). Assuming the configuration is correct, the file ownership and paths look appropriate.
30-31: > Likely an incorrect or invalid review comment.
5-5: No action needed.oven/bun:1.3.5-alpineis available on Docker Hub and ready for use.
61-63: Health check syntax is correct and requires no changes.The health check properly uses Bun's
--evalflag with fetch and promise chaining. According to Bun's documentation, the--evalflag supports promise chains, the fetch API is available in eval context, andprocess.exit()correctly returns exit codes to Docker. The endpoint/api/healthexists in the codebase and returns appropriate status responses. The logic correctly exits with code 0 on successful health check and code 1 on any failure.
| # Multi-stage Dockerfile for QCX - Optimized for Google Cloud Build and Production | ||
| # Uses pinned Bun v1.3.5-alpine for reproducible builds (January 2026) |
There was a problem hiding this comment.
Clarify version pinning strategy.
The PR summary states the goal is to use oven/bun:alpine (latest stable), but the implementation pins 1.3.5-alpine. Pinning provides reproducibility, but conflicts with the stated objective of using "latest stable."
Please clarify:
- If pinning is intentional, the PR description should be updated to reflect this decision.
- If the goal is truly to track latest, consider using
oven/bun:alpinewithout a version pin (though pinning is generally recommended for production).
🤖 Prompt for AI Agents
In @Dockerfile around lines 1 - 2, Decide and make the version strategy
consistent: either update the PR description to state that the Dockerfile
intentionally pins Bun to "1.3.5-alpine" for reproducible builds, or change the
FROM line to use the floating tag "oven/bun:alpine" if you want to track the
latest stable; mention the exact tag text ("1.3.5-alpine" or "oven/bun:alpine")
in your update so reviewers know which approach you chose.
| # Multi-stage Dockerfile for QCX - Optimized for Google Cloud Build and Production | ||
| # Uses pinned Bun v1.3.5-alpine for reproducible builds (January 2026) | ||
|
|
||
| # Stage 1: Install dependencies | ||
| FROM oven/bun:1.3.5-alpine AS deps | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Copy package files | ||
| COPY package.json bun.lock* ./ | ||
|
|
||
| # Install dependencies | ||
| # Install dependencies (frozen lockfile for reproducibility) | ||
| RUN bun install --frozen-lockfile | ||
|
|
||
| # Stage 2: Build stage | ||
| FROM oven/bun:1.1.3-alpine AS builder | ||
| # Stage 2: Build the Next.js application | ||
| FROM oven/bun:1.3.5-alpine AS builder | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Copy dependencies from deps stage | ||
| # Copy node_modules from deps stage | ||
| COPY --from=deps /app/node_modules ./node_modules | ||
|
|
||
| # Copy application source | ||
| # Copy source code | ||
| COPY . . | ||
|
|
||
| # Set build-time environment variables | ||
| # Disable telemetry during build | ||
| ENV NEXT_TELEMETRY_DISABLED=1 | ||
| ENV NODE_ENV=production | ||
|
|
||
| # Build the Next.js application | ||
| # Build the application (standalone output) | ||
| RUN bun run build | ||
|
|
||
| # Stage 3: Production runtime | ||
| FROM oven/bun:1.1.3-alpine AS runner | ||
| # Stage 3: Production runtime (minimal image running with Bun) | ||
| FROM oven/bun:1.3.5-alpine AS runner | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install only necessary runtime dependencies | ||
| RUN apk add --no-cache \ | ||
| nodejs \ | ||
| tini \ | ||
| curl \ | ||
| && addgroup -g 1001 -S nodejs \ | ||
| && adduser -S nextjs -u 1001 | ||
| # Create non-root group and user, with nextjs belonging to nodejs group | ||
| RUN addgroup -g 1001 -S nodejs \ | ||
| && adduser -S -u 1001 -G nodejs nextjs | ||
|
|
||
| # Set production environment | ||
| # Environment variables | ||
| ENV NODE_ENV=production | ||
| ENV NEXT_TELEMETRY_DISABLED=1 | ||
| ENV PORT=3000 | ||
| ENV HOSTNAME="0.0.0.0" | ||
|
|
||
| # Copy built application from builder | ||
| # Copy standalone build output (optimized for minimal size) | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ | ||
| # Copy static assets | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static | ||
| # Copy public folder | ||
| COPY --from=builder --chown=nextjs:nodejs /app/public ./public | ||
|
|
||
| # Switch to non-root user for security | ||
| # Switch to non-root user | ||
| USER nextjs | ||
|
|
||
| # Expose the application port | ||
| # Expose port | ||
| EXPOSE 3000 | ||
|
|
||
| # Health check for container orchestration | ||
| # Health check (uses Bun to fetch; adjust /api/health if your endpoint differs) | ||
| HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ | ||
| CMD curl -f http://localhost:3000/api/health || exit 1 | ||
|
|
||
| # Use tini as init system to handle signals properly | ||
| ENTRYPOINT ["/sbin/tini", "--"] | ||
| CMD ["bun", "--eval", "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"] | ||
|
|
||
| # Start the application | ||
| CMD ["node", "server.js"] | ||
| # Run the standalone server with Bun (fully compatible in Bun 1.3+) | ||
| CMD ["bun", "server.js"] |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Operational: Test Bun compatibility with Next.js standalone.
This migration from Node.js to Bun as the runtime for Next.js standalone is significant. While Bun 1.3+ advertises Node.js compatibility, thoroughly test:
- All Next.js features used in your app (API routes, middleware, ISR, etc.)
- Any native Node.js modules or dependencies that might have compatibility issues
- Performance characteristics (memory usage, request throughput, cold start times)
- Error handling and stack traces for debugging
Consider running parallel deployments initially to compare Node.js vs. Bun behavior before fully switching.
#!/bin/bash
# Description: Check for potential Node.js-specific dependencies that might have compatibility issues
# Look for dependencies that use native Node modules or might have Bun compatibility concerns
echo "Checking package.json for potentially problematic dependencies..."
# Check for native modules
jq -r '.dependencies // {} | to_entries[] | select(.key | test("^(node-|@node/)")) | .key' package.json 2>/dev/null || echo "No package.json found"
# Check for C++ addons or native dependencies
jq -r '.dependencies // {} | to_entries[] | select(.key | test("(bindings|nan|node-gyp|ffi)")) | .key' package.json 2>/dev/null
echo ""
echo "Review these dependencies for Bun compatibility at: https://bun.sh/docs/runtime/nodejs-apis"| # Run the standalone server with Bun (fully compatible in Bun 1.3+) | ||
| CMD ["bun", "server.js"] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find and inspect the Dockerfile to understand the full context
git ls-files | grep -i dockerfileRepository: QueueLab/QCX
Length of output: 66
🏁 Script executed:
# Check the full Dockerfile to understand setup around lines 65-66
fd -t f -e Dockerfile -e dockerfile | head -20Repository: QueueLab/QCX
Length of output: 38
🌐 Web query:
Bun signal handling SIGTERM SIGINT PID 1 docker
💡 Result:
Short answer: When Bun is PID 1 inside a container it will not be terminated by SIGINT/SIGTERM unless you explicitly handle those signals in your Bun app or run an init that forwards signals (tini/dumb-init / Docker --init). See Bun docs for registering handlers and use --init or an init binary if you don’t want to add handlers in-process. [1][2][3]
What to do (options):
-
Add explicit handlers in Bun (recommended if you want graceful shutdown):
process.on("SIGTERM", async () => {
// cleanup, close servers/connections
process.exit(0);
});
process.on("SIGINT", () => process.exit(0));(Bun docs demonstrate listening for SIGINT—use the same for SIGTERM). [1]
-
Run an init as PID 1 so Bun becomes a child and receives forwarded signals:
- docker run --init ... (uses tini)
- Or install/use tini/dumb-init as your ENTRYPOINT to forward/reap signals. [2][3]
-
If you use a shell entrypoint, use exec to replace the shell with Bun so Bun runs as the main process (but you still must register handlers if it is PID 1). [3]
References:
- Bun docs — listening for SIGINT / process.on. [1]
- tini (init for containers) — usage and signal forwarding. [2]
- Notes on PID 1 behaviour and options (exec, init, dumb-init). [3]
🌐 Web query:
Bun 1.3+ process signals graceful shutdown documentation
💡 Result:
Short summary — Bun 1.3+ supports process signals the same way Node does (process.on('SIGINT' / 'SIGTERM')), and Bun.Server exposes server.stop() which by default performs a graceful shutdown (waits for in‑flight requests & websockets). Typical pattern: listen for SIGINT/SIGTERM, perform cleanup (close DB, etc.), await server.stop(), then exit. [1][2][3]
Minimal example:
const server = Bun.serve({ fetch(req) { return new Response("ok"); } });
process.on("SIGTERM", async () => {
// cleanup (e.g. await db.close())
await server.stop(); // graceful: waits for in‑flight requests
process.exit(0);
});
process.on("SIGINT", () => process.kill(process.pid, "SIGTERM"));
Key docs:
- Listening for Ctrl+C / SIGINT in Bun. [1]
- Server.stop() (graceful by default; pass true to force-close). [2]
- Bun.serve / Server API (server.stop, server.unref/ref, reload, etc.). [3]
Sources:
[1] Bun guide — Listen for CTRL+C.
[2] Bun API — Server.stop.
[3] Bun API — Bun.serve / Server lifecycle.
🏁 Script executed:
cat -n DockerfileRepository: QueueLab/QCX
Length of output: 2418
🌐 Web query:
Node.js standalone server Next.js signal handling SIGTERM graceful shutdown
💡 Result:
Short answer
- For Next.js standalone (node .next/standalone/server.js) you should run a wrapper Node process that listens for SIGTERM/SIGINT, stops accepting new requests, waits for in-flight work to finish, closes DB/queues, then exits. Next.js also exposes a manual signal hook (NEXT_MANUAL_SIG_HANDLE) for adding shutdown handlers inside the Next process. [1][2]
What to do (practical options)
-
Recommended: run your own wrapper that starts the standalone server and implements graceful shutdown. Example pattern:
- spawn the standalone server or require your server file
- on SIGTERM/SIGINT: stop accepting new requests, await pending requests/tasks, close DB pools/connections, then exit(0)
- set a hard timeout and exit(1) if shutdown times out
Minimal sketch (wrapper approach):
- If you start server via node .next/standalone/server.js you can instead create a small server.js that requires/starts Next’s server and adds:
process.on('SIGTERM', async () => { await shutdown(); process.exit(0); });
This is the robust approach for containers / Kubernetes so you can control stop-timeout and resource cleanup. (General Node graceful‑shutdown guidance). [4][6]
-
Next.js built-in manual handling (Pages router / some runtime versions):
- Set NEXT_MANUAL_SIG_HANDLE=true in the environment used to run Next (e.g. in your start script). Then register process.on('SIGTERM') / process.on('SIGINT') handlers inside your app (example historically placed in pages/_document.js or root instrumentation for App Router). This lets Next’s runtime defer exit so your handlers run. Note: this is not available in next dev and behavior depends on Next version/runtime. [2][5][10]
Concrete references
- Next.js self-hosting / manual shutdown guidance (mentions sending SIGINT/SIGTERM and NEXT_MANUAL_SIG_HANDLE). [2]
- Next.js output: 'standalone' details (creates .next/standalone and minimal server.js to run with node). Use wrapper or copy public/static if needed. [1]
- Node.js/Community guides on graceful shutdown patterns and using SIGTERM with stop-timeout in Docker/K8s. Use to implement your shutdown logic (stop accepting connections, close DBs, set hard timeout). [4][6]
If you want, I can:
- produce a complete, ready-to-run wrapper server.js that starts .next/standalone/server.js and implements graceful shutdown (including waiting for pending requests and closing a sample DB connection), or
- tailor advice for Docker/Kubernetes (liveness/readiness probes and stopTimeout settings). Which would you like?
Add signal handlers for graceful shutdown—Bun as PID 1 requires explicit handling.
Next.js standalone servers (like server.js) do not have built-in signal handlers for SIGTERM/SIGINT. Without them, docker stop will forcefully kill the container after 10 seconds instead of gracefully shutting down in-flight requests.
Choose one approach:
-
Add signal handlers to server.js (recommended if you control the startup code):
process.on("SIGTERM", async () => { // Close DB connections, etc. await server.close(); process.exit(0); }); -
Add tini as init system (simpler if you can't modify server.js):
RUN apk add --no-cache tini ENTRYPOINT ["/sbin/tini", "--"] CMD ["bun", "server.js"]
Without one of these, the container will not shut down gracefully and may lose data on redeploy or restart.
🤖 Prompt for AI Agents
In @Dockerfile around lines 65 - 66, The container runs Bun as PID 1 (CMD
["bun","server.js"]) but lacks graceful shutdown handling; either add
SIGTERM/SIGINT handlers inside server.js to close resources and call
server.close() then process.exit(0) (reference server.js, server.close,
process.on("SIGTERM")/process.on("SIGINT")), or modify the Dockerfile to install
and use tini as init (add RUN to install tini and set ENTRYPOINT
["/sbin/tini","--"] before CMD ["bun","server.js"]) so signals are forwarded and
shutdowns are graceful.
PR Type
Enhancement, Documentation
Description
Replace pinned Bun version with latest stable Alpine image
Remove unnecessary runtime dependencies (nodejs, tini, curl)
Switch from Node.js to Bun runtime for faster execution
Improve health check using Bun eval instead of curl
Enhance comments and documentation throughout Dockerfile
Diagram Walkthrough
File Walkthrough
Dockerfile
Modernize Dockerfile with latest Bun and streamline dependenciesDockerfile
oven/bun:1.1.3-alpinetooven/bun:alpinefor latest stable versionproduction stage
node server.jstobun server.jsfor native Bunexecution
compatibility
optimizations
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.