Skip to content

Deploying a NestJS App

NestJS is a progressive, enterprise-grade Node.js framework for building efficient, scalable, and maintainable server-side applications. Built with TypeScript and inspired by Angular architecture, NestJS provides a modular design pattern, extensive decorator-based configuration, dependency injection, and comprehensive tooling for building robust APIs, microservices, and real-time applications. It’s trusted by organizations worldwide for mission-critical backend systems.

This comprehensive guide walks you through deploying a NestJS application to Klutch.sh, covering both automatic Nixpacks-based deployments and Docker-based deployments. You’ll learn installation steps, explore sample code, configure environment variables, and discover best practices for production deployments.

Table of Contents

  • Prerequisites
  • Getting Started: Install NestJS
  • Sample Code Examples
  • Project Structure
  • Deploying Without a Dockerfile (Nixpacks)
  • Deploying With a Dockerfile
  • Environment Variables & Configuration
  • Modules & Dependency Injection
  • Troubleshooting
  • Resources

Prerequisites

To deploy a NestJS application on Klutch.sh, ensure you have:

  • Node.js 18 or higher - NestJS requires a modern Node.js version for optimal performance
  • npm or yarn - For managing dependencies
  • Git - For version control
  • GitHub account - Klutch.sh integrates with GitHub for continuous deployments
  • Klutch.sh account - Sign up for free
  • NestJS CLI (optional) - For easier project scaffolding and code generation

Getting Started: Install NestJS

Create a New NestJS Project

Follow these steps to create and set up a new NestJS application:

  1. Install the NestJS CLI globally (optional but recommended):
    Terminal window
    npm install -g @nestjs/cli
  2. Create a new NestJS project:
    Terminal window
    nest new my-nest-app
    cd my-nest-app

    Or without the CLI:

    Terminal window
    npm i -g @nestjs/cli
    npx @nestjs/cli@latest new my-nest-app
  3. Install dependencies (if not auto-installed):
    Terminal window
    npm install
  4. Start the development server:
    Terminal window
    npm run start:dev
  5. Visit http://localhost:3000 to see your app running. The CLI creates a basic application with a hello world endpoint.

Sample Code Examples

Basic NestJS Application with Multiple Routes

Here’s a complete example with controllers, services, and request validation:

src/app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello from NestJS on Klutch.sh!';
}
getHealth() {
return {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
};
}
}
src/app.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('/health')
getHealth() {
return this.appService.getHealth();
}
@Get('/api/users/:id')
getUser(@Param('id') id: string) {
return {
id: parseInt(id),
name: 'John Doe',
email: 'john@example.com'
};
}
@Post('/api/users')
createUser(@Body() userData: { name: string; email: string }) {
return {
id: Math.floor(Math.random() * 10000),
...userData,
created: new Date().toISOString()
};
}
}

NestJS with Database Integration

src/database/database.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class DatabaseService {
async query(sql: string): Promise<any[]> {
// Your database query logic here
return [];
}
async findAll(table: string): Promise<any[]> {
return this.query(`SELECT * FROM ${table}`);
}
async findById(table: string, id: number): Promise<any> {
return this.query(`SELECT * FROM ${table} WHERE id = ${id}`);
}
}
src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { DatabaseService } from '../database/database.service';
@Injectable()
export class UsersService {
constructor(private databaseService: DatabaseService) {}
async getAllUsers() {
return this.databaseService.findAll('users');
}
async getUserById(id: number) {
return this.databaseService.findById('users', id);
}
}
src/users/users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('api/users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get()
async getAllUsers() {
return this.usersService.getAllUsers();
}
@Get(':id')
async getUserById(@Param('id') id: string) {
return this.usersService.getUserById(parseInt(id));
}
}

NestJS with Guard and Middleware

src/auth/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest<Request>();
const token = request.headers.authorization?.split(' ')[1];
if (!token) {
return false;
}
// Validate token
return true;
}
}
src/app.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth/auth.guard';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('/protected')
@UseGuards(AuthGuard)
getProtectedResource() {
return { data: 'This is protected' };
}
}

Project Structure

A typical NestJS project has this structure:

my-nest-app/
├── src/
│ ├── modules/
│ │ ├── users/
│ │ │ ├── users.controller.ts
│ │ │ ├── users.service.ts
│ │ │ ├── users.module.ts
│ │ │ └── dto/
│ │ ├── auth/
│ │ │ ├── auth.guard.ts
│ │ │ ├── auth.service.ts
│ │ │ └── auth.module.ts
│ │ └── database/
│ ├── common/
│ │ ├── filters/
│ │ ├── guards/
│ │ ├── interceptors/
│ │ └── pipes/
│ ├── app.controller.ts
│ ├── app.service.ts
│ ├── app.module.ts
│ └── main.ts
├── dist/
├── node_modules/
├── .env
├── .gitignore
├── tsconfig.json
├── package.json
└── package-lock.json

