Deploy a NestJS App

NestJS (opens in a new tab) is a progressive Node.js framework for building server-side applications that focuses on scalability, extensibility, and versatility. Using NestJS, you can build well-architected sites and applications with JavaScript or TypeScript to make it easier to collaborate, scale, and grow your projects.

This guide explains how to deploy a NestJS application to Koyeb using Git-driven deployment. The application can be built using either native buildpacks or from a Dockerfile.

To successfully follow this documentation, you will need to have a Koyeb account (opens in a new tab). You can optionally install the Koyeb CLI if you prefer to follow this guide without leaving the terminal.

You can deploy and preview the NestJS application from this guide using the Deploy to Koyeb button below.

Deploy to Koyeb (opens in a new tab)

You can consult the repository on GitHub (opens in a new tab) to find out more about the example application that this guide uses.

Install the NestJS CLI

To create a minimal NestJS application, you'll need Node.js (opens in a new tab) installed on your local computer.

First, use npm to install the NestJS command line utility globally. This tool can create new NestJS projects, run applications, and manage external library support:

npm install --global @nestjs/cli

You can verify that the installation completed successfully by typing:

nest info

The output should include information about your current environment, but will note that you're not currently in a project directory:

 _   _             _      ___  _____  _____  _     _____
| \ | |           | |    |_  |/  ___|/  __ \| |   |_   _|
|  \| |  ___  ___ | |_     | |\ `--. | /  \/| |     | |
| . ` | / _ \/ __|| __|    | | `--. \| |    | |     | |
| |\  ||  __/\__ \| |_ /\__/ //\__/ /| \__/\| |_____| |_
\_| \_/ \___||___/ \__|\____/ \____/  \____/\_____/\___/


[System Information]
OS Version     : Linux 6.5
NodeJS Version : v18.18.0
NPM Version    : 9.8.1

[Nest CLI]
Nest CLI Version : 10.1.17

[Nest Platform Information]
😼  cannot read your project package.json file, are you inside your project directory?

This is expected since we have not yet created a NestJS project.

Create a new NestJS project

Next, we can initialize a new NestJS project using the nest new command. This will create a new directory for the project and begin configuring the project.

Type the following to get started, exchanging example-nestjs to the project name that you wish to use:

nest new example-nestjs

Depending on the local tools you have installed, the nest command will prompt you for the package manager you wish to use for the new project:

⚡  We will scaffold your app in a few seconds..

? Which package manager would you ❤  to use? (Use arrow keys)
❯ npm
  yarn
  pnpm

Select your preferred package manager. We will stick with the default npm for this example.

The nest tool will configure the new project directory, installing all of the necessary dependencies and initializing a new Git repository. The project directory contains the following files and directories:

  • nest-cli.json: The configuration file for the NestJS client behavior for the project.
  • node_modules/: The project dependencies.
  • package.json: The standard Node.js project configuration, defining metadata, scripts, dependencies, etc.
  • package-lock.json: The lock file for the project dependencies.
  • README.md: A generic NestJS project README.
  • src/app.controller.spec.ts: The unit tests for the application controller.
  • src/app.controller.ts: The basic controller (opens in a new tab) with a single route configured.
  • src/app.module.ts: The root module for the application.
  • src/app.service.ts: The service file with a basic method defined.
  • src/main.ts: The entry file for the application that creates an instance of your application and sets up the server.
  • test/: The project's end-to-end test files.
  • tsconfig.build.json: The TypeScript compiler configuration to use when building the project.
  • tsconfig.json: The TypeScript compiler configuration for the project.

The primary application files are all stored within the src directory, while much of the project configuration is defined at the root level.

Test the NestJS application locally

Now that the project is initialized, we can test it locally by running the included development server. From inside your project directory, type:

npm run start:dev

By default, this will start up a local development server running at localhost:3000. The application will automatically reload when any changes are detected within the project.

If you visit the site in your browser, you'll see a simple page with the text:

Hello World!

Press CTRL-C to stop the development server when you are finished.

Modify the application port configuration and start script

Currently, the application is designed to run on port 3000. This value is hardcoded in the src/main.ts file. Let's make the port configurable at runtime to make deployment more flexible.

Open the src/main.ts in your text editor make the following changes:

src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
 
const port = process.env.PORT || 3000
console.log(`Launching NestJS app on port ${port}, URL: http://0.0.0.0:${port}`)
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  await app.listen(port)
}
bootstrap()

Instead of listening to port 3000 directly, the server now listens to the port defined by the PORT environment variable. It continues falls back on port 3000 if no value is defined.

Save and close the file when you are finished.

Test out the new functionality by starting the development server with the following command:

PORT=8000 npm run start:dev

If everything goes well, the application will now be served on port 8000.

Since we're already modifying the default behavior of the application, let's also adjust the project's start script. Currently the application's start script is configured to use the development server, but most Node.js projects use the start script to run in production.

