Skip to content

Deploying a Next.js App

Next.js is the leading React framework for production, enabling developers to build fast, scalable, and feature-rich web applications with ease. It offers hybrid static and server-side rendering, file-based routing, API routes, automatic code splitting, optimized image handling, and built-in SEO support. With Next.js, you can build everything from marketing websites to complex web applications, all powered by a single framework that scales from startups to enterprises.

This comprehensive guide walks you through deploying a Next.js 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 Next.js
  • Sample Code Examples
  • Project Structure
  • Deploying Without a Dockerfile (Nixpacks)
  • Deploying With a Dockerfile
  • Environment Variables & Configuration
  • API Routes & Middleware
  • Performance & Optimization
  • Troubleshooting
  • Resources

Prerequisites

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

  • Node.js 18 or higher - Next.js 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

Getting Started: Install Next.js

Create a New Next.js Project

Follow these steps to create and set up a new Next.js application:

  1. Create a new Next.js project using the create-next-app tool:
    Terminal window
    npx create-next-app@latest my-next-app
    cd my-next-app

    Choose your preferences during setup (TypeScript, ESLint, Tailwind CSS, etc.).

  2. Start the development server:
    Terminal window
    npm run dev
  3. Visit http://localhost:3000 to see your app running. Next.js will automatically reload as you make changes.
  4. Explore the project structure. Next.js uses the `app` directory (App Router) or `pages` directory (Pages Router) for file-based routing.
  5. Build the application locally to ensure it compiles correctly:
    Terminal window
    npm run build
    npm start

Sample Code Examples

Basic Next.js Page with Server Components

Here’s a complete example using the App Router with server and client components:

app/page.tsx
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Home | My Next.js App',
description: 'Welcome to my Next.js application on Klutch.sh'
};
export default function Home() {
return (
<main className="min-h-screen bg-gradient-to-b from-blue-50 to-white p-8">
<div className="max-w-4xl mx-auto">
<h1 className="text-4xl font-bold mb-4">Welcome to Next.js on Klutch.sh!</h1>
<p className="text-lg text-gray-700 mb-8">
This is a modern Next.js application deployed on Klutch.sh.
</p>
</div>
</main>
);
}

API Routes Example

app/api/health/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
}
app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const users = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
return NextResponse.json({ users });
}
export async function POST(request: NextRequest) {
const data = await request.json();
const newUser = {
id: Math.floor(Math.random() * 10000),
...data,
created: new Date().toISOString()
};
return NextResponse.json(newUser, { status: 201 });
}

Next.js with Database Integration

lib/db.ts
// Example using a database client like Prisma or similar
export async function getUsers() {
// Replace with your actual database query
return [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
}
export async function getUserById(id: number) {
const users = await getUsers();
return users.find(user => user.id === id);
}
app/users/page.tsx
import { getUsers } from '@/lib/db';
export default async function UsersPage() {
const users = await getUsers();
return (
<main className="p-8">
<h1 className="text-3xl font-bold mb-6">Users</h1>
<div className="grid gap-4">
{users.map(user => (
<div key={user.id} className="border p-4 rounded-lg">
<h2 className="font-semibold">{user.name}</h2>
<p className="text-gray-600">{user.email}</p>
</div>
))}
</div>
</main>
);
}

Project Structure

A typical Next.js project with the App Router has this structure:

my-next-app/
├── app/
│ ├── api/
│ │ ├── health/
│ │ │ └── route.ts
│ │ └── users/
│ │ └── route.ts
│ ├── users/
│ │ └── page.tsx
│ ├── layout.tsx
│ ├── page.tsx
│ └── globals.css
├── lib/
│ ├── db.ts
│ └── utils.ts
├── public/
│ ├── images/
│ └── favicon.ico
├── .env.local
├── .gitignore
├── next.config.js
├── package.json
├── package-lock.json
├── tsconfig.json
└── README.md

Deploying Without a Dockerfile

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

  1. Build your Next.js app for production to ensure everything compiles correctly:
    Terminal window
    npm run build

    Verify the .next folder is created with your built application.

  2. Push your Next.js application to a GitHub repository with all your source code, `app` or `pages` directory, `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 Next.js App").
  5. Create a new app with the following configuration:
    • Repository - Select your Next.js 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 Next.js 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 (API keys, database URLs, 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., next start)
    • 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 Next.js 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 `/api/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 Next.js 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 Next.js 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/.next ./.next
    COPY --from=builder /app/public ./public
    COPY --from=builder /app/next.config.js ./
    # 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/api/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
    # Start the application
    CMD ["npm", "start"]
  2. Create a `.dockerignore` file to exclude unnecessary files from the Docker build:
    node_modules
    npm-debug.log
    .next
    .git
    .gitignore
    README.md
    .env
    .env.local
    .vscode
    .idea
    .DS_Store
    public/.next
  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

Next.js 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
NODE_ENV=production
# Next.js specific
NEXT_PUBLIC_API_URL=https://api.example.com
# Database connection
DATABASE_URL=postgresql://user:password@db.example.com:5432/mydb
# API Keys and Secrets
API_KEY=your_api_key_here
SECRET_KEY=your_secret_key_here
JWT_SECRET=your_jwt_secret_here
# Third-party services
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_PUBLIC_KEY=pk_live_xxx
# Analytics
NEXT_PUBLIC_GA_ID=G-xxx
# Features
ENABLE_ANALYTICS=true
LOG_LEVEL=info

Using Environment Variables in Your App

app/api/config/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({
apiUrl: process.env.NEXT_PUBLIC_API_URL,
environment: process.env.NODE_ENV,
hasDatabase: !!process.env.DATABASE_URL
});
}
lib/api.ts
export const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api';
export async function fetchUsers() {
const res = await fetch(`${API_URL}/users`);
return res.json();
}

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: npm start

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


