How to Deploy a Node.js App on ScalaHosting Using SPanel

How to Deploy a Node.js App on ScalaHosting Using SPanel

Deploy a Node.js App on ScalaHosting Using SPanel

Deploying a Node.js application on a VPS involves more than moving your project files to a server. The production dependencies have to install correctly, the Node.js process has to stay running after you close SSH, the domain has to reach the correct process, and you need a repeatable way to push the next release without rebuilding the setup from scratch.

ScalaHosting’s SPanel separates those jobs into different tools. Git Version Control handles the repository, NodeJS Manager runs the application, the domain tools connect the hostname, SSL Certificates handles the certificate state, and the log and resource pages help you verify what happens after the application starts.

This guide follows that sequence from the beginning. Your ScalaHosting plan, domain, GitHub repository, username, and application port can all be different from mine. I will show you the values I used so you can see a complete working example, but I will also point out which values you should replace with your own.

If you are still comparing the platform itself, the HostAdvice ScalaHosting VPS review covers the broader VPS, SPanel, support, and hosting experience. Here, the focus is the Node.js deployment.

Deploy Your Node.js App on a Managed VPS
Use ScalaHosting VPS and SPanel to deploy, run, secure, and monitor a Node.js application with a repeatable workflow.
Visit ScalaHosting
Takeaways
  • Git Version Control manages the application files, while NodeJS Manager manages the long-running Node.js process.
  • Use .autodeploy to install production dependencies after a repository pull, then restart NodeJS Manager after server-side code changes.
  • Verify the deployment layer by layer: localhost, hostname routing, public DNS, HTTPS, logs, resource usage, and monitoring.

How This Node.js Deployment Fits Together

Before you start configuring SPanel, it helps to know what each part of the setup is responsible for. The deployment in this guide has seven stages:

  • You develop and test the Node.js application on your local machine.
  • You push the application to a GitHub repository.
  • SPanel Git Version Control clones that repository onto the VPS and pulls later changes.
  • A repository-level .autodeploy script installs the production dependencies after a pull.
  • SPanel NodeJS Manager starts and supervises the Node.js process.
  • SPanel’s web-server layer connects your domain or subdomain to the internal Node.js port.
  • You verify the public site, SSL certificate, logs, resource usage, and monitoring.

Git Version Control and NodeJS Manager are especially important to keep separate in your head. Git controls the files on disk. NodeJS Manager controls the long-running application process that is using those files.

That difference mattered when I deployed version 1.1.0. The repository on the VPS had already updated to 1.1.0, but the health endpoint still reported version 1.0.0 because the old Node.js process was still running. Restarting the application in NodeJS Manager loaded the new release.

The internal application port works in a similar way. My Express app listened on port 3100, but visitors did not have to add that port to the URL. SPanel accepted the request for the public hostname and passed it to the process internally.

Tip
Treat the deployment as a series of layers. Verify one layer before moving to the next. If the Node.js process cannot answer on localhost, there is no value in troubleshooting public DNS yet.

Before You Start

I used a small Express project called HostAdvice Node.js Demo. It serves a normal webpage, exposes a health endpoint, reports runtime information, and includes a controlled error route that I used later to test SPanel’s logs.

My test environment used the following values:

  • ScalaHosting plan: Build #1 managed VPS
  • Resources shown in SPanel: 4 GB RAM and 50 GB storage
  • SPanel account username: nodeapp
  • Test parent domain: moonshotcoil.eu
  • Test application subdomain: node.moonshotcoil.eu
  • GitHub repository: https://github.com/KimothoKarani/hostadvice-nodejs-demo
  • Branch: main
  • Repository path on the VPS: /home/nodeapp/hostadvice-nodejs-demo
  • Subdomain document root: /home/nodeapp/node.moonshotcoil.eu
  • Internal Node.js port: 3100
  • Node.js version on the VPS: v20.20.2
  • npm version on the VPS: 10.8.2

You do not need the same Build #1 plan to follow the workflow. Another ScalaHosting managed VPS plan can have different CPU, memory, and storage allocations. The important requirement is that your SPanel account exposes the Git and Node.js tools used in the steps below.

The same applies to the domain. I used moonshotcoil.eu because it was my test domain. If your domain is example.com, you might deploy the application at app.example.com, api.example.com, portal.example.com, or the main domain itself.

If you do not already have an Express project, our guide on how to create a Node.js and Express web server. For this tutorial, your application should already run locally before you start configuring the VPS.

I also recommend checking three application details before you continue.

First, package.json should contain a production start command. Mine uses:

"scripts": {

  "start": "node server.js"

}

Second, your server should be able to listen on a configurable port. My server.js includes:

const PORT = Number(process.env.PORT) || 3100;

const HOST = process.env.HOST || "0.0.0.0";

Third, add a lightweight health endpoint if your application does not already have one. Mine returns the current version, process uptime, and timestamp:

app.get("/health", (req, res) => {

  res.status(200).json({

    status: "ok",

    version: packageJson.version,

    uptimeSeconds: uptimeSeconds(),

    timestamp: new Date().toISOString()

  });

});

The health endpoint becomes one of the most useful checks in this deployment because it tells you what the running process is serving, not merely what files are present on the VPS.

Step 1: Create a Dedicated SPanel Hosting Account

Start in the SPanel Admin Interface. This is the server-level area where you create and manage hosting accounts.

SPanel Admin Interface dashboard for managing the VPS

Open Accounts Management and click Create a New Account.

SPanel Create a New Account page with account creation options

