Asynx
Back to Blog
DevOps Guide

How to Deploy a Turborepo with Next.js and NestJs

Hosting on Vercel? This is not your blog. This guide is for those deploying Next.js frontend and NestJs backend from a Turborepo into custom environments like VPS, bare metal, Docker or Kubernetes.

V
Vijayaraghavan
10/9/2025
5 min read
Thumbnail


Repo Structure

Plain Text
1apps/
2web/ ← Next.js 15.2.1 (Frontend)
3api/ ← NestJS 11.1.2 (Backend)
4packages/ ← Shared libraries

Prerequisite: Docker Installed

Ensure you have Docker installed on your system where you are going to deploy the Turborepo. If not, refer to Docker official docs


Step 1: Configure Next.js (apps/web)

Next.js needs to be run in standalone mode for optimal Docker performance

Edit apps/web/next.config.ts or .js

typescript
1module.exports = {
2output: 'standalone',
3}


Step 2: Create Dockerfile for Next.js

Create a Dockerfile in Next.js folder, in our case it is apps/web/Dockerfile

Docker
1# Stage 1: Install dependencies
2FROM oven/bun:1-alpine AS deps
3WORKDIR /app
4COPY package.json bun.lock turbo.json ./
5COPY apps/web/package.json ./apps/web/
6COPY packages/ ./packages/
7RUN bun install
8
9# Stage 2: Build the Next.js application
10FROM oven/bun:1-alpine AS builder
11WORKDIR /app
12ENV NODE_OPTIONS="--max-old-space-size=4096"
13COPY . .
14RUN bun install
15RUN bun run build --filter=web --concurrency=1
16
17# Stage 3: Prepare the runtime
18FROM oven/bun:1-alpine AS runner
19WORKDIR /app
20RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
21COPY --from=builder /app/apps/web/.next/standalone ./
22COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
23COPY --from=builder /app/apps/web/public ./apps/web/public
24RUN chown -R nextjs:nodejs /app
25USER nextjs
26EXPOSE 3000
27CMD ["node", "apps/web/server.js"]
Tested and working - feel free to copy paste, but always strive to understand the concept.

V

Vijayaraghavan
Founder - Asynx Devs


Step 3: Dockerfile for NestJS (apps/api)

Let's create a Dockerfile for our NestJS backend. In our case it's apps/api/Dockerfile

Docker
1FROM oven/bun:1-alpine AS deps
2WORKDIR /app
3COPY package.json bun.lock turbo.json ./
4COPY apps/api/package.json ./apps/api/
5COPY packages/ ./packages/
6RUN bun install
7
8FROM oven/bun:1-alpine AS builder
9WORKDIR /app
10COPY . .
11RUN bun install
12RUN bun run build --filter=api
13
14FROM oven/bun:1-alpine AS runner
15WORKDIR /app
16RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nestjs
17COPY --from=deps /app/node_modules ./node_modules
18COPY --from=builder /app/apps/api/dist ./dist
19COPY --from=builder /app/apps/api/package.json ./package.json
20RUN chown -R nestjs:nodejs /app
21USER nestjs
22EXPOSE 3001
23ENV NODE_ENV=production PORT=3001
24CMD ["bun", "run", "start:prod"]
Tested and working - feel free to copy paste, but always strive to understand the concept.


Step 4: Create a Docker compose yaml

At the root of your folder (not at apps/, it must be located where apps, turbo.json is located), create a docker-compose.yml:

Docker
1version: '3.8'
2
3services:
4web:
5build:
6context: .
7dockerfile: apps/web/Dockerfile
8ports:
9- "3000:3000"
10environment:
11- NODE_ENV=production
12env_file:
13- apps/web/.env.production
14depends_on:
15- api
16networks:
17- app-network
18
19api:
20build:
21context: .
22dockerfile: apps/api/Dockerfile
23ports:
24- "3001:3001"
25environment:
26- NODE_ENV=production
27- PORT=3001
28networks:
29- app-network
30
31networks:
32app-network:
33driver: bridge


Step 5: Build & Run

docker compose -f docker-compose.yml up -d --build

That's it's done, pull the image and run the container on any system of your preference.


Here are the common pitfall's and misconfigurations while deploying a Turborepo:

1. Turbo.json Misconfiguration:

Here's a Tested & Working turbo.json config for this setup:

Plain Text
1{
2"$schema": "https://turborepo.com/schema.json",
3"ui": "tui",
4"tasks": {
5"build": {
6"dependsOn": ["^build"],
7"inputs": ["$TURBO_DEFAULT$", ".env*"],
8"outputs": [".next/**", "!.next/cache/**", "dist/**", "build/**"]
9},
10"web#build": {
11"dependsOn": ["^build"],
12"inputs": ["$TURBO_DEFAULT$", ".env*"],
13"outputs": [".next/**", "!.next/cache/**"]
14},
15"api#build": {
16"dependsOn": ["^build"],
17"inputs": ["$TURBO_DEFAULT$", ".env*"],
18"outputs": ["dist/**"]
19},
20"lint": {
21"dependsOn": ["^lint"]
22},
23"check-types": {
24"dependsOn": ["^check-types"]
25},
26"dev": {
27"cache": false,
28"persistent": true
29}
30}
31}


2. Frontend ↔ Backend Communication:

When communicating between frontend and backend inside Docker never use localhost.

✅ Use: http://api:3001
❌ Don’t use: http://localhost:3001

This works because api is the Docker service name, and both containers are on the same bridge network. Check your Dockerfile created in the above steps to determine the name usage. In our case it is api.


Deployment Tip

Once your image is build, you can export it using:
docker save -o myapp.tar <image_name>

Then transfer to another server and run:

docker load -i myapp.tar
docker compose up -d


☸️ Kubernetes Deployment?

🧭 If you're looking to host this into a Kubernetes (k8s/k3s). check out our guide: How to Deploy a Turborepo into Kubernetes.


Conclusion

You now have a production ready, scalable deployment setup for your Turborepo app with Next.js and NestJS - fully containerized and platform agnostic. Whether you are deploying it to a VPS, cloud VM or in a Kubernetes Cluster, the foundation is solid.

How to deploy a Nextjs and Nestjs turborepo
how to deploy a Turborepo
Deploying a Turborepo (Next.js + NestJs) on any platform
Share this article:
Vijayaraghavan

Author

Vijayaraghavan

Founder & Director - Asynx Pvt. Ltd

Related Posts

Share Article