API Routes & Middleware

Next.js provides powerful API routes and middleware for handling server-side logic.

Creating API Routes

app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
return NextResponse.json({
id: params.id,
title: 'Sample Post',
content: 'This is a sample post from Klutch.sh'
});
}
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const data = await request.json();
return NextResponse.json({
id: params.id,
...data,
updated: new Date().toISOString()
});
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
return NextResponse.json({ deleted: true, id: params.id });
}

Using Middleware

middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
// Add security headers
const response = NextResponse.next();
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-XSS-Protection', '1; mode=block');
return response;
}
export const config = {
matcher: ['/((?!_next|public).*)']
};

Performance & Optimization

Image Optimization

app/page.tsx
import Image from 'next/image';
export default function Home() {
return (
<main>
<Image
src="/hero-image.jpg"
alt="Hero image"
width={800}
height={400}
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
</main>
);
}

Font Optimization

app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({
children
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}

Caching Strategy

app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const response = NextResponse.json({ users: [] });
// Cache for 1 hour
response.headers.set('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400');
return response;
}

Performance Tips

  1. Use Image Optimization - Always use the Next.js Image component
  2. Implement Incremental Static Regeneration (ISR) - Cache pages and revalidate on demand
  3. Code Splitting - Next.js automatically splits code by route
  4. Database Query Optimization - Use efficient queries and connection pooling
  5. CDN Integration - Leverage Klutch.sh’s built-in CDN for static assets
  6. API Route Optimization - Keep API routes fast with proper error handling
  7. Monitoring - Use Next.js analytics to monitor performance

Troubleshooting

Application Won’t Start

Problem - Deployment completes but the app shows as unhealthy

Solution:

  • Verify your Next.js app builds locally: npm run build
  • Check that package.json has a valid start script
  • Ensure the app listens on port 3000
  • Check application logs in the Klutch.sh dashboard for error messages
  • Verify all environment variables are set correctly

Build Fails with Module Errors

Problem - Build fails with “Cannot find module” errors

Solution:

  • Ensure your package.json is in the root directory
  • Verify all dependencies are listed correctly in dependencies
  • Check that package-lock.json exists and is up to date
  • Run npm install locally to verify dependencies work
  • Remove node_modules and package-lock.json, then run npm install fresh

Static Generation Issues

Problem - Pages don’t generate correctly during build

Solution:

  • Check for dynamic routes that need parameters
  • Verify generateStaticParams is implemented for dynamic routes
  • Check for data fetching errors in build logs
  • Consider using ISR (Incremental Static Regeneration) for dynamic content

High Memory Usage

Problem - App uses excessive memory and gets killed

Solution:

  • Check for memory leaks in API routes
  • Implement proper cleanup for database connections
  • Monitor build size with npm run build
  • Consider splitting large pages into smaller components
  • Consider upgrading compute tier

Slow Page Load Times

Problem - Pages load slowly even though Next.js is optimized

Solution:

  • Optimize images using the Image component
  • Implement proper caching strategies
  • Use API route pagination for large datasets
  • Enable compression middleware
  • Consider enabling Vercel Analytics or similar monitoring tools

Resources


Summary

Deploying a Next.js application on Klutch.sh is straightforward whether you choose Nixpacks or Docker. Both methods provide reliable, scalable hosting for your modern React applications. Start with Nixpacks for simplicity and rapid deployments, or use Docker for complete control over your build environment. With Next.js’s powerful features and Klutch.sh’s automatic scaling, load balancing, and environment management, you can deploy your application from development to production and scale it to serve millions of users efficiently.