For a custom Node.js application, select Create an Empty Account. You do not need a WordPress or website-builder preset because NodeJS Manager will run the application later.

SPanel account setup with Create an Empty Account selected

Next, choose Use Own Domain and enter your own parent domain and account username. I used moonshotcoil.eu as the parent domain and nodeapp as the username.

If your domain is example.com, enter example.com here. Do not copy moonshotcoil.eu from my screenshots. It is only the test domain used for this walkthrough.

SPanel account form with a parent domain and account username

Before creating the account, open Limit Resources & Features. SPanel lets you control storage, inodes, databases, CPU, memory, processes, email limits, and other account-level resources.

SPanel resource limits and feature settings for a hosting account

I left the limits unrestricted during this test because the VPS was being used for the deployment exercise. That removed account quotas as a variable while I was verifying the Node.js workflow.

If several client sites or applications share your VPS, you can set limits according to the resources each account should be allowed to consume. I would base those values on actual usage after the application has been running rather than choosing arbitrary limits during the first setup.

After you finish the account settings, create the account and wait for SPanel to provision it.

SPanel provisioning status while creating a hosting account

When provisioning finishes, return to Accounts Management. Confirm that the account appears with Active status.

SPanel Accounts Management list with a new active account

Click Manage beside the account.

SPanel opens the account-level User Interface. From this screen, you can access Domains, Subdomains, DNS Editor, Git Version Control, Logs, SSL Certificates, SSH Terminal, Resource Usage, and NodeJS Manager.

SPanel account dashboard with domain, Git, SSH, SSL, logs, and NodeJS Manager tools

The account is now ready. From this point, most of the work happens in this User Interface. We will only return to the Admin Interface when we need a server-level setting such as SSH access.

Tip
Tip: Use your parent domain at the account level if you want SPanel to manage several subdomains under the same account. I initially started with my Node.js subdomain as the account domain and later changed it to the parent domain. Starting with the parent domain avoids that extra correction.

Step 2: Connect Your Domain to the VPS

Option 1: Let SPanel manage the DNS zone

This is the route I used for the test domain. The domain was initially delegated to nameservers from another hosting provider:

ns1-ahimsa.vivawebhost.com

ns2-ahimsa.vivawebhost.com

At the registrar, I replaced those nameservers with the two nameservers assigned to the ScalaHosting VPS:

ns1.cloud-7522f7.managed-vps.net

ns2.cloud-7522f7.managed-vps.net

Once you save the change, SPanel becomes authoritative for the DNS zone after the delegation propagates. Public resolvers may continue returning the old state for a while, so you do not need to stop the deployment while you wait.

Option 2: Keep your current DNS provider

If your domain already uses Cloudflare or another DNS provider, you do not have to move the whole DNS zone to SPanel. Keep your existing nameservers and create an A record for the hostname you want to use for the Node.js application.

For example, if you want the app at app.example.com, you could create:

Type:  A

Name:  app

Value: YOUR_VPS_IPV4_ADDRESS

This approach is often better when the parent domain already handles production email, other websites, verification records, or services you do not want to migrate.

If you need to check which nameservers currently control your domain, HostAdvice has a guide on how to check your domain’s nameservers.

I continued with SPanel-managed DNS for the rest of this walkthrough. After saving the nameserver change, I moved on to creating the application subdomain instead of waiting for public DNS propagation to finish.

Tip
Changing nameservers affects the whole DNS zone. If the domain already carries production services, review the existing records before moving authority to SPanel. If you only need one new application hostname, an A record at your current DNS provider may be the simpler option.

Step 3: Create Your Application Subdomain

Now create the hostname that users will open for your Node.js application. In the account User Interface, open Domains and then select Subdomains.

SPanel Subdomains page before creating an application hostname

Click Add a New Subdomain.

Choose a subdomain that fits your project. You might use app, api, portal, dashboard, or another name. In my test, I used node under moonshotcoil.eu, which created node.moonshotcoil.eu.

My configuration was:

  • Subdomain: node
  • Parent domain: moonshotcoil.eu
  • Resulting test hostname: node.moonshotcoil.eu
  • Document root: /home/nodeapp/node.moonshotcoil.eu

The form also shows a PHP version. Leave that setting at the default for this Node.js deployment. SPanel uses the same subdomain tool for PHP websites, but NodeJS Manager controls the Node.js application later.

Create the subdomain. After SPanel returns to the subdomain list, confirm that your new hostname appears with the expected document root.

SPanel Existing Subdomains list showing the application hostname and document root

The hostname now exists inside the SPanel account. The next step is to verify that the DNS zone contains a record for it.

Step 4: Verify the DNS Record for Your Application Hostname

Open Domains and then select DNS Editor.

SPanel DNS Editor with DNS records for the account domain

Find the A record for the subdomain you just created. In my test, SPanel automatically created records for node.moonshotcoil.eu and www.node.moonshotcoil.eu.

The important test record pointed to the VPS IP:

node      A   217.174.148.177

If you are using app.example.com instead, you should see the corresponding app record pointing to your own VPS IP.

At this stage, the SPanel zone itself was correct, but public DNS had not yet caught up with the nameserver change. A propagation check still showed the test domain as unresolved from the public locations I checked.

That did not prevent the rest of the deployment. The next several steps work directly with the VPS, so I continued with Git and Node.js while DNS propagated in the background.

Step 5: Clone Your Node.js Repository From GitHub

With the account and hostname prepared, bring the application source code onto the VPS.

SPanel Files menu with Git Version Control selected

