Server Management
How to Install Node.js and Run Apps Safely
Node.js runs JavaScript on the server. This guide installs the current LTS from the NodeSource apt repository (with its GPG key) or via nvm per user, then runs your app as a dedicated non-root user behind an Nginx reverse proxy. Exposing a Node process directly as root is a serious risk.
Install Node.js from NodeSource (with GPG)
Add the NodeSource repository, whose setup script installs a verified GPG key, then install the current LTS. Alternatively, use nvm for a per-user install that never needs root.
# The NodeSource setup script adds a GPG-verified apt repo (it does NOT pipe an app into a root shell) curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs # Per-user alternative (no sudo, isolates Node to your account): # curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash && nvm install --lts
Verify the Installation
Confirm Node and npm report sane versions.
node -v # should print v22.x (current LTS) npm -v
Create a Dedicated Non-Root App User
Run your application under its own locked-down account so a bug in your app cannot become root on the host.
sudo useradd -r -m -d /opt/myapp -s /usr/sbin/nologin nodeapp sudo cp -r ./your-app/* /opt/myapp/ sudo chown -R nodeapp:nodeapp /opt/myapp
Run the App with systemd (Recommended)
A systemd unit keeps the app running, restarts it on crash, starts it on boot, and runs it as the nodeapp user. This is cleaner than running things by hand.
sudo nano /etc/systemd/system/myapp.service # Paste: [Unit] Description=My Node App After=network.target [Service] User=nodeapp WorkingDirectory=/opt/myapp ExecStart=/usr/bin/node /opt/myapp/app.js Restart=on-failure Environment=NODE_ENV=production [Install] WantedBy=multi-user.target # Then: sudo systemctl daemon-reload sudo systemctl enable --now myapp sudo systemctl status myapp --no-pager # verify it is running # pm2 alternative (as the nodeapp user): pm2 start app.js --name myapp && pm2 save && pm2 startup
Put Nginx in Front as a Reverse Proxy
Keep Node listening on localhost and let Nginx handle the public port and TLS. This shields the app and gives you a place to terminate HTTPS.
sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/myapp
# Inside the server { } block:
# location / {
# proxy_pass http://127.0.0.1:3000; # your app's local port
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $remote_addr;
# }
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxAdd TLS and Firewall
Issue a certificate for the proxy and open only web ports, keeping the Node port private to localhost.
sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d yourdomain.com --redirect sudo ufw allow 'Nginx Full' # do NOT open the Node port (3000); it stays on localhost
Node.js is installed from a GPG-verified repository, your app runs as a non-root user under systemd, and it sits behind Nginx with TLS while its own port stays bound to localhost. Never run application code as root, and keep Node on a supported LTS line for security updates.