How to Create a Next.js app with Bun, Docker, and Postgressql

Alireza Farokhi
7 min readOct 14, 2023

--

Intro: In today’s fast-paced world of web development, deploying your Next.js application efficiently is crucial. You want speed, simplicity, and power — all without breaking the bank. This is where Bun steps in. A new JavaScript runtime designed with a focus on speed, elegant APIs, and a cohesive developer experience, Bun promises to revolutionize your deployment process.

If you’re wondering how to harness the combined power of Next.js, Docker, and PostgreSQL, while integrating Bun into the mix, you’re in the right place. In this step-by-step guide, we’ll walk you through deploying a Next.js app with these technologies/. Buckle up; it’s time to take your deployment game to the next level.

Now without further ado, let's jump in to see what steps we’re going to take today:

What? Bun?

Meet Bun — The New JavaScript Runtime

In this section, we’ll introduce Bun and highlight why it’s a game-changer in the world of JavaScript development.

Bun — A JavaScript Runtime for the Modern Era

  • Bun’s Design Goals: We’ll start by explaining Bun’s three major design goals: speed, elegant APIs, and a cohesive developer experience. These goals are what set Bun apart in the world of JavaScript runtimes.
  • Speed Matters: Emphasize the importance of speed in today’s computing landscape, especially as edge computing gains prominence. Bun’s integration with JavaScriptCore, the performance-focused JS engine from Safari, is a testament to its commitment to speed.
  • Elegant APIs: Discuss how Bun provides a minimal set of highly optimized APIs for common tasks, making development more straightforward and efficient.
  • A Complete Toolkit: Highlight that Bun is more than just a runtime; it’s a complete toolkit for building JavaScript apps. It includes a package manager, test runner, and bundler, making it a one-stop solution for developers.

Bun as a Drop-In Replacement for Node.js

Step 1: Set Up Your Development Environment

In this step, we’ll cover the essential preparations you need to make before diving into the technical aspects of deployment.

Bun:

let’s start by installing bun on your system:
open your console/terminal and paste the command below:

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

Docker:
Docker Installation on Linux:

  • Linux distributions vary, so please follow the official Docker documentation for detailed installation steps tailored to your distribution: Install Docker Engine

Docker Installation on Mac:

  1. Download Docker Desktop for Mac from the Docker website: Docker Desktop for Mac
  2. Double-click the downloaded .dmg file.
  3. Drag the Docker icon to the Applications folder.
  4. Launch Docker from the Applications folder.

Docker Installation on Windows:

  1. Download Docker Desktop for Windows from the Docker website: Docker Desktop for Windows
  2. Run the installer.
  3. Follow the installation wizard’s instructions, allowing Docker to use the Windows Subsystem for Linux (WSL) if prompted.
  4. Once the installation is complete, Docker should be running on your Windows machine.

Installing Docker Compose on Linux, Mac, and Windows:

Docker Compose Installation on Linux, Mac, and Windows:

  1. Download the Docker Compose binary from the official GitHub repository: Docker Compose Releases
  2. For Linux and Mac, you can use the following command to download the binary:
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

For Windows, you’ll need to download the Windows binary from the same GitHub releases page.

Make the binary executable (Linux and Mac):

sudo chmod +x /usr/local/bin/docker-compose

On Windows, simply place the downloaded binary in a location that’s in your system’s PATH.

Verify the installation by running the following command:

docker-compose --version

This should display the Docker Compose version if the installation was successful on all platforms. If you encounter any issues or have specific questions during the installation, please feel free to ask for assistance.

Step 2: Install Next.js with Bun

bun create next-app

then you would need to run through the installation steps:

Install Next.js using Bun

Alternatively, you can download it from this GitHub repo.

git clone git@github.com:alireza-farokhi/nextsjs-bun-docker.git

now let’s navigate to our project’s root directory:

cd nextjs-bun-docker

now we need to install dependencies (if cloning from the git repo)”

Installing dependencies using Bun vs NPM

You won’t believe how incredibly fast is bun:

133 packages installed [278.00ms]

vs

added 116 packages, and audited 117 packages in 34s

Now, let's build and start our app by running:

bun next build

and

bun next start
bun next start

Now we can head to the localhost:3000 and see our Next.js app served with Bun.

Now, Let’s start dockerizing the app. Create a directory named docker and a file named dev.Dockerfile in it:

# docker/dev.Dockerfile
FROM oven/bun:latest

WORKDIR /app/next-app

COPY package.json ./
COPY bun.lockb ./

RUN bun install

COPY . .


# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry
# Uncomment the following line to disable telemetry at run time
ENV NEXT_TELEMETRY_DISABLED 1

# for deploting the build version

# RUN bun next build
# and
# CMD bun next start

# OR for sart Next.js in development, comment above two lines and uncomment below line

CMD bun run dev

# Note: Don't expose ports here, Compose will handle that for us

now, in the project’s root directory let’s create docker-compose.dev.yml

# docker-compose.dev.yml
version: '3'

services:
next-app:
container_name: next-app
build:
context: .
dockerfile: ./docker/dev.Dockerfile
volumes:
- .:/app/next-app
restart: always
ports:
- 3000:3000

and by creating a .dockerignore in root directory, we can skip copying some of the files we don’t want to be moved to our docker container:

# .dockerignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo

Now, let’s try to run our docker container by running:

docker-compose -f docker-compose.dev.yml up -d --build
build a Next.js app with Bun using Docker

Let’s head back to localhost:3000 and see our app.

PostgresSql and Prisma

let’s connect our app to a Postgres docker image and create a table using Prisma.

updating our docker-compose file:

# docker-compose.dev.yml
version: '3'

services:
next-app:
container_name: next-app
build:
context: .
dockerfile: ./docker/dev.Dockerfile
# Set envrionment variables based on the .env file
env_file:
- .env
# Set environment variables directly in the docker-compose file
environment:
POSTGRES_ADDR: postgres
POSTGRES_DATABASE: midshiftDB
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
depends_on:
postgres:
condition: service_healthy
volumes:
- .:/app/next-app
restart: always
ports:
- 3000:3000
postgres:
image: postgres:15
restart: always
ports:
- 5436:5432
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test:
["CMD", "pg_isready", "-U", "postgres", "-d", "midshiftDB"]
interval: 5s
timeout: 10s
retries: 5

volumes:
pgdata: {}

and create a .env file in the root directory:

# .env
POSTGRES_DB="next_app"
POSTGRES_USER="postgres"
POSTGRES_PASSWORD="postgres"

DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5436/${POSTGRES_DB}?schema=public"

and stop and start the Docker containers again:

docker-compose -f docker-compose.dev.yml down
docker-compose -f docker-compose.dev.yml up -d -V --build

here is our command output for a fresh install of everything, which if you clone the repo and run the docker-compose command above you’ll see:

Next.js Bun Docker PostgresSql
Next.js Bun Docker PostgresSql

Prisma

Prisma unlocks a new level of developer experience when working with databases thanks to its intuitive data model, automated migrations, type-safety & auto-completion.

let’s install

bun install prisma --save-dev

and

bun prisma init

which creates a Prisma directory, let’s navigate to it and update our scheme.prisma

// prisma/scheme.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model users {
id Int @id @default(autoincrement())
email String @unique
name String?
}

now, let’s update our docker file to include Prisam's migration

FROM oven/bun:latest

WORKDIR /app/next-app

COPY package.json ./
COPY bun.lockb ./

RUN bun install

COPY . .

RUN bun prisma migrate dev --name init

RUN bun prisma generate

# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry
# Uncomment the following line to disable telemetry at run time
ENV NEXT_TELEMETRY_DISABLED 1

# for deploting the build version

# RUN bun next build
# and
# RUN bun next start

# OR for sart Next.js in development, comment above two lines and uncomment below line

CMD bun run dev

# Note: Don't expose ports here, Compose will handle that for us

which the result would be:

and if you want to make sure your table has been created in the Postgres container you can navigate to the container and see:

Now you can start developing your next Next.js full stack app with the Bun, Docker, and Postgres.

feel free to engage and ask any questions in the comments or if you want to get in touch: https://alireza-farokhi.github.io/

--

--