Open Files and then select Git Version Control.

SPanel Git Version Control page before adding a repository

Click Create a New Repository.

Fill the form with your own repository details. My test used:

  • Repository Name: hostadvice-nodejs-demo
  • Repository Path: /home/nodeapp/hostadvice-nodejs-demo
  • Repository Type: Public
  • Repository URL: https://github.com/KimothoKarani/hostadvice-nodejs-demo.git
  • Branch: main
  • Automatic Pull & Deploy: Disabled

SPanel Git repository form configured for a Node.js project

The repository path is deliberately separate from the subdomain document root. My source code lives at /home/nodeapp/hostadvice-nodejs-demo, while the subdomain document root is /home/nodeapp/node.moonshotcoil.eu.

That works because NodeJS Manager runs the application from the repository directory. SPanel’s web server later connects the public hostname to the running process, so the Git repository does not have to live directly inside the public document root.

I also left Automatic Pull & Deploy disabled. During the first deployment, manual pulls make the state changes easier to verify because you decide exactly when the VPS receives a new commit.

Click Clone Repository and wait for SPanel to finish cloning the project.

After the clone succeeds, the repository appears in Git Version Control with the tracked branch.

SPanel Git Version Control list showing the cloned repository

The files are now on the VPS, but the application is not running yet and the production dependencies have not been installed. Before installing anything, verify the server environment over SSH.

Tip
Tip: Keeping the repository outside the public document root gives you a cleaner separation between source code and the public hostname. NodeJS Manager, not direct file serving, is responsible for running the application.

Step 6: Enable SSH and Verify the Server Environment

Open Tools and then select SSH Terminal.

In my account, SPanel initially displayed a message saying SSH was disabled.

SPanel SSH Terminal showing that SSH access is disabled

SSH permission is controlled from the server-level Admin Interface. Return to the Admin Interface, open Accounts Management, and select Manage SSH Access.

Turn SSH on for the account. My server displayed port 6543.

SPanel Manage SSH Access page with SSH enabled for the account

Now open a terminal on your local computer and connect to the VPS. I used the server IP because public DNS was still propagating:

ssh -p 6543 nodeapp@217.174.148.177

Replace nodeapp, the IP address, and the SSH port with the values shown for your own hosting account.

After the connection succeeds, verify the account identity and home directory:

whoami

pwd

My server returned:

nodeapp

/home/nodeapp

Next, check the Node.js and npm versions:

node –version

npm –version

My environment returned:

v20.20.2

10.8.2

Then move into the repository SPanel cloned and verify the Git state:

cd ~/hostadvice-nodejs-demo

pwd

git branch –show-current

git status

ls -la

I confirmed that the repository path was /home/nodeapp/hostadvice-nodejs-demo, the active branch was main, the working tree matched origin/main, and package.json, package-lock.json, server.js, and the public directory were present.

SSH terminal verifying the Node.js, npm, and Git environment

You now have a clean baseline. The code is in the expected directory, the account can run Node.js and npm, and Git is tracking the correct branch. The next step is to install the production dependencies in a repeatable way.

Step 7: Add .autodeploy and Install Production Dependencies

SPanel Git Version Control can run deployment commands after it pulls a repository. In the version I tested, those commands live in an executable file named .autodeploy at the repository root.

I added the file to the local project so the production installation step would be version-controlled with the application.

My .autodeploy file contains:

#!/bin/bash

set -e

cd "$(dirname "$0")"

echo "Installing production dependencies..."

npm ci --omit=dev

echo "Deployment dependencies installed successfully."

The first line runs the script with Bash. set -e stops the script when a command fails instead of continuing through the rest of the deployment. The cd command ensures npm runs from the repository directory.

I used npm ci because package-lock.json is committed. That installs the dependency tree recorded in the lock file and gives the production server a predictable package set. The –omit=dev option leaves out development-only packages because this Express app does not need them at runtime.

If your own application needs a build step or uses development dependencies during compilation, adjust the script to match your project. Do not copy –omit=dev blindly if your build depends on packages stored under devDependencies.

Make the deployment script executable:

chmod +x .autodeploy

ls -l .autodeploy

Then commit and push the file to GitHub:

git add .autodeploy

git commit -m “Add SPanel deployment script”

git push origin main

After GitHub has the new commit, return to SPanel and open Git Version Control. Open Actions beside your repository.

SPanel Git Version Control Actions menu for a repository

Choose Pull & Deploy.

SPanel pulls the new commit and triggers .autodeploy. In my test, it displayed a success message confirming that the repository was updated and the deployment scripts were triggered.

SPanel confirmation that Pull and Deploy completed successfully

Return to SSH and verify that the dependencies actually installed:

cd ~/hostadvice-nodejs-demo

ls -ld node_modules

npm list express –depth=0

git status

node_modules existed, Express was installed, and the Git working tree remained clean because node_modules is excluded from the repository.

The application directory is now ready for NodeJS Manager.

Tip
Tip: Keep deployment commands in .autodeploy rather than relying on a series of manual SSH commands. When the project later needs a build command or another repeatable preparation step, you can review and version that change with the code.
Run Node.js With SPanel
Deploy your custom Node.js application through SPanel NodeJS Manager, with managed process control and dedicated VPS resources.
Visit ScalaHosting

Step 8: Deploy the Application With NodeJS Manager

Open Software and then select NodeJS Manager.

SPanel Software menu with NodeJS Manager selected

Click Deploy a New App.

SPanel NodeJS Manager page with the Deploy a New App option

