
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.
- 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.
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.

Open Accounts Management and click Create a New Account.

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.

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.

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.

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.

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

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.

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.
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.
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.

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.

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.

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.

Open Files and then select Git Version Control.

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

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.

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.
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.

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.

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.

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.

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.

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.
Step 8: Deploy the Application With NodeJS Manager
Open Software and then select NodeJS Manager.

Click Deploy a New App.

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.

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

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.

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

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.
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.

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.

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.
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.

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.

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

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.
Step 13: Review Resource Usage
After confirming the logs, open Tools and select Resource Usage.

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.

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.


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.

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.
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.

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.

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.
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.

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

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

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.

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

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.

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.
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.

