On an Ubuntu or Debian-family VPS, the safest setup is to install Node.js for the account that runs your application, install MySQL from a compatible package source, create a separate database user, keep MySQL listening on localhost, and expose only the ports your application needs. Verify each layer—SSH, packages, services, credentials, firewall, and the application connection—before moving to the next.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
This guide assumes a new or existing Linux VPS with SSH access and a sudo-capable account. The commands apply to Ubuntu and closely related Debian distributions; package names, configuration-file locations, and release availability can differ on other distributions.
As an Amazon Associate I earn from qualifying purchases.
Recommended Free Tools
What you need before installing anything
- An Ubuntu or Debian-family VPS with a supported release and a public IP or private network address appropriate for your deployment.
- An SSH account with sudo privileges. Keep one administrative SSH session open while changing firewall rules so a mistake does not lock you out.
- A DNS name if the application will be served publicly, plus a plan for TLS termination and a reverse proxy if you are not exposing Node’s development server directly.
- A decision about how the Node process will run: a single deployment user, a dedicated service account, or a process manager. The installation method should match that decision.
Run commands as your normal sudo-capable user unless a command explicitly uses sudo. Do not run the application as MySQL’s root account or as the Linux root user.
Choose installation sources deliberately
There is no single best package source for every VPS. Node.js and MySQL have different release and service-management concerns, so evaluate them separately.
#1 Best Overall
Node.js installation choices
| Method | Version control | Reproducibility | Best fit | Trade-off |
|---|---|---|---|---|
| nvm from the official Node.js Linux instructions | Per-user selection of LTS, Current, or a specific major line | High for user-level development and deployments when the selected line is recorded | A deployment user, staging host, or a VPS running several Node applications | Each user has its own Node installation; system services need an explicit environment or a system-wide binary |
| Distribution package | Controlled by the Ubuntu or Debian release | High within that distribution release | Simple system services where conservative updates are preferred | The distribution package can lag upstream Node.js releases |
| System-wide upstream binary or repository | Depends on the repository and your pinning policy | High when versions are pinned and documented | Multiple service accounts that must use one centrally managed runtime | Upgrades affect every application using that binary |
The official Node.js download page publishes separate Current and Long-Term Support (LTS) channels. The page captured for this guide listed Node.js v24.21.0 as LTS and v26.8.2 as Current; those labels and numbers change, so select the release shown on the page when you install rather than treating these figures as permanent.
MySQL installation choices
| Source | Release freshness | Vendor support | Upgrade considerations |
|---|---|---|---|
| Ubuntu or Debian native repository | Aligned with the VPS distribution and may lag upstream MySQL | Integrated with the distribution’s package and security process | Usually the least surprising path for that operating-system release |
| MySQL’s APT repository | Offers a choice of MySQL release series maintained by the upstream project | Upstream MySQL packaging and documentation | Requires careful repository selection and testing before major upgrades |
| Other upstream packages, binaries, source, or containers | Varies by method | Varies by method | Adds operational decisions that are outside a basic Ubuntu VPS setup |
For a single Ubuntu or Debian VPS, start with the native package unless you have a documented requirement for a newer MySQL series and have checked compatibility with the operating-system release. MySQL documents native repositories, its own APT or Yum repositories, packages, binaries, source builds, and containers as distinct installation paths.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →1. Connect over SSH and update the host
Open an SSH session and confirm that sudo works before changing packages or firewall policy.
ssh your_user@your_vps_address
sudo -v
sudo apt update
sudo apt upgrade
apt update refreshes package metadata; apt upgrade applies available upgrades. If the upgrade includes a kernel or critical system library, schedule a reboot according to your provider’s guidance and verify that SSH returns afterward. Keep the second administrative shell open while you continue.
Record the operating-system release before choosing an external repository:
cat /etc/os-release
uname -a
Do not add a repository intended for a different Ubuntu or Debian release. A mismatched repository can replace core libraries or leave MySQL dependencies in an inconsistent state.
2. Install Node.js with nvm
nvm is a practical choice when one application user needs a deliberate Node release. The official Node.js download page supplies the current Linux installer command. Copy that command from the page for the release you intend to use; installer URLs and recommended versions can change.
Install and load nvm
After running the official installer, load nvm into the current shell. The installer normally adds initialization to your shell profile; sourcing the script now avoids having to reconnect.
Rank #2
source ~/.nvm/nvm.sh
command -v nvm
The second command should print nvm. If it prints nothing, inspect the installer output and your shell profile, then open a new SSH session or source the profile before proceeding.
Select an LTS or Current release
For production applications, select the LTS line unless a dependency specifically requires Current. You can install the latest LTS line, or install a named major version after checking the official release page.
nvm install --lts
nvm alias default 'lts/*'
node -v
npm -v
If you have chosen a specific major line, replace the LTS command with that line, for example nvm install 24, then set the default alias to the installed version. Verify both outputs and record them with the application deployment documentation. A shell that uses nvm can show one version while a system service launched without the user’s profile finds another, so service configuration must use the intended absolute Node path or explicitly load nvm.
Make the runtime usable by a service
nvm installs per user. That is useful for isolation but means a service manager will not automatically inherit your interactive shell. Find the selected binary:
which node
which npm
node -p "process.execPath"
When creating a systemd unit or another service definition, run the process as the deployment user and reference the path returned by process.execPath, or use a service wrapper that loads the user’s nvm initialization. After an nvm upgrade, check whether the service path still points to the intended version.
3. Install MySQL and verify its service
Ubuntu documents installation through the native package manager:
sudo apt install mysql-server
MySQL’s APT instructions use the equivalent sudo apt-get install mysql-server. Use one package manager consistently on a host. The package installation should create and start the MySQL service automatically on a standard Ubuntu system.
Check the service immediately:
sudo service mysql status
sudo systemctl status mysql
On a healthy installation, the status should show an active running service. If it is inactive or failed, inspect the journal before trying repeated restarts:
sudo journalctl -u mysql --no-pager -n 100
sudo systemctl restart mysql
Debian and Ubuntu package installations use systemd for MySQL startup and shutdown. Enablement is normally installed with the package; confirm it if the VPS has been customized:
sudo systemctl is-enabled mysql
sudo systemctl is-active mysql
4. Create a database and a least-privilege application account
Your application should use its own MySQL account, not the administrative root account. Ubuntu notes that the default root account is local and may authenticate through auth_socket; that is why sudo mysql can work even when a root password has not been configured.
Free tools Windows power users keep installed
One-click scans. No signup required.
Open the local administrative session
sudo mysql
At the mysql> prompt, create a database and a user restricted to local connections:
CREATE DATABASE app_db;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'use-a-long-unique-password';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Choose a long, unique password and keep it out of source control. The listed privileges cover many CRUD applications; add schema-change privileges only for a controlled migration account, and grant additional rights only when the application genuinely needs them. If the application connects through TCP to 127.0.0.1 rather than through a local socket, create the account for that host as well, because MySQL matches the account host field:
sudo mysql
CREATE USER 'app_user'@'127.0.0.1' IDENTIFIED BY 'use-a-long-unique-password';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'127.0.0.1';
FLUSH PRIVILEGES;
EXIT;
Use one connection style consistently. An account created only for localhost is not automatically the same account as one created for 127.0.0.1.
Check the account’s effective grants
sudo mysql -e "SHOW GRANTS FOR 'app_user'@'localhost';"
Review this output whenever deployment scripts change permissions. Do not grant ALL PRIVILEGES ON *.* to the application unless you have a specific, documented administrative requirement.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems5. Keep MySQL private by default
If Node.js and MySQL run on the same VPS, the database does not need to accept Internet connections. MySQL configuration is under /etc/mysql. The bind-address setting controls which network interface accepts connections.
Local-only configuration
Find the active setting before editing:
sudo grep -R "^[[:space:]]*bind-address" /etc/mysql
For a same-host application, use a loopback address such as 127.0.0.1 in the active server configuration. Preserve the existing file’s format and make a backup before editing. Then restart MySQL:
Rank #4
sudo systemctl restart mysql.service
sudo systemctl status mysql
A restart that returns to an active state confirms that MySQL accepted the configuration syntax. It does not by itself prove that the application credentials or privileges are correct; perform the application-level test later.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11When a remote client is genuinely required
Changing bind-address to a non-loopback address is an exception, not a default. Use a private network interface where possible, restrict the source addresses at the VPS firewall and any provider firewall, and require encrypted MySQL connections. Never open the database port to every Internet address merely to make a remote tool connect. A private tunnel or administrative bastion is preferable to a public database listener.
After changing the listener, confirm what is actually bound:
sudo ss -ltnp | grep 3306
Only the intended address should appear. If the service listens on all interfaces, revisit bind-address, firewall rules, and any provider-level security group before exposing the host.
6. Configure UFW without locking yourself out
Ubuntu identifies UFW as its default firewall configuration tool. Netfilter ultimately enforces the rules, while UFW provides a manageable command interface.
Allow SSH first
Determine the SSH port in use, then allow it before enabling UFW. The common port is 22:
sudo ufw status verbose
sudo ufw allow 22/tcp
If SSH uses another port, allow that port instead. Keep your existing SSH session open and test a second login before enabling the firewall.
Allow only application traffic
Allow the web ports or other ports your deployment actually uses. A typical public web service needs HTTP and HTTPS:
Best Value
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status numbered
If a reverse proxy is the only public entry point, do not expose the Node application’s internal listening port. If you temporarily need a development port, restrict it to your own source address and remove the rule after testing.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Keep the MySQL port closed publicly
Do not add a broad rule for TCP 3306 when the application and database share the VPS. If a remote design is unavoidable, allow 3306 only from the exact private or administrative source address and combine that rule with MySQL’s restricted bind address and TLS configuration. Verify both UFW and any cloud-provider firewall; the more permissive layer determines whether traffic can reach the host.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.7. Connect the Node application to MySQL
Install the MySQL driver your application uses in the application directory, then keep connection details in environment variables or a secret manager rather than committing them to JavaScript files.
cd /path/to/your/app
npm install mysql2
The following minimal example uses the commonly used promise API. It assumes the application is connecting over TCP to the local address and that the corresponding MySQL account was created for 127.0.0.1.
const mysql = require('mysql2/promise');
async function checkDatabase() {
const pool = mysql.createPool({
host: process.env.DB_HOST || '127.0.0.1',
port: Number(process.env.DB_PORT || 3306),
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 5
});
const [rows] = await pool.query('SELECT 1 AS ok');
console.log(rows);
await pool.end();
}
checkDatabase().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Set the variables only in the service environment or a protected deployment mechanism:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →export DB_HOST=127.0.0.1
export DB_PORT=3306
export DB_USER=app_user
export DB_PASSWORD='use-a-long-unique-password'
export DB_NAME=app_db
node db-check.js
A successful SELECT 1 proves that DNS or host selection, TCP reachability, authentication, and basic query permission work together. It does not prove that every migration or business query has the required rights. Test the smallest set of intended operations, and confirm that an operation the account should not perform is rejected.
8. Make the deployment persistent
Interactive shell tests disappear when you disconnect. Run the application under a service manager or process supervisor and make the runtime, working directory, user, environment, and restart policy explicit.
Record the runtime and application paths
node -p "process.execPath"
pwd
id
Use the returned absolute Node path and a dedicated Linux user in your service definition. Ensure that user can read the application files and secret source but cannot write to code or unrelated system directories.
Check service behavior after a reboot
After installing the service definition, start it, inspect its status and logs, and perform a controlled reboot during a maintenance window. Confirm that MySQL starts before the application attempts its first connection, or configure the application to retry transient database-unavailable errors.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutesudo systemctl status mysql
sudo systemctl status your-node-service
sudo journalctl -u your-node-service --no-pager -n 100
MySQL itself is managed by systemd on Ubuntu and Debian. Use the same status, restart, and journal commands after package upgrades or configuration changes.
Troubleshooting by symptom
node or npm is “command not found”
- Run
source ~/.nvm/nvm.shand checkcommand -v nvm. - Open a new SSH session so the shell profile is read.
- Check whether the failing command runs as the same user that installed nvm. nvm installations are per user.
- If only the system service fails, use the absolute path from
node -p "process.execPath"in the service configuration.
MySQL installation fails or the package is unavailable
- Run
sudo apt updateand check the VPS release in/etc/os-release. - Remove or correct repositories intended for another distribution release.
- Decide whether the native repository’s older version is acceptable before adding MySQL’s APT repository. Test a major-version change on a disposable VPS or staging system first.
MySQL is not running
- Read the recent service log with
sudo journalctl -u mysql --no-pager -n 100. - Check disk space and file ownership if the log reports storage or permission errors.
- Review the most recent configuration edit under
/etc/mysql. - Run
sudo systemctl restart mysqlonly after correcting the cause, then verify withsudo systemctl status mysql.
“Access denied” from the application
- Confirm the username, password, database name, and host in the service environment.
- Check whether the account is defined for
localhostor127.0.0.1; MySQL treats those host values as different account matches. - Inspect grants with
SHOW GRANTSand verify that the database name is correct. - Do not solve the error by switching the application to the root account.
“Connection refused” or a timeout
- Check that MySQL is active and listening with
sudo ss -ltnp | grep 3306. - Compare the application’s host with the configured
bind-address. - For a local deployment, use loopback and keep 3306 closed in UFW.
- For a remote deployment, inspect UFW, the provider firewall, source-address restrictions, and TLS requirements together.
The firewall disconnects SSH
Use the still-open administrative session or your VPS console to restore an allow rule for the actual SSH port. Do not enable UFW until a second SSH login has been tested with the intended rule in place.
Quick Recap
Post-installation checklist
- SSH access works with a sudo-capable non-root account, and a recovery console or second session is available.
node -vandnpm -vshow the versions recorded for the deployment.- MySQL reports an active systemd service and starts after a controlled reboot.
- The application uses a dedicated MySQL account with only the required database privileges.
- The MySQL listener is local unless a documented remote design requires otherwise.
- UFW allows SSH and the public application ports but does not expose 3306 broadly.
- Database credentials are supplied through protected environment or secret-management settings.
- A minimal Node connection test succeeds, and an unauthorized database operation is rejected.
- Service logs are accessible with
journalctl, and you know how to restart both MySQL and the Node service.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