For Application, choose Custom. My interface also offered n8n Automation, but this project is a normal Express application.

Next, open the Application URL dropdown and select the domain or subdomain where you want the app to appear.

SPanel NodeJS Manager application URL selector

I selected node.moonshotcoil.eu because that was my test hostname. You should select the hostname you created for your own application, such as app.example.com.

Leave the optional URL folder empty if you want the application at the root of the hostname.

For Application Path, enter the repository directory. Mine was:

/home/nodeapp/hostadvice-nodejs-demo

Finally, choose an available internal port. I used 3100 because the Express app already falls back to that port.

My completed configuration was:

  • Application: Custom
  • Application URL: node.moonshotcoil.eu
  • Application Path: /home/nodeapp/hostadvice-nodejs-demo
  • Port: 3100

SPanel Node.js deployment form with application path and internal port

The Application URL and port solve different parts of the request. The URL is what the visitor opens. The port is where the Node.js process listens inside the server. SPanel connects the two.

Click Deploy and wait for NodeJS Manager to create the application.

SPanel NodeJS Manager showing the new application deployment

After deployment, NodeJS Manager showed my application as Online on port 3100 and displayed its memory, CPU, and uptime information.

SPanel NodeJS Manager showing an online Node.js application

Do not treat Online as the final verification. It confirms that SPanel started a process, but the next step is to verify that the application itself can answer requests.

Step 9: Verify the Running Application

First, test the Node.js process directly

Keep your SSH session open and call the health endpoint on localhost:

curl http://127.0.0.1:3100/health

Replace 3100 if you chose a different internal port.

My response was:

{

  “status”: “ok”,

  “version”: “1.0.0”,

  “uptimeSeconds”: 379,

  “timestamp”: “2026-10-01T11:35:58.581Z”

}

This request bypasses public DNS and the public hostname. It talks directly to the Express process and proves that Node.js is listening on the expected port.

I also checked the richer runtime endpoint:

curl http://127.0.0.1:3100/api/status

That response confirmed the application name, version 1.0.0, production environment, Node.js v20.20.2, process ID, and deployment target.

Then, test SPanel’s hostname routing

Public DNS had not finished propagating during my test, but I could still verify that SPanel was routing the hostname to Node.js. From my local computer, I used curl –resolve.

The command below tells curl to connect the test hostname to the VPS IP for this request while still sending the correct hostname to the web server:

curl –resolve node.moonshotcoil.eu:80:217.174.148.177 \

  http://node.moonshotcoil.eu/health

For your own application, replace the hostname and VPS IP with your values.

The request returned the same healthy JSON response. I also checked the homepage headers:

curl –resolve node.moonshotcoil.eu:80:217.174.148.177 \

  -I http://node.moonshotcoil.eu

The server returned HTTP 200 through Apache.

At this point, two different layers had passed. The localhost request proved the Node.js process worked. The hostname request proved SPanel’s web-server layer was forwarding the test hostname to that process.

Tip
Tip: Always test localhost before the public hostname. If localhost fails, stay with the application, dependencies, port, or process. If localhost works and the hostname fails, move outward to SPanel routing, DNS, or SSL.

Step 10: Check the Initial SSL State

After the HTTP path worked, I checked the certificate state before continuing with the release test.

Open Tools and select SSL Certificates.

SPanel SSL Certificates page showing a temporary self-signed certificate

In my test, node.moonshotcoil.eu still had a self-signed certificate. SPanel stated that it would replace the temporary certificate with a Let’s Encrypt certificate once the domain pointed to the server.

A forced HTTPS request reached the server but failed certificate validation because the certificate was self-signed. That was consistent with the state shown in SPanel.

I did not try to hide that result with a permanent verification bypass. The Node.js process and HTTP routing were already proven, so I left the certificate alone and continued testing the deployment workflow while public DNS propagated.

We will return to SSL after the public hostname resolves normally.

Step 11: Deploy an Update and Reload the Running Process

The first deployment was working, so I tested the part that matters after launch: releasing a new version.

I changed the application from version 1.0.0 to version 1.1.0 and changed the visible heading from Node.js deployment on ScalaHosting to Node.js deployment verified with SPanel.

On the local machine, I updated the package version without automatically creating a Git tag:

npm version 1.1.0 –no-git-tag-version

After testing the change locally, I committed and pushed the release:

git add package.json package-lock.json public/index.html

git commit -m “Release version 1.1.0”

git push origin main

Once GitHub had the new commit, I returned to SPanel, opened Git Version Control, opened the repository Actions menu, and selected Pull & Deploy.

After SPanel completed the pull, I verified the files over SSH:

git log -1 –oneline

grep ‘”version”‘ package.json

The repository showed the new commit, and package.json contained version 1.1.0. The files on disk were current.

Then I checked the running application:

curl http://127.0.0.1:3100/health

The health endpoint still reported version 1.0.0.

That result did not mean Pull & Deploy had failed. Git had updated the files correctly. The old result came from the already-running Node.js process, which had not reloaded the new application state.

Return to NodeJS Manager and open Actions beside the application.

SPanel NodeJS Manager Actions menu with Restart available

Select Restart. Wait for the application to return to Online status, then run the health check again:

curl http://127.0.0.1:3100/health

This time the response showed:

{

  “status”: “ok”,

  “version”: “1.1.0”,

  “uptimeSeconds”: 12,

  “timestamp”: “2026-10-01T11:48:12.489Z”

}

The version changed to 1.1.0, and the low uptime confirmed that a new process had started.

