Deploying an Express App with PM2 on Deployxa: A Complete Guide | Deployxa

Express is the most popular Node.js framework, and deploying it with PM2 on Deployxa is straightforward. Here is the complete zero-config guide.

← Back to Dispatch Articles
Engineering Log

Deploying an Express App with PM2 on Deployxa: A Complete Guide

Express is the most popular Node.js framework, and deploying it with PM2 on Deployxa is straightforward. Here is the complete zero-config guide.

Deploying an Express App with PM2 on Deployxa

Express is the most popular Node.js web framework, and it is a favorite of AI assistants for building APIs and web apps. It is minimal, flexible, and has a massive ecosystem of middleware. But deploying Express in production requires a process manager (like PM2) to handle restarts, clustering, and logs, which is a step that vibe coders often miss. Deployxa's zero-config engine handles the PM2 configuration automatically, detecting Express from your package.json and configuring the deployment. Here is how to deploy an Express app with PM2 on Deployxa.

The direct answer is that Deployxa auto-detects Express from your package.json (which includes express). It configures the build and start commands: the build command is npm install, the start command is npm start (which runs node server.js or node src/index.js), and the runtime is Node 20. For production-grade deployments, you can use PM2 as the process manager, which Deployxa supports via a custom start command. You do not write a Dockerfile, you do not configure PM2 manually, and you do not manage the process manager. The platform handles all of it, just as it does for Next.js and Hono apps.

Why Express Is a Great Choice for AI-Generated APIs

Three reasons explain why Express is a great choice for AI-generated APIs. First, it is the most popular Node.js framework, which means the LLM's training data is dominated by Express examples. This makes Express the most reliable framework for AI code generation. Second, it is minimal and flexible. Express does not impose a specific structure, which means the LLM can generate code that fits the user's request. Third, it has a massive ecosystem. Express has middleware for everything (CORS, auth, logging, rate limiting), which means the LLM can leverage existing solutions. For more on Node.js deployment, see our article on running BullMQ background workers on persistent containers.

The Architecture: Express + PM2 + Container

Here is how Deployxa deploys an Express app.

The Express container

The ingestion service detects Express from your package.json. It configures the build and start commands:

  • Build command: npm install
  • Start command: npm start (or pm2 start ecosystem.config.js for PM2)
  • Runtime: Node 20

The PM2 configuration

PM2 is a process manager that handles restarts, clustering, and logs. For production deployments, PM2 provides several benefits: automatic restarts on crashes, clustering (running multiple worker processes), and log management. Deployxa supports PM2 via a custom start command.

The reverse proxy

Traefik v3 routes traffic from your custom domain to the Express container, with automatic SSL via Let's Encrypt.

Step-by-Step: Deploying an Express App

Here is the exact workflow for a typical Cursor-generated Express app.

Step 1: Create your Express app

// server.js
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(cors());
app.use(express.json());
app.use(morgan('combined'));

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

app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

const users = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob', email: '[email protected]' },
];

app.get('/users', (req, res) => {
  res.json({ users });
});

app.post('/users', (req, res) => {
  const { name, email } = req.body;
  const newUser = { id: users.length + 1, name, email };
  users.push(newUser);
  res.status(201).json({ user: newUser });
});

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

Step 2: Create package.json

{
  "name": "my-api",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.19.0",
    "cors": "^2.8.5",
    "morgan": "^1.10.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.0"
  }
}

Step 3: (Optional) Configure PM2 for production

For production, create an ecosystem.config.js file:

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-api',
    script: 'server.js',
    instances: 'max', // Use all CPU cores
    exec_mode: 'cluster',
    max_memory_restart: '500M',
    env: {
      NODE_ENV: 'production',
    },
  }],
};

Update your package.json start script:

{
  "scripts": {
    "start": "pm2-runtime ecosystem.config.js"
  }
}

Install PM2:

npm install pm2

Step 4: Push to GitHub

git init
git add .
git commit -m "express app"
git remote add origin https://github.com/yourname/my-app.git
git push -u origin main

Step 5: Connect to Deployxa

In the Deployxa dashboard, connect your repository. Deployxa auto-detects Express:

[ingest] Detected Node.js project
[ingest] Framework: express
[ingest] Runtime: node 20.x
[ingest] Build command: npm install
[ingest] Start command: npm start
[ingest] Port: $PORT

