Using a VPS to deploy a node js web app

Contabo Ubuntu VPS Setup: SSH, MariaDB, Node.js, systemd

Contabo Ubuntu VPS: SSH, MariaDB, Node.js, systemd Service

This guide assumes:

  • You have a new Contabo VPS with Ubuntu (latest LTS).
  • You have the VPS IP and initial root password.
  • You will create a non-root user, install MariaDB and Node.js, upload your Node app, and run it as a systemd service.
Replace placeholders: YOUR_SERVER_IP, youruser, yourdb, user1, StrongPasswordHere, /opt/app, server.js with your actual values.

1. Local machine: tools you need

1.1 On Windows (PowerShell)

  1. Install Git for Windows from the official site.
  2. During setup, allow it to add git, ssh to your PATH.
  3. You can use either:
    • PowerShell (recommended) or
    • Git Bash (also fine)

1.2 On local Ubuntu

If you also have an Ubuntu laptop/desktop:

sudo apt update
sudo apt install -y openssh-client git

2. Generate SSH key (Windows or Ubuntu)

Run this in PowerShell (Windows) or a terminal (Ubuntu):

ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter for default file location (~/.ssh/id_ed25519), set a passphrase if you like.

Show the public key:

type $env:USERPROFILE\.ssh\id_ed25519.pub   # PowerShell
# OR on Ubuntu:
cat ~/.ssh/id_ed25519.pub

Copy the full line starting with ssh-ed25519.


3. First login to the Contabo VPS (root)

From PowerShell or Ubuntu terminal:

ssh root@YOUR_SERVER_IP

Enter the root password from Contabo. Once in:

apt update
apt upgrade -y

4. Create a non-root sudo user

Inside the VPS as root:

adduser youruser
usermod -aG sudo youruser

Create SSH directory for this user and add your public key:

su - youruser
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys

Paste your copied id_ed25519.pub line into authorized_keys, save and exit.

chmod 600 ~/.ssh/authorized_keys

Open a second terminal on your local machine and test:

ssh youruser@YOUR_SERVER_IP

5. Harden SSH (basic)

Back as root or with sudo:

sudo nano /etc/ssh/sshd_config

Change or add:

PermitRootLogin no
PasswordAuthentication no

Then restart SSH:

sudo systemctl restart ssh

Test again from local: you should now only be able to log in using your SSH key:

ssh youruser@YOUR_SERVER_IP

6. Basic firewall (UFW)

sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

7. Install MariaDB

sudo apt update
sudo apt install -y mariadb-server mariadb-client

Secure the installation:

sudo mysql_secure_installation

Answer the prompts (set a strong root password, remove test DB, etc.).

7.1 Create database and user

Log in to MariaDB:

sudo mysql -u root -p

Inside the MariaDB shell:

CREATE DATABASE yourdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER 'user1'@'localhost' IDENTIFIED BY 'StrongPasswordHere';
GRANT ALL PRIVILEGES ON yourdb.* TO 'user1'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Use a proper strong password instead of StrongPasswordHere.

8. Install Node.js and npm

Example using NodeSource (Node 22 LTS style). Run as youruser with sudo:

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

Check versions:

node -v
npm -v

9. Create an app directory and user

Create a dedicated user for your Node app (no shell login):

sudo useradd -r -m -d /opt/app -s /usr/sbin/nologin appuser

Create app directory and set permissions:

sudo mkdir -p /opt/app
sudo chown appuser:appuser /opt/app

10. Upload or clone your Node.js code

10.1 From Windows using PowerShell (SCP)

From your local machine, assuming your app files are in C:\myapp:

scp -r C:\myapp\* youruser@YOUR_SERVER_IP:/home/youruser/myapp

10.2 Move code into app directory

On the server:

sudo rsync -a /home/youruser/myapp/ /opt/app/
sudo chown -R appuser:appuser /opt/app

11. Install Node dependencies and test run

Switch to appuser:

sudo -u appuser -s
cd /opt/app

Install dependencies from package.json:

npm install

Optional: create/update .env with DB credentials etc:

nano .env
# Example content:
DB_HOST=localhost
DB_NAME=yourdb
DB_USER=user1
DB_PASS=StrongPasswordHere
PORT=3000

Test the app (adjust for your entry file):

node server.js

Check in your browser: http://YOUR_SERVER_IP:3000 (if you expose that port) or behind a reverse proxy later.

Stop the test with Ctrl+C.


12. Create a systemd service for Node.js

Exit from appuser shell back to sudo user if needed, then:

sudo nano /etc/systemd/system/myapp.service

Example service file:

[Unit]
Description=My Node.js App
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/app
EnvironmentFile=/opt/app/.env
ExecStart=/usr/bin/node /opt/app/server.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Reload systemd and start the service:

sudo systemctl daemon-reload
sudo systemctl start myapp.service
sudo systemctl status myapp.service

Enable on boot:

sudo systemctl enable myapp.service

View logs:

sudo journalctl -u myapp.service -f

13. Basic hardening checklist

  • Use a strong password for user1 and all system users.
  • Keep apt update and apt upgrade run regularly.
  • Only open ports you need (SSH, HTTP/HTTPS, DB not exposed publicly).
  • Optionally add fail2ban to block brute-force SSH attempts:
    sudo apt install -y fail2ban
  • Do not run your Node app as root; use appuser as shown.

14. Using local Ubuntu instead of Windows

On a local Ubuntu machine everything is similar:

  • Generate SSH key: ssh-keygen -t ed25519
  • Connect: ssh youruser@YOUR_SERVER_IP
  • Upload: scp -r ./myapp youruser@YOUR_SERVER_IP:/home/youruser/

Then follow the same steps on the VPS (create appuser, move app to /opt/app, npm install, systemd service, etc.).