DEV Community

Cover image for How to Secure a Linux Server for Node.js (Beginner-Friendly, Step by Step)
Yousuf Basir
Yousuf Basir

Posted on

How to Secure a Linux Server for Node.js (Beginner-Friendly, Step by Step)

When you deploy a Node.js (or any web) application on a Linux server, the biggest risk is not your code — it’s server misconfiguration.

This tutorial shows how to turn a fresh Ubuntu server into a secure, production-ready environment, where:

  • Root login is disabled
  • SSH is key-only
  • Each app runs as its own locked-down user
  • Even if your app is hacked, the system stays safe

This guide is beginner-friendly and does not require Docker.


🧠 Security Philosophy (Simple Explanation)

We follow three basic rules:

  1. Never run apps as root
  2. Each app gets its own user
  3. The app can only access its own files

If an attacker breaks into your app, they should not be able to:

  • install crypto miners
  • modify system files
  • add users
  • affect other apps

Assumptions

  • Ubuntu 22.04 / 24.04
  • You can SSH into the server
  • You start as root (fresh cloud server)

Step 1: Create a Non-Root Admin User

First, create a normal user for yourself (example: dev).

adduser dev
Enter fullscreen mode Exit fullscreen mode

Give it a strong password.

Then allow this user to administer the system without logging in as root:

usermod -aG sudo dev
Enter fullscreen mode Exit fullscreen mode

Why?

  • Root SSH access is dangerous
  • A normal user + sudo is safer and auditable

Step 2: Set Up SSH Key Login

On your local machine, generate an SSH key (if you don’t have one):

ssh-keygen -t ed25519
Enter fullscreen mode Exit fullscreen mode

Copy the public key to the server:

mkdir -p /home/dev/.ssh
nano /home/dev/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Paste your public key.

Fix permissions:

chown -R dev:dev /home/dev/.ssh
chmod 700 /home/dev/.ssh
chmod 600 /home/dev/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Now test from your computer:

ssh dev@YOUR_SERVER_IP
Enter fullscreen mode Exit fullscreen mode

If this works, you’re safe to continue.


Step 3: Lock Down SSH (Very Important)

Edit SSH config:

sudo nano /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Ensure these lines exist and are not commented:

PermitRootLogin no
PubkeyAuthentication yes
Enter fullscreen mode Exit fullscreen mode

At the bottom of the file, add:

Match all
    PasswordAuthentication no
Enter fullscreen mode Exit fullscreen mode

Reload SSH safely:

sudo systemctl reload ssh
Enter fullscreen mode Exit fullscreen mode

What this does

  • Root SSH login ❌ disabled
  • Password login ❌ disabled
  • SSH keys ✅ required

💡 Cloud recovery consoles still work — you won’t get locked out.


Step 4: Enable the Firewall

Allow required ports before enabling:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
Enter fullscreen mode Exit fullscreen mode

Enable firewall:

sudo ufw enable
Enter fullscreen mode Exit fullscreen mode

Check status:

sudo ufw status verbose
Enter fullscreen mode Exit fullscreen mode

Why?

  • Blocks random internet scans
  • Only allows what you explicitly need

Step 5: Install Node.js (System-Wide)

Install Node.js using the official repository (example: Node 24 LTS):

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
Enter fullscreen mode Exit fullscreen mode

Verify:

node -v
which node
Enter fullscreen mode Exit fullscreen mode

Expected:

  • /usr/bin/node
  • owned by root

Why not nvm?

  • System services (systemd) don’t work well with nvm
  • Root-owned Node is safer and predictable

Step 6: Create a Dedicated App User

Never run apps as your admin user.

Create a service user (example: svc-nextjs):

sudo adduser \
  --system \
  --no-create-home \
  --group \
  --shell /usr/sbin/nologin \
  svc-nextjs
Enter fullscreen mode Exit fullscreen mode

What this means

  • No SSH access
  • No shell
  • No sudo
  • Only exists to run the app

Step 7: Isolate App Files

Create a directory for your app:

sudo mkdir -p /var/apps/nextjs
sudo chown -R svc-nextjs:svc-nextjs /var/apps/nextjs
sudo chmod 750 /var/apps/nextjs
Enter fullscreen mode Exit fullscreen mode

Test as admin:

cd /var/apps/nextjs
Enter fullscreen mode Exit fullscreen mode

This should fail ❌ — that’s correct.

Test as service user:

sudo -u svc-nextjs ls /var/apps/nextjs
Enter fullscreen mode Exit fullscreen mode

This should work ✅.


Step 8: Run the App with systemd (Hardened)

Create a service file:

sudo nano /etc/systemd/system/nextjs.service
Enter fullscreen mode Exit fullscreen mode

Paste:

[Unit]
Description=Next.js Application (Hardened)
After=network.target

[Service]
Type=simple
User=svc-nextjs
Group=svc-nextjs
WorkingDirectory=/var/apps/nextjs

ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=3

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/apps/nextjs
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
CapabilityBoundingSet=
AmbientCapabilities=
UMask=0077

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Reload systemd:

sudo systemctl daemon-reload
Enter fullscreen mode Exit fullscreen mode

Step 9: Check Security Score

Run:

systemd-analyze security nextjs.service
Enter fullscreen mode Exit fullscreen mode

A score around 2–3 is excellent for a web service.

What this protects against

  • Privilege escalation
  • System file modification
  • Kernel abuse
  • Crypto miners
  • Lateral movement

Even if the app is hacked, the OS remains safe.


Step 10: Final Sanity Checks

sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication'
Enter fullscreen mode Exit fullscreen mode

Expected:

permitrootlogin no
passwordauthentication no
pubkeyauthentication yes
Enter fullscreen mode Exit fullscreen mode

Check firewall:

sudo ufw status
Enter fullscreen mode Exit fullscreen mode

What You Achieved

✅ Root SSH disabled
✅ Password login disabled
✅ Key-only access
✅ Firewall enabled
✅ Per-app service users
✅ Filesystem isolation
✅ Strong systemd sandbox

This is real production security, not just theory.


Final Advice

  • One app = one service user
  • Never run apps as root
  • Let systemd enforce security
  • Keep SSH boring and locked down

You now have a secure Linux foundation for Node.js, Python, or any backend service.


Happy shipping 🚀
And welcome to proper server hardening.

Top comments (0)