Setup a Real-Time Node.js & Socket.IO on Ubuntu

By Anurag Singh

Updated on May 30, 2025

Setup a Real-Time Node.js & Socket.IO on Ubuntu

Learn how we build a scalable real-time app with Node.js, Express, and Socket.IO on Ubuntu 24.04.

Introduction

Web environment, real-time features like live chat, notifications, and collaborative dashboards have moved from “nice-to-have” to “must-have.” In this tutorial, we walk through every configuration and code snippet needed to launch a production-grade WebSocket server on Ubuntu 24.04 using Node.js and Socket.IO.

We explain why each step matters— from installing the latest Node LTS via NodeSource, to setting up Nginx as a reverse proxy with SSL, to managing uptime with PM2. By the end, our application will support secure, low-latency two-way communication, ready for scalable deployments. Let’s dive in and transform our Ubuntu server into a real-time powerhouse.

Prerequisites

Before we begin, let’s ensure we have the following in place:

Setup a Real-Time Node.js & Socket.IO on Ubuntu

1. Preparing the Ubuntu 24.04 Server

Before anything else, keep our system secure and up to date:

sudo apt update && sudo apt upgrade -y

Next, install essential build tools:

sudo apt install -y build-essential curl git
  • build-essential gives us gcc, make, and other compilers needed by some Node modules.
  • curl and git will let us fetch Node.js installers and clone repos.

2. Installing Node.js via NodeSource

Ubuntu’s default Node.js may lag behind current versions. We’ll use NodeSource to get the latest LTS:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
  • setup_lts.x ensures we track the latest long-term support (e.g., 22.x).
  • nodejs includes npm automatically.

Verify versions:

node -v    # e.g. v22.x.x
npm -v     # e.g. 11.x.x

3. Bootstrapping Our Socket.IO Project

Create a project directory and enter it:

mkdir realtime-app && cd realtime-app

Initialize NPM:

npm init -y

Install dependencies:

npm install express socket.io
  • express for handling HTTP requests and serving static files.
  • socket.io for WebSocket abstraction, automatic reconnection, and rooms.

Install dev-tools (optional):

npm install --save-dev nodemon

nodemon restarts our server on file changes—handy during development.

4. Writing the WebSocket Server

Create a file named server.js:

nano server.js

Add following code:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: { origin: "*", methods: ["GET","POST"] }
});

// Serve a simple client
app.use(express.static('public'));

io.on('connection', socket => {
  console.log(`Client connected [id=${socket.id}]`);

  // Listen for messages from clients
  socket.on('message', data => {
    console.log(`Received: ${data}`);
    // Broadcast to all connected clients
    io.emit('message', data);
  });

  socket.on('disconnect', () => {
    console.log(`Client disconnected [id=${socket.id}]`);
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});
  • Why split http.createServer and Socket.IO? It lets us handle both HTTP and WebSocket on the same port.
  • CORS config allows our client to connect from any origin during development; tune it down in production.

5. Building a Simple Client

Create public directory 

sudo mkdir public

Create index.html inside public directory

nano public/index.html

Add following code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Real-Time Chat</title>
</head>
<body>
  <h1>Real-Time Chat</h1>
  <div id="messages"></div>
  <input id="input" autocomplete="off"/><button id="send">Send</button>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();

    const input = document.getElementById('input');
    const send  = document.getElementById('send');
    const msgs  = document.getElementById('messages');

    send.addEventListener('click', () => {
      const text = input.value.trim();
      if (!text) return;
      socket.emit('message', text);
      input.value = '';
    });

    socket.on('message', msg => {
      const el = document.createElement('div');
      el.textContent = msg;
      msgs.appendChild(el);
    });
  </script>
</body>
</html>
  • Why static files? Socket.IO client script is served automatically from the server at /socket.io/socket.io.js.
  • How it works: On sending, we emit a message event; on receiving, we append messages to the DOM.

6. Managing the Process with PM2

To keep our server running and restart on failure:

npm install -g pm2
pm2 start server.js --name realtime-app
pm2 save
pm2 startup systemd   # Auto-start on reboot
  • pm2 save writes the running processes to be resurrected on system boot.
  • systemd integration means our app will start automatically when the server reboots.

7. Configuring Nginx as a Reverse Proxy

A reverse proxy handles HTTPS, load balancing, and scale. Install Nginx:

sudo apt install -y nginx

Create Nginx conf file:

nano /etc/nginx/sites-available/realtime

Add following code:

server {
    listen 80;
    server_name example.com;  # replace with our domain

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;          # WebSocket upgrade
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/realtime /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
  • proxy_set_header Upgrade is essential for WebSocket handshakes.
  • No hardcoding ports on clients—we’ll connect simply to https://example.com.

8. Configure Firewall

If you have enabled UFW, add HTTP and HTTPS ports:

ufw allow 80/tcp
ufw allow 443/tcp

9. Securing with HTTPS (Let’s Encrypt)

Encrypt traffic for privacy:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
  • Certbot auto-edits our Nginx config to listen on 443 and handle renewals.
  • Test certificate renewal: sudo certbot renew --dry-run.

10. Testing and Scaling

  • Local test: curl http://localhost:3000 should fetch our index.html.
  • Browser test: Visit https://example.com and open DevTools → Network to confirm WebSocket frames (look for “101 Switching Protocols”).

Scaling tips:

  • Use Redis adapter for Socket.IO to emit events across multiple Node.js instances.
  • Add load balancers (e.g., AWS ELB) in front of multiple Nginx servers.
  • Monitor with PM2 dashboard or integrate with Prometheus.

11. Maintenance Best Practices %

  • Logging: Integrate Winston or Bunyan for structured logs.
  • Environment variables: Store secrets and ports in a .env file and load via dotenv.
  • Automatic restarts: Use PM2’s watch mode (careful in production).
  • Health checks: Create a simple /health endpoint returning 200 OK for uptime monitoring.
  • Backups: Regularly snapshot your server or use containerized deployments (Docker).

We've learn how we build a scalable real-time app with Node.js, Express, and Socket.IO on Ubuntu 24.04. By following these steps, we’ve built a robust real-time application on Ubuntu 24.04, complete with process management, secure WebSocket proxying, and SSL. Our application can now handle live updates—chat messages, live dashboards, collaborative tools—with confidence and scalability.