Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions community-tutorials/deploying-nextjs-15-to-netcup/01-en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
---
title: Deplyoing Next.js 15+ to Netcup Webhosting
description: Learn how deploy your Next.js 15+ to Netcup Webhosting
level: [intermediate]
updated_at: 2025-04-09
slug: deploying-nextjs-15-to-netcup
author_name: Alexander Schiegl
author_url: https://github.com/Alex-Schiegl
author_image:
author_bio:
tags: [nextjs]
netcup_product_url: https://www.netcup.com/en/hosting
language: en
available_languages: [en]
---

# Introduction
This tutorial shows you how to deploy a Next.js 15+ application on Netcup Webhosting, using a custom server.js file and Node.js support provided through the Plesk interface.

You’ll go through the full process: setting up your Next.js project with a standalone server, preparing your hosting environment, uploading the files, and configuring everything inside the Netcup panel.

If you're working with a shared hosting plan and want to run a custom Node.js app without switching to a VPS, this guide will help you make it work step by step. The screenshots are based on my own setup, so paths and names might include “schiegl-connect” — just replace them with your own values as needed.

# Requirements
* you have your nextjs app running locally on your node environment
* have a Webhosting account on Netcup
* Remind throughout the complete tutorial, to use your own paths. I intentionally added my schiegl-custom paths in screenshots etc, so that you can understand the connecting-parts easier.

# Step 1 - Make you base setup right of nextjs
![base-structure](./images/base-structure-repo.png)

You need an own server.js and NOT the native next.js launcher in node_modules/.bin/next

## 1. Create a server.js:
```javascript
const next = require('next');
const http = require('http');
const os = require('os');

const port = process.env.PORT || 3000;
const app = next({ dev: false }); // Important for production
const handle = app.getRequestHandler();

// Get LAN IP address
function getLocalExternalIP() {
const interfaces = os.networkInterfaces();
for (const name of Object.keys(interfaces)) {
for (const iface of interfaces[name]) {
if (iface.family === 'IPv4' && !iface.internal) {
return iface.address;
}
}
}
return 'localhost';
}

app.prepare().then(() => {
http.createServer((req, res) => {
handle(req, res);
}).listen(port, () => {
const localURL = `http://localhost:${port}`;
const lanURL = `http://${getLocalExternalIP()}:${port}`;

console.log('🚀 Server is running!');
console.log(`🔗 Local: \x1b[36m${localURL}\x1b[0m`);
console.log(`🌐 LAN: \x1b[36m${lanURL}\x1b[0m`);
console.log('✅ Ready to serve!');
});
});
```

## 2. enhance package.json
I needed to change the content of "scripts" to:
```json
"scripts": {
"start": "cross-env NODE_ENV=production node server.js",
"build": "next build",
"dev": "next dev --turbopack",
"lint": "next lint"
}
```

## 3. install cross-env
```bash
npm install --save-dev cross-env
```
in order to be able to run 'npm start' from your dev-Windows machine, if you have one AND it works with Phusion (which is the node.js handler at Netcup).

Test locally to run:
- on Windows: $env:PORT=4001; npm start
- on Linux: PORT=4001 npm start (<- not tested, ChatGPT generated)

If it works on your dev-machine, go on.
It should look like that:
![happy-local-setup](./images/happy-local-setup.png)

Yes, and test your application, if still everything works 😉

# Step 2 - Add Subdomain (if needed)
If you need a subdomain, add it now.
![subdomain](./images/add-subdomain.png)
If not, just make sure you have a default-setting for the page we configure now.
You do not need to change the document root, as we delete it anyways.

# Step 3 - Setup SSL and verify Default working
-> with SSL/TLS Certificates for example https://helpcenter.netcup.com/en/wiki/web-hosting/enabling-ssl-tls

Then, you should see at least that:
![apache](./images/apache-online.png)

That is the default Apache "Hi". We gonna stop that to make place for node.js.

# Step 4 - Disable Proxy (Apache Use)
Click:
![first-nginx](./images/first_step_nginx.png)

Click:
![second-nginx](./images/second_step_nginx.png)

Now, Apache is not bothered anymore and we are ready to setup node.js

# Step 5 - Place your Code on the Webhosting
SSH connect to your server.

It should look like that now:
![ssh-1](./images/ssh-1.png)

Remove the httpdocs
```bash
rm -rf httpdocs/
```
and add your code (via git for example, see this screenshot)
![ssh-2](./images/ssh-2.png)
It should look like in the screenshot now at your setup. So you see our server.js? Remember, this is located in the root /demo.schiegl-connect.at/.

# Step 6 - Setup Node.js in Webhosting Plesk Panel in Netcup
Go into Node Config:
![node-1](./images/node-1.png)

Change the two settings accordingly and activate Node.js (the activation takes a little bit):
![node-2](./images/node-2.png)

Do a npm install the Plesk way and wait until finished:
![node-3](./images/node-3.png)

Then do npm run build and wait until finished
![node-4](./images/node-4.png)

Change the document root to: <your_path>/.next/static (this is only possible now, AFTER the build)
![node-5](./images/node-5.png)

> 💡 When you cannot change the document root here (that is the case when you configure not a subdomain, but the main-domain), then you need to go to CCP Mainpanel:
![node-51](./images/change_main_domain_settings.png)
and add it hard there:
![node-5](./images/change_main_domain_document_path.png)
> in this case, you need to set the "Anwendungsstamm" in the node.js app again. ⚠️ as of 2025-10-10 it was not possible to stop the Apache and Apache forwarding of https://proglovedemo.app/ (Apache was always sitting in the root), I needed to add another subdomain https://fiori.proglovedemo.app and forward to there from proglovedemo.app. In a subdomain, everything works fine as the normal guide was already.

Now do a run start and hope 😊:
![node-6](./images/node-6.png)

Should look like that:
![node-7](./images/node-7.png)

# Conclusion
Yes, works:

![final](./images/final.png)

Have a nice day!

# Licence

[MIT](https://github.com/netcup-community/community-tutorials/blob/main/LICENSE)

Copyright (c) 2025 netcup

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Contributor's Certificate of Origin
By making a contribution to this project, I certify that:

1) The contribution was created in whole or in part by me and I have the right to submit it under the licence indicated in the file; or

2) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate licence and I have the right under that licence to submit that work with modifications, whether created in whole or in part by me, under the same licence (unless I am permitted to submit under a different licence), as indicated in the file; or

3) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.

4) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the licence(s) involved.

5) The trademark rights, including the names schiegl-connect and other trademarks of Schiegl Informatics FlexCo, remain the exclusive property of Schiegl Informatics FlexCo. Their use in this tutorial is solely for educational and illustrative purposes, and does not imply any affiliation, endorsement, or transfer of rights.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.