Step 6: Configure environment variables

In the Deployxa dashboard, add any environment variables your app needs (e.g., DATABASE_URL, JWT_SECRET). The pre-flight scanner will warn you about any that are clearly required but missing. For more on environment variables, see our article on the vibe coder's guide to environment variables.

Step 7: Deploy

Click Deploy. The build installs your dependencies, the container starts with npm start, and your app is live within 60 to 90 seconds.

Step 8: Add a custom domain

Add a custom domain in the Deployxa dashboard. SSL is provisioned automatically.

Step 9: Verify with deployxa doctor

Run deployxa doctor to verify health. The 14-point readiness engine checks SSL, DNS, environment variables, health endpoints, and container status.

Common Pitfalls and Troubleshooting

The first pitfall is using the development server in production. Express's built-in app.listen() is fine for production (unlike Flask's development server), but it does not handle clustering or automatic restarts. The fix is to use PM2 for production, which provides clustering and automatic restarts. The second pitfall is the PORT environment variable. Express apps often hardcode the port (e.g., app.listen(3000)), but Deployxa assigns a dynamic port via the PORT environment variable. The fix is to use process.env.PORT || 3000 as shown in the code above. The third pitfall is unhandled promise rejections. If an async route handler throws an unhandled promise rejection, the Node.js process crashes. The fix is to add a global error handler and to use express-async-errors (or to wrap async handlers with a try-catch). The fourth pitfall is memory leaks. Express apps can develop memory leaks (e.g., from unclosed database connections, from event listener accumulation), which cause the container to run out of memory over time. The fix is to use PM2's max_memory_restart option, which restarts the process when it exceeds a memory limit. The fifth pitfall is the number of PM2 instances. The default (instances: 'max') uses all CPU cores, which is appropriate for most apps. For apps with high memory usage, you might need fewer instances (to avoid running out of memory). For more on performance, see our article on SPA vs SSR hardware sizing.

Performance: Express vs Fastify vs Hono

Express, Fastify, and Hono are three leading Node.js API frameworks. Express is the most popular (and has the largest ecosystem), but it is also the slowest. Fastify is 2-3x faster than Express, because it uses a more efficient routing engine. Hono is the fastest (and the most portable), because it is built on Web Standards. For new APIs, Fastify or Hono is recommended over Express, because they are faster and more modern. For existing Express apps, there is no need to migrate unless performance is a concern. Deployxa supports all three equally, with the AutoRepairService and the zero-config engine handling each framework automatically. For more on framework comparisons, see our articles on deploying a Hono API and deploying a Flask app with Gunicorn.

Advanced Express Patterns

Beyond the basics, Express apps benefit from several advanced patterns. The first is middleware. Express's middleware system (e.g., cors, morgan, helmet, express-rate-limit) handles cross-cutting concerns. The second is error handling. Express's error-handling middleware (a middleware with 4 arguments) catches errors from route handlers and returns appropriate error responses. The third is routing. Express's express.Router() lets you organize routes into modules, which makes the code more maintainable. The fourth is validation. Libraries like zod and express-validator validate request bodies, query parameters, and headers, which ensures your API receives well-formed input. The fifth is testing. Express has built-in support for testing via supertest and vitest, which makes it easy to write unit and integration tests. For more on testing, see our article on the testing void. For more on Express deployment, see our articles on deploying a Spring Boot app and deploying a Phoenix app with Elixir.

Conclusion: Express with PM2 Without the Configuration

Express is the most popular Node.js framework, and deploying it with PM2 should be as simple as pushing to Git. Deployxa's zero-config engine makes it so: no Dockerfile, no PM2 configuration, no process manager management. Stop configuring PM2 and start shipping.

Ready to deploy your Express app? Drag your project to Deployxa Drop for an instant live preview, or install the CLI with npm i -g @deployxa/cli and deploy from your terminal. For more on framework deep-dives, see our articles on deploying a Flask app with Gunicorn and deploying a Spring Boot app. Learn about deploying a Phoenix app with Elixir and deploying a Ruby on Rails app in our companion articles. Explore our free developer tools to speed up your workflow.

Ready to deploy with Deployxa?

Deploy your apps globally with automatic SSL and AI diagnostics.

Start Free Now