I also checked the public page through the forced hostname route:

curl –resolve node.moonshotcoil.eu:80:217.174.148.177 \

  http://node.moonshotcoil.eu | grep -i “verified with SPanel”

The response contained:

<h1>Node.js deployment verified with SPanel</h1>

The update path was now proven: GitHub received the release, SPanel pulled the new files, NodeJS Manager restarted the process, and the health endpoint confirmed the new version.

Tip
Insight: Pull & Deploy and Restart are separate parts of this deployment. The first changes the repository on disk. The second reloads the long-running Node.js process. Verify both instead of assuming one automatically implies the other.

Step 12: Test SPanel’s Application and Domain Logs

Generate one controlled HTTP 500 response

With version 1.1.0 running, I generated a known failure so I could trace it through SPanel without crashing the application.

The demo includes /api/test-error. It writes a controlled message to stderr and returns HTTP 500.

From my local machine, I requested that route through SPanel’s hostname path:

curl –resolve node.moonshotcoil.eu:80:217.174.148.177 \

  -i http://node.moonshotcoil.eu/api/test-error

The response was:

HTTP/1.1 500 Internal Server Error

{“status”:”error”,”message”:”Intentional test error for log verification.”}

I immediately checked /health again and it still returned status ok with version 1.1.0. That told me one request had failed, but the Node.js service itself was still healthy.

Compare the application log with the access log

First, return to NodeJS Manager. Open Actions beside the application and select View Logs.

SPanel NodeJS Manager application log output for the test error

My SPanel version separated Output log and Error log. In Error log, the controlled request appeared with the application message:

[controlled-error] … Intentional test failure requested.

SPanel domain access log showing the controlled HTTP 500 request

Next, open Domains and select Logs. Choose your application hostname and open the Access tab.

The access log showed the failed route with status 500 and the later health request with status 200:

GET /api/test-error HTTP/1.1″ 500

GET /health HTTP/1.1″ 200

SPanel domain error log for the Node.js application

The two logs answer different questions. The domain access log tells you which request arrived, when it arrived, and which status the server returned. The NodeJS Manager log tells you what the application itself wrote at about the same time.

Tip
Insight: When you troubleshoot a real 500 response, start with the failing URL and timestamp in the access log. Then inspect the Node.js log around that same time. That gives you a request to anchor the investigation instead of scanning unrelated output.

Step 13: Review Resource Usage

After confirming the logs, open Tools and select Resource Usage.

SPanel Resource Usage page showing account resource statistics

Because I left the account limits unrestricted, SPanel showed unlimited account-level CPU, memory, transfer, IOPS, and process limits. The page also displayed disk usage, inode usage, CPU history, memory history, and the top account processes.

In my test, Top 5 Processes included:

  • node
  • npm run start
  • PM2 v7.0.4: God

That gives useful context about this particular SPanel environment. The application was not attached to my interactive SSH session, and PM2 appeared among the processes while NodeJS Manager kept the app online.

I would not assume that every SPanel version exposes the exact same process names. The operational point is that NodeJS Manager was supervising the service after I disconnected from SSH.

Once the app receives real traffic, this page also helps you revisit the resource limits from Step 1. Real CPU and memory history gives you a much better basis for account limits than guessing before launch.

Step 14: Review Website Monitoring Before You Enable It

Open Domains and select Website Monitoring.

SPanel Website Monitoring page before monitoring is enabled

The main table in my SPanel version included columns for Uptime, Average Speed, TTFB, Slow Events, and SSL Status.

Open Actions beside your application hostname and select Edit.

SPanel Website Monitoring Actions menu with Edit and History

SPanel Website Monitoring settings with the Enable Monitoring toggle

The configuration was simpler than I expected. My SPanel version exposed one Enable Monitoring toggle rather than separate controls for individual checks or polling intervals.

I left monitoring disabled at this stage because public DNS was still propagating and the application hostname still had the temporary self-signed certificate. Starting monitoring then could have filled the first history with setup-state DNS or certificate failures instead of the stable public application.

We will enable it after public DNS and trusted HTTPS are working.

Step 15: Finish Public DNS, HTTPS, and the Browser Test

Confirm the public DNS record

The original test hostname, node.moonshotcoil.eu, never began resolving publicly. After ScalaHosting support checked it, they confirmed that the DNS was not resolving correctly and suggested checking the domain with its registrar.

Rather than leave the rest of the deployment blocked by a domain-level problem, I moved the public test to a second domain I already controlled.

I used hostadvice.tech, which was already registered and using Hostinger’s DNS nameservers. I did not move the domain’s nameservers to ScalaHosting. Instead, I created only the application subdomain at Hostinger and pointed it to the ScalaHosting VPS.

The public DNS record was:

Type:  A

Name:  node

Value: 217.XXX.XXX.177

That created node.hostadvice.tech while leaving the rest of hostadvice.tech on its existing DNS setup. If your own domain already uses Cloudflare, Hostinger, GoDaddy, or another DNS provider, you can use the same approach: create an A record for the application hostname and point it to your VPS.

After creating the record, I checked it against both Cloudflare’s and Google’s public DNS resolvers:

dig @1.1.1.1 +short node.hostadvice.tech

dig @8.8.8.8 +short node.hostadvice.tech

Both returned the ScalaHosting VPS IP:

217.XXX.XXX.177

That confirmed the hostname was resolving publicly. I then added hostadvice.tech to the existing nodeapp account in SPanel, created node.hostadvice.tech as a subdomain, and changed the existing NodeJS Manager application URL from the earlier test hostname to node.hostadvice.tech. The repository path and internal port did not change.