Deploying Without a Dockerfile

Klutch.sh uses Nixpacks to automatically detect and build your NestJS application. This is the simplest deployment option that requires no additional configuration files.

  1. Build your NestJS application for production to ensure everything compiles correctly:
    Terminal window
    npm run build

    Verify the dist folder is created with compiled code.

  2. Push your NestJS application to a GitHub repository with all your source code, `src` folder, `package.json`, and `package-lock.json` files.
  3. Log in to your Klutch.sh dashboard.
  4. Create a new project and give it a name (e.g., "My NestJS API").
  5. Create a new app with the following configuration:
    • Repository - Select your NestJS GitHub repository and the branch to deploy
    • Traffic Type - Select HTTP (for web applications serving HTTP traffic)
    • Internal Port - Set to 3000 (the default port for NestJS applications)
    • Region - Choose your preferred region for deployment
    • Compute - Select the appropriate compute resource size
    • Instances - Choose how many instances to run (start with 1 for testing)
    • Environment Variables - Add any environment variables your app needs (database URLs, API keys, secrets, etc.)

    If you need to customize the start command or build process, you can set Nixpacks environment variables:

    • START_COMMAND: Override the default start command (e.g., node dist/main.js)
    • BUILD_COMMAND: Override the default build command (e.g., npm run build)
  6. Click "Create" to deploy. Klutch.sh will automatically detect your Node.js project, install dependencies, build your NestJS application, and start it.
  7. Once deployed, your app will be available at a URL like `example-app.klutch.sh`. Test it by visiting the URL in your browser and checking the health endpoint at `/health`.

Deploying With a Dockerfile

If you prefer more control over the build and runtime environment, you can use a Dockerfile. Klutch.sh will automatically detect and use any Dockerfile in your repository’s root directory.

  1. Create a `Dockerfile` in your project root:
    # Multi-stage build for optimized NestJS deployment
    FROM node:18-alpine AS builder
    WORKDIR /app
    # Copy package files
    COPY package*.json ./
    # Install dependencies
    RUN npm ci
    # Copy source code
    COPY . .
    # Build NestJS application
    RUN npm run build
    # Production stage
    FROM node:18-alpine
    WORKDIR /app
    # Copy package files
    COPY package*.json ./
    # Install only production dependencies
    RUN npm ci --only=production
    # Copy built application from builder
    COPY --from=builder /app/dist ./dist
    # Set production environment
    ENV NODE_ENV=production
    # Expose port (must match your app's listening port)
    EXPOSE 3000
    # Health check
    HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
    # Start the application
    CMD ["node", "dist/main.js"]
  2. Create a `.dockerignore` file to exclude unnecessary files from the Docker build:
    node_modules
    npm-debug.log
    dist
    .git
    .gitignore
    src
    tsconfig.json
    README.md
    .env
    .env.local
    .vscode
    .idea
    .DS_Store
  3. Push your code (with Dockerfile and .dockerignore) to GitHub.
  4. Follow the same deployment steps as the Nixpacks method:
    • Log in to Klutch.sh
    • Create a new project
    • Create a new app pointing to your GitHub repository
    • Set the traffic type to HTTP and internal port to 3000
    • Add any required environment variables
    • Click “Create”

    Klutch.sh will automatically detect your Dockerfile and use it to build and deploy your application.

  5. Your deployed app will be available at `example-app.klutch.sh` once the build and deployment complete.

Environment Variables & Configuration

NestJS applications use environment variables for configuration. Set these in the Klutch.sh dashboard during app creation or update them afterward.

Common Environment Variables

# Server configuration
PORT=3000
NODE_ENV=production
# Database connection
DATABASE_URL=postgresql://user:password@db.example.com:5432/mydb
DATABASE_HOST=db.example.com
DATABASE_PORT=5432
DATABASE_NAME=mydb
DATABASE_USER=user
DATABASE_PASSWORD=password
# API Keys and Secrets
API_KEY=your_api_key_here
SECRET_KEY=your_secret_key_here
JWT_SECRET=your_jwt_secret_here
JWT_EXPIRATION=3600
# CORS configuration
CORS_ORIGIN=https://yourdomain.com
# Logging
LOG_LEVEL=debug
LOG_FORMAT=json
# Application settings
DEBUG=false
ENABLE_LOGGING=true

Using Environment Variables in Your App