Open the package.json file in your editor and modify the start script to match the value being used for start:prod:

package.json
{
  ...
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "node dist/main",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  ...
}

Save and close the file when you're finished.

This example application should run on any modern version of Node.js. If your application requires a specific Node.js version, add or set the engines section in your package.json file. Consult the build with Node.js page to learn more.

Create a Dockerfile for the project (Optional)

We can build and run our NestJS project on Koyeb using the native Node.js buildpack, but we can also optionally build from a Dockerfile for more control. To make it possible to build a container image for our application, we just need to create the Dockerfile. We'll also define a .dockerignore file to tell the builder what files to skip when creating the image.

Start by defining the .dockerignore file in your main project directory. Inside, paste the following contents:

.git
.gitignore
Dockerfile
.dockerignore
dist
node_modules
.env
README.md

This file tells Docker to not include Git files, the Docker files themselves, dependency directories, built artifacts, or environment files unless explicitly called out within the Dockerfile. This helps ensure that the image we build is not bloated and that the build completes faster.

Next, create a new file called Dockerfile within the main project directory. Inside, paste the following contents, adapted from this NestJS template (opens in a new tab):

# Build stage
FROM node:lts-alpine AS builder
 
USER node
WORKDIR /home/node
 
COPY package*.json .
RUN npm ci
 
COPY --chown=node:node . .
RUN npm run build && npm prune --omit=dev
 
 
# Final run stage
FROM node:lts-alpine
 
ENV NODE_ENV production
USER node
WORKDIR /home/node
 
COPY --from=builder --chown=node:node /home/node/package*.json .
COPY --from=builder --chown=node:node /home/node/node_modules ./node_modules
COPY --from=builder --chown=node:node /home/node/dist ./dist
 
ARG PORT
EXPOSE ${PORT:-3000}
 
CMD ["node", "dist/main.js"]

This Dockerfile uses a multistage build (opens in a new tab) to separate the build steps from the final image environment. This creates a more streamlined image and allows us to tightly control what files are included in the final image.

Both stages are based on the Alpine version of the base Node.js image (opens in a new tab) and both configure a dedicated user called node to perform the build and run actions. The application is built and run from /home/node, the node user's home directory, to make permissions management more straightforward.

The build stage is configured to copy the package.json and package-lock.json files and install the dependencies. It then copies the remaining files, builds the project, and prunes the development dependencies from the environment.

The production stage sets the NODE_ENV environment variable to production to tell NestJS that it should run with production features enabled. It then copies the package.json and package-lock.json files, the dependencies, and the built artifacts from the build stage. It marks the PORT environment as user configurable and exposes the port. Afterwards, it executes the same command that the start script defines in the package.json file.

If you have Docker installed locally, you can build and test the image on your computer and optionally upload it to a registry. You can deploy container images from any container registry to Koyeb.

We can also build the Dockerfile directly from the repository when we deploy, which is useful as a way of automatically deploying when changes occur. We will demonstrate this method as one of the options in this guide.

Push the project to GitHub

Once you're finished configuring the project locally, you can commit your work and push it to GitHub.

NestJS automatically initializes a Git repository when the nest new command is run, so we just need to add our project files to it. Run the following commands within the project's root directory to commit the project files and push changes to GitHub.

Remember to replace the values of <YOUR_GITHUB_USERNAME> and <YOUR_REPOSITORY_NAME> with your own information:

git add :/
git commit -m "Initial commit"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git push -u origin main

Deploy to Koyeb using git-driven deployment

Once the repository is pushed to GitHub, you can deploy the NestJS application to Koyeb. Any changes in the deployed branch of your codebase will automatically trigger a redeploy on Koyeb, ensuring that your application is always up-to-date.

To deploy the NestJS app on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create Web Service on the Overview tab of the Koyeb control panel.
  2. Select GitHub as the deployment option.
  3. Choose the GitHub repository and branch containing your application code. Alternatively, you can enter our public NestJS example repository (opens in a new tab) into the Public GitHub repository at the bottom of the page: https://github.com/koyeb/example-nestjs.
  4. Choose the Builder for your project. We can use either a Dockerfile or buildpack for this repository.
  5. Name the App and Service, for example example-nestjs.
  6. Click the Deploy button.

A Koyeb App and Service will be created. Your application will be built and deployed to Koyeb. Once the build has finished, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.

Deploy to Koyeb from a container registry

If you choose to build a container image for the NestJS application, you can optionally deploy the application from a container registry instead of from GitHub.

To deploy a pre-built NestJS container image on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create Web Service on the Overview tab of the Koyeb control panel.
  2. Select Docker as the deployment option.
  3. Choose the container image and tag from your registry and click Next to continue.
  4. Name the App and Service, for example example-nestjs.
  5. Click the Deploy button.

A Koyeb App and Service will be created. The container image will be pulled from the container registry and a container will be deployed from it. Once the initialization is complete, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.