SPanel Git and NodeJS Manager configuration for the public application hostname

A normal HTTP request could now reach the application without curl –resolve:

curl -i http://node.hostadvice.tech/health

The response returned HTTP 200 and the running application reported version 1.1.0. At this point, public DNS and SPanel’s hostname routing were both working through the normal internet path.

Tip
Tip: You do not have to move an entire domain’s nameservers just to deploy one Node.js application. If the domain already has working DNS elsewhere, pointing only the application subdomain to the VPS can be simpler and less disruptive.

Install and verify the trusted SSL certificate

With node.hostadvice.tech resolving publicly, I returned to Tools and opened SSL Certificates in SPanel. The hostname still had the temporary self-signed certificate, so a normal HTTPS request failed certificate validation.

From the Actions menu for node.hostadvice.tech, I installed SPanel’s free Let’s Encrypt certificate.

SPanel SSL Certificates action to install a Let’s Encrypt certificate

After the installation completed, I repeated the HTTPS health check without using -k or disabling certificate verification:

curl -i https://node.hostadvice.tech/health

This time the request succeeded over HTTPS:

HTTP/2 200

strict-transport-security: max-age=15552000; includeSubDomains

content-type: application/json; charset=utf-8

server: Apache

{“status”:”ok”,”version”:”1.1.0″,”uptimeSeconds”:206,”timestamp”:”2026-10-02T13:08:54.968Z”}

The important change is not simply the 200 response. curl accepted the certificate normally, which confirms that the hostname was now serving a trusted certificate instead of the earlier self-signed one.

For more background on certificate setup and verification, we have a guide on how to get an SSL certificate.

Open the live application in the browsersting/security/how-to-get-an-ssl-certificate/

After DNS and HTTPS were working from the terminal, I opened the application in a normal browser:

https://node.hostadvice.tech

The page loaded successfully and displayed the version 1.1.0 deployment. The live status panel showed the Node.js runtime as v20.20.2, the environment as Production, and the application health as Healthy.

Node.js demo application running publicly over HTTPS

I also checked plain HTTP separately:

curl -I http://node.hostadvice.tech

In this test, HTTP returned 200 OK rather than redirecting automatically to HTTPS. That does not prevent the HTTPS deployment from working; it means both HTTP and HTTPS remained available.

If you require every HTTP request to be forced to HTTPS, configure and test that redirect separately for your own application instead of assuming certificate installation enables it automatically.

At this point, the public deployment was complete. The hostname resolved without a local override, SPanel routed it to the managed Node.js process, Let’s Encrypt passed normal certificate validation, and the application loaded successfully in the browser.

Tip
Insight: Verify the public deployment from more than one angle. DNS lookup confirms the hostname, the HTTPS health request confirms the certificate and backend path, and the browser confirms the user-facing application. A successful result in one layer does not automatically prove the others.
Secure and Monitor Your Node.js Deployment
Use managed VPS hosting with SSL, logs, resource visibility, and monitoring to keep your Node.js application reliable after launch.
Visit ScalaHosting

Step 16: Enable Website Monitoring

Once the public hostname and HTTPS connection were stable, I enabled SPanel’s Website Monitoring for node.hostadvice.tech. This is the right point to start monitoring because the history will represent the working public deployment rather than the earlier DNS and self-signed-certificate state.

Open Domains and select Website Monitoring. SPanel lists the domains and subdomains on the account along with columns for Status, Uptime, Average Speed, TTFB, Slow Events, and SSL Status.

SPanel Website Monitoring page with monitoring disabled for the application

Find your application hostname, open Actions, and select Edit.

SPanel Website Monitoring Actions menu for the public application hostname

Initially, Enable Monitoring was off. After switching it on, SPanel exposed additional monitoring controls.

SPanel Website Monitoring setup with monitoring enabled

For this test, I configured:

  • Check interval: 1 minute
  • Monitor Website Speed: On
  • Check SSL: On
  • Monitor Website Health: On

SPanel also provides a speed-threshold field and a Check Text field. I left those unset for this initial test because I wanted the first monitoring run to establish the basic availability, response-time, SSL, and health data before adding more specific alert conditions.

SPanel Website Monitoring configuration with speed, SSL, and health checks

After saving the settings, SPanel confirmed that website monitoring for node.hostadvice.tech had been set up and enabled successfully.

The first measurements appeared almost immediately. At the time of my check, SPanel reported:

  • Status: Enabled
  • Uptime: 100%
  • Average Speed: 0.07 seconds
  • TTFB: 0.07 seconds
  • Slow Events: 0

SPanel Website Monitoring results showing uptime, speed, TTFB, and slow events

Treat those numbers as an initial sample rather than a performance benchmark. Monitoring had only just been enabled, so there was not enough history to say how the application performs over longer periods or under changing traffic.

The History view confirmed that limitation. SPanel displayed a message saying there was no historical data for the domain yet and to wait a few days after enabling monitoring.

SPanel Website Monitoring History showing that no historical data is available yet

That is a useful place to end the initial deployment test. You have confirmed that the service is reachable now, while SPanel can continue collecting uptime and response-time data after the deployment is complete.

Tip
Insight: Do not treat the first monitoring sample as your long-term performance result. Give SPanel time to build history, then use the uptime, average speed, TTFB, slow-event, and SSL data to spot changes from the application’s normal baseline.

Your Normal Release Workflow After the First Deployment

