How to build an Express.js server in Ubuntu

A central server connects to four laptops with data transfer arrows, illustrating a network or data sharing concept.

Setting up an Express.js server on Ubuntu is simple and efficient. Here’s what you’ll learn in this guide:

  • Install Node.js and npm: Use the latest NodeSource repository to ensure compatibility.
  • Set up an Express.js server: Create a basic server with routing and middleware.
  • Deploy with PM2 and Nginx: Ensure a stable production environment.
  • Secure with HTTPS: Use Let’s Encrypt for SSL certificates.

This step-by-step guide will help you create a secure, production-ready server for APIs, real-time apps, or single-page applications. Whether you’re a beginner or experienced, this process ensures a reliable setup.

How to Deploy a Node.js App to Your Ubuntu VPS

System Requirements and Setup

Make sure your Ubuntu system is ready with the necessary tools and configurations to avoid any setup problems and ensure smooth performance.

Required Software Setup

You’ll need to install a few key components on your Ubuntu system:

  • Essential tools: build-essential, curl, git, nano or vim, PM2, and Nginx

Start by updating your system and installing the required packages:

sudo apt update
sudo apt upgrade -y
sudo apt install build-essential curl git -y

VPS Setup Guide

Choose a VPS plan that aligns with the resource demands of your application. Here’s a quick comparison of options:

Resource RequirementsRecommended VPS PlanMonthly CostBest For
Entry LevelKVM1-US (1GB RAM)$10Development, Testing
Small ProductionKVM2-US (2GB RAM)$20Small Applications
Medium ProductionKVM4-US (4GB RAM)$40Medium Traffic Sites
High PerformanceKVM8-US (8GB RAM)$80High Traffic Applications

Standard Features:

  • Network Speed: 1 Gbps port
  • Traffic: Unmetered bandwidth
  • Storage: NVMe SSD (20GB to 80GB)
  • Control: Full root access
  • Support: 24/7 technical help

For most Express.js applications, the KVM2-US plan ($20/month) is a solid choice. It offers 2 vCores, 2GB RAM, and 25GB of fast NVMe storage. This setup works well for development and small to medium production environments, delivering reliable performance.

Tips for Securing Your VPS:

  • Set up SSH key authentication
  • Configure UFW firewall rules
  • Regularly update packages
  • Create a non-root deployment user for added security

Once your system and VPS are ready, you can move on to installing Node.js and npm in the next steps.

Node.js and npm Installation

To run Express.js applications on Ubuntu, you’ll need to set up Node.js and npm. Using the NodeSource repository ensures you’re working with the latest stable version.

Setting Up the Node.js Repository

Ubuntu’s default repository often includes outdated Node.js versions that may lack important security updates [2]. To get a newer version, you’ll use the NodeSource repository, which offers official builds for Debian-based systems.

Start by downloading and running the NodeSource setup script:

curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh
sudo bash /tmp/nodesource_setup.sh

Installing Node.js and npm

Follow these steps to install and verify Node.js and npm:

StepCommandPurpose
Install Node.jssudo apt install nodejsInstalls both Node.js and npm
Verify Node.jsnode -vConfirms the installed Node.js version
Verify npmnpm -vConfirms the installed npm version
Update npmsudo npm install -g npm@latestUpdates npm to the latest version

When working with Express.js, make sure you’re using a compatible Node.js version:

  • Express 4.x needs Node.js 0.10 or higher
  • Express 5.x requires Node.js 18 or higher [1]

Once Node.js and npm are set up, you’re ready to start configuring your Express.js server.

Express.js Server Setup

Once you’ve installed Node.js and npm, you can start building your Express.js application.

Project Setup

Begin by creating a new project directory and initializing it:

mkdir express-ubuntu-server
cd express-ubuntu-server
npm init -y

Installing Express.js

Install Express.js locally using npm:

npm install express

Setting Up a Basic Server

Now, create a file named app.js and include the following code:

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});

To test your server:

  • Run the server with: node app.js
  • Open your browser and go to http://localhost:3000 to see the message “Hello World!”
  • Stop the server anytime by pressing Ctrl+C

