Setup NextJS project with Bun

Introduction

Bun is a modern JavaScript runtime built with the goal of optimizing performance and improving the development experience. In addition to being compatible with the NodeJS ecosystem, Bun also integrates a package manager, bundler and test runner in the same tool.

Some outstanding advantages of Bun:

  • Dependency installation speed is significantly faster than npm, yarn and even pnpm.
  • High startup and application execution performance thanks to being written in Zig.
  • Integrates multiple tools in one runtime, helping reduce the number of dependencies required.
  • Supports projects using modern frameworks like NextJS and NestJS well.
  • Helps shorten build times and optimize the CI/CD process.

Detail

  • Note that although using Bun can provide comprehensive and effective support for NPM packages, there are still many risks regarding compatibility with older package versions.
  • If the project you are developing is relatively large and has been operating for a long time, consider carefully before deciding to migrate to Bun.
  • It will be more suitable if you or your company intend to deploy a new project, then using Bun will help enhance your experience in the development, testing and CI/CD process quickly.

First, install as follows if you use Linux/MacOS:

curl -fsSL https://bun.sh/install | bash

To create a NextJS project:

bunx create-next-app@latest

Build Docker image

For NextJS projects, it supports a standalone mode to build a separate server that can handle both server side and client side, which you can use directly without going through any CDN.

First, configure the next.config.ts file to use standalone mode as follows:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: 'standalone',
};

export default nextConfig;

Create a Dockerfile with the following content:

FROM oven/bun:1-alpine AS deps
WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

FROM oven/bun:1-alpine AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production

RUN bun run build

FROM node:22-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000

CMD ["node", "server.js"]

The above Dockerfile uses a multi-stage build model to optimize image size and increase build speed, making the final image more compact, reducing deployment time and taking advantage of Bun's performance during package installation as well as application builds.

  • The deps stage uses the Bun image to install all dependencies. Only copying package.json and bun.lock helps Docker utilize the cache layer effectively, only reinstalling packages when these files change.

  • The builder stage continues to use Bun to build the NextJS application. The entire source code is copied into the container and built using the bun run build command.

  • The runner stage uses a lighter NodeJS Alpine image to run the application in the production environment.

  • Create a separate User and group to avoid running the container with root privileges, increasing security.

  • Copy the necessary folders such as:

    • public: resources allowing public access
    • .next/static: static resources, without any logic processing
    • .next/standalone: this is the structure created by NextJS when using the standalone mode, helping reduce image size and only keeping the files necessary to run the application
  • The command CMD ["node", "server.js"] starts the NextJS application in the production environment.

Happy coding!

See more articles here.

Comments

Popular posts from this blog

All Practice Series

Kubernetes Deployment for Zero Downtime

Deploying a NodeJS Server on Google Kubernetes Engine

Setting up Kubernetes Dashboard with Kind

Sitemap

React Practice Series

Monitoring with cAdvisor, Prometheus and Grafana on Docker

DevOps Practice Series

A Handy Guide to Using Dynamic Import in JavaScript

Using Kafka with Docker and NodeJS