src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.env.PORT || 3000;
const environment = process.env.NODE_ENV || 'development';
app.enableCors({
origin: process.env.CORS_ORIGIN || '*'
});
await app.listen(port);
console.log(`Server running in ${environment} mode on port ${port}`);
}
bootstrap();
src/config/database.config.ts
import { registerAs } from '@nestjs/config';
export default registerAs('database', () => ({
host: process.env.DATABASE_HOST || 'localhost',
port: parseInt(process.env.DATABASE_PORT || '5432'),
database: process.env.DATABASE_NAME || 'mydb',
username: process.env.DATABASE_USER || 'user',
password: process.env.DATABASE_PASSWORD,
synchronize: process.env.NODE_ENV !== 'production',
logging: process.env.NODE_ENV === 'development'
}));

Customizing Build and Start Commands with Nixpacks

If using Nixpacks deployment without a Dockerfile, you can customize build and start commands by setting environment variables:

BUILD_COMMAND: npm run build
START_COMMAND: node dist/main.js

Set these as environment variables during app creation on Klutch.sh.


Modules & Dependency Injection

NestJS’s modular architecture and dependency injection system make it easy to organize and scale applications:

Creating Custom Modules

src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
@Module({
providers: [UsersService],
controllers: [UsersController],
exports: [UsersService]
})
export class UsersModule {}
src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { DatabaseModule } from './database/database.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env'
}),
DatabaseModule,
UsersModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}

Scaling with Multiple Instances

Klutch.sh makes it easy to scale your NestJS application:

  • Multiple Instances - Set the number of instances in the app configuration to distribute traffic across multiple containers
  • Load Balancing - Klutch.sh automatically load balances traffic across all instances
  • Stateless Design - Ensure your application is stateless so it can run on multiple instances
  • Shared Resources - Use external services (Redis, databases) for shared state across instances

Performance Optimization Tips

  1. Use Request Caching - Cache responses for frequently accessed endpoints
  2. Implement Rate Limiting - Protect your API from abuse
  3. Database Connection Pooling - Manage database connections efficiently
  4. Async Operations - Use async/await for non-blocking operations
  5. Compression - Enable gzip compression for responses
  6. Logging Strategy - Use structured logging for better observability
  7. Module Organization - Keep modules small and focused

Example with performance optimizations:

src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as compression from 'compression';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable compression
app.use(compression());
// Set security headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
next();
});
const port = process.env.PORT || 3000;
await app.listen(port);
console.log(`Server started on port ${port}`);
}
bootstrap();

Troubleshooting

Application Won’t Start

Problem - Deployment completes but the app shows as unhealthy

Solution:

  • Check that your app listens on port 3000 (or the port you specified)
  • Verify that process.env.PORT is properly used in your main.ts
  • Ensure the dist folder is created with compiled code
  • Check that all imports are correct and no circular dependencies exist
  • Check application logs in the Klutch.sh dashboard for error messages
  • Verify package.json has a valid start or start:prod script

Build Fails with “Cannot find module”

Problem - Dependencies are not installed during build

Solution:

  • Ensure your package.json is in the root directory
  • Verify all dependencies are listed in dependencies, not just devDependencies
  • Check that dependency names are spelled correctly
  • Ensure package-lock.json exists and is up to date
  • Run npm install locally to verify dependencies work

TypeScript Compilation Errors

Problem - Build fails with TypeScript errors

Solution:

  • Verify tsconfig.json has correct settings
  • Check that all type definitions are installed
  • Run npm run build locally to identify errors
  • Ensure TypeScript version compatibility with your Node.js version

High Memory Usage

Problem - App uses excessive memory and gets killed

Solution:

  • Check for memory leaks in your application code
  • Implement proper cleanup for connections and event listeners
  • Monitor memory usage during development with the --inspect flag
  • Consider implementing request/response size limits
  • Consider upgrading compute tier or reducing instance connections

Slow API Response Times

Problem - API endpoints respond slowly despite NestJS being fast

Solution:

  • Check for synchronous operations that should be async
  • Implement caching for frequently accessed data
  • Enable compression middleware to reduce payload size
  • Profile your application to identify bottlenecks
  • Optimize database queries and add indexes
  • Consider upgrading your compute tier

Resources


Summary

Deploying a NestJS application on Klutch.sh is straightforward whether you choose Nixpacks or Docker. Both methods provide reliable, scalable hosting for your enterprise-grade Node.js applications. Start with Nixpacks for simplicity and rapid deployments, or use Docker for complete control over your build environment. With NestJS’s modular architecture and Klutch.sh’s automatic scaling, load balancing, and environment management, you can deploy your application from development to production and scale it to handle millions of requests with ease.