The first deployment is long because you are creating the account, DNS setup, Git repository, managed process, SSL state, logs, and monitoring for the first time. You do not repeat all of those steps for every release.

For this project, the normal release process is:

  • Make the code change and test it locally.
  • Commit the change and push it to the production branch on GitHub.
  • Open Git Version Control in SPanel and run Pull & Deploy.
  • Open NodeJS Manager and restart the managed application when server-side code has changed.
  • Call /health and confirm the running version is the release you expected.
  • Open the public HTTPS page and check the logs if the result is not correct.

That sequence reflects the version 1.1.0 test. Git controls the source files, .autodeploy handles the dependency step, NodeJS Manager controls the running process, and /health tells you what that process actually loaded.

If you later enable Automatic Pull & Deploy, test the release path again. Automation should remove a known manual step, not hide which state the application is in.

Production Checks I Would Add Next

1. Keep application secrets outside Git

API keys, database passwords, JWT secrets, and other credentials should not be committed to the repository. Use the environment-variable mechanism supported by your deployment setup rather than hard-coding credentials in server.js or committing a .env file.

This demo did not require application secrets, so I have not added an untested SPanel environment-variable workflow to the deployment steps. If your application depends on secrets, confirm the supported method in your SPanel version before the first production release.

2. Assign internal ports deliberately

If several Node.js services share the same VPS, each running process needs its own available internal port. The public hostnames can still use normal HTTP and HTTPS.

For example:

  • api.example.com can use internal port 3100.
  • admin.example.com can use internal port 3101.
  • jobs.example.com can use internal port 3102.

Document the assignments so you do not create port conflicts later.

3. Decide how much Git deployment automation you need

I kept Automatic Pull & Deploy disabled while proving the workflow. That let me see each state separately: the commit existed on GitHub, SPanel pulled it, .autodeploy ran, and NodeJS Manager restarted the process.

Once that behavior is predictable, you can decide if automatic pulling fits your release process. Keep a way to verify the deployed commit, dependency step, restart behavior, and health endpoint even after you automate more of the release.

4. Know what Git can and cannot recover

Git gives you application source history, but a production application may also depend on database data, uploaded files, generated assets, and secrets that are not stored in the repository.

SPanel includes backup tools, so review what your hosting backups cover and how a restore works before the application contains data you cannot recreate. A source-code rollback and a data restore solve different problems.

5. Keep the health endpoint small and useful

The /health endpoint was one of the most useful pieces of this test. It separated the state of the running process from the files on disk, DNS, SSL, and the frontend.

Keep your own endpoint quick to answer and expose only the information you are comfortable making visible. If you later add database or downstream-service checks, decide carefully which dependency failures should make the health check fail.

Troubleshooting ScalaHosting Node.js Deployments

1. NodeJS Manager says Online, but the app does not respond

Start with the Node.js process itself:

curl http://127.0.0.1:3100/health

Use your own internal port if it differs. If localhost fails, focus on the application path, startup command, dependencies, port, or NodeJS Manager logs. Do not start with public DNS.

If localhost works, move outward and test the hostname. If public DNS is not ready yet, use curl –resolve. If the hostname fails while localhost works, focus on the SPanel Application URL, domain configuration, web-server routing, DNS, or SSL.

2. The app works on localhost but not through the domain

Confirm that NodeJS Manager uses the hostname you are requesting. Then confirm the domain or subdomain exists in SPanel and the DNS record points to the VPS.

dig +short app.example.com

dig NS example.com +short

If you recently changed nameservers, the authoritative SPanel zone can already be correct while public resolvers still return an older state.

3. Pull & Deploy succeeds, but the old version is still running

Check the repository first:

git log -1 –oneline

grep ‘”version”‘ package.json

If the files contain the new release but /health still reports the previous version, restart the application in NodeJS Manager. That is exactly what happened during my 1.0.0 to 1.1.0 test.

4. Pull & Deploy runs, but dependencies are missing

Check that .autodeploy exists and is executable:

ls -l .autodeploy

If the script uses npm ci, confirm package-lock.json is committed. You can then run the same npm command manually over SSH to isolate the installation error.

5. SSH Terminal says SSH is disabled

Return to the Admin Interface, open Accounts Management, and select Manage SSH Access. Enable the correct hosting account and use the port shown on that page. My server used 6543, but your server may show a different port.

6. HTTPS still shows a self-signed certificate

Check public DNS first. In my test, the Node.js process and SPanel’s HTTP hostname routing were already working while the application hostname still had the temporary self-signed certificate.

Do not treat curl -k as the finished solution. It only bypasses certificate validation for that request. The final state is a trusted certificate that passes normal curl and browser validation.

7. A request returns HTTP 500

Open Domains and select Logs. Find the failing request in the Access tab and note the URL, timestamp, and status. Then open NodeJS Manager, select View Logs, and inspect the application output at the same time.

The access log tells you which request failed. The application log provides the Node.js-side context that can explain why.

8. The app stops when you close SSH

Do not rely on a foreground npm start process launched from an interactive SSH session. Deploy through NodeJS Manager so SPanel supervises the service after your terminal disconnects.

9. The domain reaches the VPS, but the wrong site loads

Check the exact Application URL selected in NodeJS Manager and confirm that the requested hostname belongs to the same SPanel account. A correct A record gets the request to the VPS, but the web-server layer still needs the correct hostname-to-application mapping.

10. The health endpoint works, but the browser page is broken