Setting Up for Development Testing

For testing purposes, install Jest, SuperTest, and cross-env:

npm install --save-dev jest supertest cross-env

Update your package.json file to include the following test script:

{
    "scripts": {
        "test": "cross-env NODE_ENV=test jest --testTimeout=5000"
    }
}

Ignoring Node Modules

To prevent committing unnecessary files, create a .gitignore file with the following content:

echo "node_modules/" > .gitignore

With your basic setup complete, your server is ready for additional configurations in the next steps.

sbb-itb-0ad7fa2

Server Features and Settings

Once your basic Express.js server is set up, the next step is to add features and configurations to prepare it for production.

API Routes Setup

Define your API routes to handle different functionalities. Here’s an example:

const express = require('express');
const router = express.Router();

// User routes
router.get('/users', (req, res) => {
    res.json({ message: 'Get all users' });
});

router.get('/users/:userId', (req, res) => {
    const userId = req.params.userId;
    res.json({ message: `Get user with ID: ${userId}` });
});

// Apply routes to main app
app.use('/api', router);

For better organization, create a routes directory and separate files based on features:

/express-ubuntu-server
    /routes
        users.js
        products.js
        orders.js

This structure makes managing and scaling your app much easier.

Static File Management

To efficiently serve static files like images, CSS, and JavaScript, use the express.static middleware:

const path = require('path');

// Serve files from multiple directories
app.use(express.static(path.join(__dirname, 'public')));
app.use('/assets', express.static(path.join(__dirname, 'assets')));

Organize your static files in a well-structured directory:

/express-ubuntu-server
    /public
        /images
        /css
        /js
    /assets
        /uploads
        /documents

This setup ensures your server can easily locate and serve static resources.

Middleware Setup

Middleware plays a key role in request handling and boosting security. Here’s how to set up some essential middleware:

// Request parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Custom logger middleware
app.use((req, res, next) => {
    console.log(`${new Date().toLocaleString('en-US')} - ${req.method} ${req.path}`);
    next();
});

// Error handling middleware
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({
        error: 'Something went wrong!',
        timestamp: new Date().toLocaleString('en-US')
    });
});

Here’s a quick overview of some commonly used middleware:

MiddlewarePurposeImplementation
express.json()Parse JSON payloadsapp.use(express.json())
express.urlencoded()Parse URL-encoded bodiesapp.use(express.urlencoded({ extended: true }))
corsEnable Cross-Origin Resource Sharingapp.use(cors())
helmetAdd security headersapp.use(helmet())
compressionCompress response bodiesapp.use(compression())

To use third-party middleware like cors, helmet, and compression, install them via npm:

npm install cors helmet compression

“Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle.” [3]

When implementing middleware, remember to handle errors properly and log important details. The order of middleware matters: start with general middleware, then add route handlers, and finish with error-handling middleware. This ensures a smooth and secure production environment.

Server Deployment Steps

This section outlines how to deploy a dependable Ubuntu setup for your application.

Local Testing

Before deployment, ensure your Express.js app works as expected by testing it locally with Jest and SuperTest:

const request = require('supertest');
const app = require('../app');

describe('API Endpoints', () => {
    test('GET /api/products returns 200', async () => {
        const response = await request(app)
            .get('/api/products')
            .expect('Content-Type', /json/)
            .expect(200);
        expect(response.body).toHaveProperty('products');
    });
});

Add test scripts to your package.json file:

{
    "scripts": {
        "test": "NODE_ENV=test jest --detectOpenHandles",
        "test:watch": "NODE_ENV=test jest --watch"
    }
}

Once your local tests confirm everything is working, move on to managing your app with PM2.

PM2 Process Management

PM2 is a tool that helps manage your Express.js app in production.

Install PM2 globally:

sudo npm install pm2@latest -g

Set up a configuration file, ecosystem.config.js, in your project directory:

module.exports = {
    apps: [{
        name: "express-app",
        script: "./app.js",
        instances: "max",
        exec_mode: "cluster",
        env: {
            NODE_ENV: "production",
            PORT: 3000
        },
        error_file: "./logs/err.log",
        out_file: "./logs/out.log"
    }]
}

Launch and manage your app with the following commands:

pm2 start ecosystem.config.js
pm2 startup systemd
pm2 save

Here are some useful PM2 commands:

PM2 CommandPurpose
pm2 listView running applications
pm2 monitMonitor CPU and memory usage
pm2 logsView application logs
pm2 reload allReload apps with zero downtime

These commands help keep your app running smoothly and allow it to scale as needed.

Nginx Setup

To set up Nginx as a reverse proxy for your application:

  1. Install Nginx:
sudo apt update
sudo apt install nginx
  1. Create a configuration file at /etc/nginx/sites-available/express-app:
server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
  1. Enable the configuration and restart Nginx:
sudo ln -s /etc/nginx/sites-available/express-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

“PM2 makes it possible to daemonize applications so that they will run in the background as a service.” [4]

Common Problems and Solutions

When setting up an Express.js server on Ubuntu, certain issues can pop up. Here’s how to tackle them head-on.

Permission Issues

Permission errors often arise during package installation or while accessing directories. Here’s a quick fix:

mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.profile
source ~/.profile

For system-level tasks, use sudo only when necessary:

TaskCommand ExampleWhen to Use
System Updatessudo apt updateManaging system packages
Software Installationsudo apt install nodejsAdding new software
Global npm PackagesUse ~/.npm-globalAvoid using sudo with npm

Port Conflicts

The “EADDRINUSE” error happens when multiple applications try to use the same port. Here’s how to resolve it:

  1. Find processes using the port: sudo netstat -tulpn | grep LISTEN
  2. End the conflicting process: sudo kill -9 $(sudo lsof -t -i:3000)

To avoid conflicts in production, set an alternate port in your code:

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

Server Start Issues

If your server refuses to start, try these steps:

  1. Allow Node.js to use lower ports: sudo apt-get install libcap2-bin sudo setcap cap_net_bind_service=+ep `readlink -f \`which node\``
  2. Add error handling to your app: process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); process.exit(1); }); app.listen(port, (error) => { if (error) { console.error('Failed to start server:', error); process.exit(1); } });

For better stability and security, you can:

  • Use authbind to let non-root users access lower ports.
  • Set up robust error logging and monitoring.
  • Configure automatic restarts for your app in case of failure.

Taking these steps will make your deployment process much smoother.

Building a Reliable Express.js Server on Ubuntu

Setting up an Express.js server on Ubuntu requires attention to both development and operational details. To keep your server running smoothly, it’s crucial to have solid monitoring and maintenance tools in place.

Here are some tools you should consider:

ToolPurposeWhat It Does
PM2Process ManagementTracks hardware metrics and logs exceptions
Express Status MonitorReal-Time MetricsAdds a /status endpoint with visual data using Socket.io
New RelicPerformance MonitoringDelivers pre-configured tools for tracking server performance
Clinic.jsDiagnosticsPinpoints and fixes performance issues

By integrating these tools, you can maintain a stable and efficient server environment. But tools alone aren’t enough – best practices are just as essential.

Development Tips

  • Use DEBUG=express:* node index.js to enable debugging.
  • Set up structured logging for better traceability.
  • Add error-tracking services like Sentry.io or AppSignal to catch issues early.

Operational Tips

  • Stick to a regular maintenance schedule.
  • Centralize your logs for easier monitoring and troubleshooting.
  • Use performance metrics tools to gain insights into server health.

For complex setups or critical applications, you may need to consider managed IT services. These can help ensure your server meets performance expectations, especially if your team lacks the necessary expertise.

With these tools and strategies in place, your Ubuntu-based Express.js server will be well-prepared for production workloads.

Facebook
Twitter
LinkedIn

Table of Contents

KVM VPS Running Anywhere in 2 minutes

Get started today

With VPS.US VPS Hosting you get all the features, tools

Image