Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Deploy Next.js on a Managed Server

profile picture
Author
just-read-the-instructions
Published
2025-03-26
Time to read
5 minutes reading time

Introduction

In this tutorial we will deploy a Next.js application on a Hetzner Managed Server for production. We will set up PM2 and a reverse proxy configuration.

Prerequisites

  • SSH access
  • hos-nodejs is already installed (send support request via konsoleH)
  • Process release for PM2*, next-server* and sh (send support request via konsoleH)

Step 1 - Set Node.js version

Depending on which Node.js version your application requires, execute one of the following commands:

# for v18
echo 18 > ~/.nodeversion

# for v20
echo 20 > ~/.nodeversion

# for v22
echo 22 > ~/.nodeversion

Use v22 if you create a new application.

You can check the currently used version via node -v.

Step 2 - Install PM2

Adjust $PATH variable

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Install PM2 via

npm install -g pm2

and check if PM2 is installed

pm2 --version

This should give you the version number.

Step 3 - Create Next.js application

Change into your home directory and create a new Next.js application via the following command:

cd "$HOME"
npx create-next-app@latest
cd example-app/ # Replace example-app with the name of your app

Alternatively, you can upload your existing application via FTP, SFTP or clone it from your Git repository.

Step 4 - Build your Next.js application

Adjust your next.config.ts and make sure output option is set to standalone:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
    output: 'standalone',
};

export default nextConfig;

Now create a production build via

npm run build

Step 5 - Copy static files into the document root

To serve the static files directly via Apache, we have to copy the files into a directory Apache can access.

In this example, the document root of the domain is set directly to the public_html directory via konsoleH.

Run the commands below inside the directory of your app.

Replace holu with the actual username of your current user.

cp -r public/* /usr/www/users/holu
mkdir /usr/www/users/holu/_next
cp -r .next/static/ /usr/www/users/holu/_next

This step has to be repeated every time you rebuild your application via npm run build. You might want to delete old files before.

Step 6 - Start Next.js via PM2

Start your application in cluster mode:

pm2 start --name example-app -i max .next/standalone/server.js
pm2 save

To check if your application is running, run pm2 status. The output should look like this:

$ pm2 status
┌────┬────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name           │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ example-app    │ default     │ 0.1.0   │ cluster │ 78638    │ 85s    │ 0    │ online    │ 0%       │ 97.1mb   │ holu     │ disabled │
│ 1  │ example-app    │ default     │ 0.1.0   │ cluster │ 78645    │ 85s    │ 0    │ online    │ 0%       │ 96.9mb   │ holu     │ disabled │
│ 2  │ example-app    │ default     │ 0.1.0   │ cluster │ 78652    │ 85s    │ 0    │ online    │ 0%       │ 94.3mb   │ holu     │ disabled │
│ 3  │ example-app    │ default     │ 0.1.0   │ cluster │ 78659    │ 85s    │ 0    │ online    │ 0%       │ 94.9mb   │ holu     │ disabled │
│ 4  │ example-app    │ default     │ 0.1.0   │ cluster │ 78670    │ 85s    │ 0    │ online    │ 0%       │ 95.0mb   │ holu     │ disabled │
│ 5  │ example-app    │ default     │ 0.1.0   │ cluster │ 78681    │ 85s    │ 0    │ online    │ 0%       │ 95.0mb   │ holu     │ disabled │
│ 6  │ example-app    │ default     │ 0.1.0   │ cluster │ 78692    │ 85s    │ 0    │ online    │ 0%       │ 95.2mb   │ holu     │ disabled │
│ 7  │ example-app    │ default     │ 0.1.0   │ cluster │ 78703    │ 85s    │ 0    │ online    │ 0%       │ 95.0mb   │ holu     │ disabled │
└────┴────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

Step 7 - Create .htaccess

Add the following .htaccess file to your document root (e.g. public_html):

DirectoryIndex disabled

RewriteEngine On

RewriteCond %{REQUEST_URI} !^/?$
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^.*$ - [NC,L]

RewriteRule ^(.*)$ http://localhost:3000/$1 [P,L]

If your application is using a different port, you have to replace 3000 with your port.

Step 8 - Create a @reboot cronjob

To automatically start PM2 and your application after a server restart, create the following cronjob via the Cronjob-Manager in konsoleH:

@reboot "$HOME/.local/bin/pm2" resurrect

Step 9 - Test

Open your domain in the browser and you should see your Next.js application.

Conclusion

You now have a production ready deployment of your Next.js application on your Managed Server.

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue

Discover our

Managed Servers

Focus on your projects. We'll take care of the configuration and updates.

Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more