A successful health endpoint tells you the backend process is alive. Move one layer up and check the browser developer console, static file paths, frontend API requests, and the public application URL. The backend can be healthy while a separate frontend resource is failing.

Conclusion

The deployment becomes much easier to understand when you keep the layers separate and move through them in order.

Start by creating the SPanel account and connecting your own domain. Then create the hostname for the application and verify its DNS record. After that, clone the repository, enable SSH, verify the runtime, install the dependencies through .autodeploy, and deploy the application with NodeJS Manager.

Once the process is running, verify it on localhost before testing the hostname. Then deploy a real update, restart the managed process, and use the health endpoint to confirm the new version. After that, use SPanel’s logs and Resource Usage to understand what the process is doing, then finish public DNS, HTTPS, the browser check, and monitoring.

The version 1.1.0 test showed why that sequence matters. Git had already updated the files on disk, but the running service stayed on version 1.0.0 until NodeJS Manager restarted it. The health endpoint made the difference visible immediately.

After the initial setup, the release process becomes much shorter: test the change locally, push it to GitHub, run Pull & Deploy, restart the managed application when necessary, verify /health, and confirm the public HTTPS page.

ScalaHosting
CA$4.21 /mo
Starting price
Visit ScalaHosting
Rating based on expert review
  • User Friendly
    4.8
  • Support
    4.9
  • Features
    4.7
  • Reliability
    4.7
  • Pricing
    4.5

Frequently Asked Questions

Can I deploy a Node.js app on ScalaHosting without SSH?

You can perform the main workflow through SPanel, including cloning the repository, running Pull & Deploy, launching the app in NodeJS Manager, and viewing logs. SSH is still useful during the first setup because it lets you verify Node.js, npm, the Git state, installed dependencies, and localhost health checks directly.

Do I need to put the Node.js app in public_html?

No. In my deployment, the repository lives at /home/nodeapp/hostadvice-nodejs-demo while the test subdomain has a separate document root. NodeJS Manager runs the application from the repository path, and SPanel routes the hostname to the process.

Do I have to use node.moonshotcoil.eu?

No. That is only the hostname I used for this test. Use your own domain or subdomain. For example, you could use app.example.com, api.example.com, dashboard.example.com, or the main domain if that matches your application.
h uptime.

Do I need the same ScalaHosting plan used in this test?

No. I used the Build #1 managed VPS, but another plan can have different CPU, RAM, and storage. The steps in this guide depend on the SPanel Git, domain, SSH, SSL, log, and Node.js tools rather than the exact resource allocation I tested.

Why use npm ci instead of npm install?

The project includes package-lock.json, so npm ci installs the dependency tree recorded in that lock file. That makes the server installation more predictable. If your project does not have a lock file, npm ci will not work until you create and commit one.

Can SPanel clone a private GitHub repository?

Git Version Control includes a private-repository option. This walkthrough used a public repository, so I did not need to configure private Git authentication. Follow the authentication workflow shown by your SPanel version rather than embedding credentials directly in the repository URL.

Should I enable Automatic Pull & Deploy?

I would start manually. First prove that the repository updates correctly, .autodeploy completes, the application restarts as expected, and /health reports the correct version. After that, you can decide if automatic pulling fits your release process.

Which internal port should I use?

Use an available port and configure the application and NodeJS Manager consistently. I used 3100. If several Node.js processes share the VPS, assign a different available port to each one.

Why does the browser not need port 3100 in the URL?

Port 3100 belongs to the internal Node.js process. SPanel’s public web server receives the normal HTTP or HTTPS request for the hostname and forwards it internally to the process.

Why test localhost before the domain?

It separates the application from the public web stack. If 127.0.0.1 on the application port fails, the problem is still inside the app or its process. If localhost works but the hostname fails, you can stop debugging Express and move to SPanel routing, DNS, or SSL.

What did the version 1.1.0 update test prove?

It proved that Pull & Deploy updated the repository on disk but did not reload the already-running Node.js process in my setup. The server files showed version 1.1.0 while /health still returned 1.0.0. After I restarted the application in NodeJS Manager, /health returned 1.1.0 with a fres

How to Create a PowerPoint With AI From Your Business Data

Say you need to present last month's sales results. You have an Excel file, a few notes from the team, and ten minutes on the meeting agen...
4 min read
Walter Akolo
Walter Akolo
Hosting Expert

How One Brand Replatformed WordPress Without Losing Rankings

One of the most daunting things you can do to your website is replatforming it; it’s somewhat akin to performing open-heart surgery on the roa...
3 min read
Walter Akolo
Walter Akolo
Hosting Expert

Evaluating Hybrid Cloud Storage for Scalable Web Hosting

As web hosting faces growing pressures from increasing traffic, media-rich content, and user collaboration, scalable storage is now essential ...
6 min read
Walter Akolo
Walter Akolo
Hosting Expert

How to Evaluate a Residential Proxy Provider Before You Scale

Choosing the wrong residential proxy provider before a high-traffic launch can undo weeks of work. Teams often find out mid-campaign that the ...
5 min read
Walter Akolo
Walter Akolo
Hosting Expert
Click to go to the top of the page
Go To Top
HostAdvice.com provides professional web hosting reviews fully independent of any other entity. Our reviews are unbiased, honest, and apply the same evaluation standards to all those reviewed. While monetary compensation is received from a few of the companies listed on this site, compensation of services and products have no influence on the direction or conclusions of our reviews. Nor does the compensation influence our rankings for certain host companies. This compensation covers account purchasing costs, testing costs and royalties paid to reviewers.