☰
30.06.2026

How to Deploy a Node.js API for a Mobile App on a VPS

Most mobile apps have a half that users never see. It is the server side that stores data, checks logins, serves feeds, and accepts orders. To deploy a Node.js API for a mobile app on a VPS, we need a server that keeps this half online around the clock. In this guide we walk the full path, from a clean virtual server to a working API with an encrypted connection, ready to take requests from iOS and Android.

This material is for developers who built an app and hit the question of where to actually run it, for beginners with no admin experience, and for product owners who do not yet have an in-house infrastructure team. We aimed to explain things so that a reader without a deep Linux background follows every step and sees why it matters.

Node.js remains one of the most popular choices for a mobile app backend: one language on both client and server, a fast start, and a huge ecosystem of ready-made libraries. Next we look at how to bring it to a state where it runs reliably under real load.

What a Mobile App Backend Is and Why It Needs a VPS

When a user opens an app and sees a list of products or messages, the app sends a request to the server and gets a response. This exchange goes through an API. In plain terms, an API is a set of addresses where the app asks for data and sends changes. For example, the app gets a list of orders from /api/orders, and by posting a new order there it saves the order on the server.

The code behind this API has to run somewhere, and that is where a VPS comes in. A VPS is a virtual private server, a dedicated slice of a powerful physical machine with its own operating system, where we log in with administrator rights and install whatever we need. It is effectively a full computer in a data center, reachable over the network twenty four hours a day.

How a VPS differs from the neighboring options. Shared hosting is cheaper, but there we share the environment with other sites and barely control the settings, which falls short for a server application. Serverless platforms are convenient at the start, yet the price climbs with the number of requests and platform limits appear. A VPS gives the middle ground: full control over the environment, a predictable fixed price, and the freedom to install any Node.js version, database, and supporting services side by side.

There is a wide gap between an app running on a developer laptop and an app holding hundreds of users at once. On your own machine one command is enough. On a server the process has to survive reboots and crashes, the connection has to be encrypted, and stray ports have to stay closed. That gap is what we close in this guide.

What You Need Before You Start

Before we move to commands, let us gather a short list of what we need at hand. Without these pieces the later steps will not come together.

The virtual server itself can be rented from Serverspace. When ordering, we just pick an Ubuntu 24.04 image, and within a minute we have a clean machine with SSH access in a US data center, onto which we roll the whole stack from this guide.

How Node.js, PM2, and Nginx Work Together

Before the hands-on part it helps to understand the architecture we are heading toward. It has become the de facto standard for running Node.js on a single server, and it is built from three parts.

Node.js listens on an internal port. The application itself starts and accepts requests on a local port, for example 3000. This port is visible only inside the server and does not face the internet directly. That way we keep the app behind a protective layer.

Nginx sits in front and proxies traffic. Nginx is a web server that accepts public requests on the standard ports 80 and 443 and forwards them to the internal port of the application. This mode is called a reverse proxy. Nginx also takes on connection encryption, serving static files, and compressing responses, offloading the application itself.

PM2 keeps the process alive. PM2 is a process manager for Node.js. It restarts the app if it crashes, brings it back up automatically after a server reboot, and can run several copies of the app across CPU cores, spreading the load between them. Without such a manager, any crash would mean the app stays down until someone connects by hand and starts it again.

Putting it all together, the request path looks like this. A user in the mobile app calls a secure address, the request reaches Nginx, which decrypts the connection and passes it to the internal port where PM2 keeps Node.js running. Each element does its own narrow job, and together they deliver stability.

Step-by-Step Deployment of a Node.js API on a VPS

Now we go through the whole process step by step. The commands assume Ubuntu 24.04, and we run them in order after connecting to the server over SSH.

Step 1. Prepare the Server and a Separate User

Right after creating the server, we refresh the package list and the packages themselves to close known vulnerabilities and pick up fresh versions:

sudo apt update && sudo apt upgrade -y

Running the application as the root superuser is dangerous: if an attacker finds a hole in the code, they gain full power over the server. So we create a separate user with limited rights specifically for the app:

sudo adduser nodeapp

From here on we do most of the application work as this user, leaving root only for system tasks.

Step 2. Install the Right Node.js Version

The standard Ubuntu repository usually carries a badly outdated Node.js version, sometimes several major releases behind. So we install it from the official NodeSource source, which provides current builds. We add the repository for the 24 LTS line and install:

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -

sudo apt install -y nodejs

We confirm the install with node -v. The response should show a version starting with 24. An alternative path is the nvm version manager, handy when one machine needs to keep several Node.js versions at once.

Step 3. Run the Application Through PM2

We copy the app code to the server (usually via git clone from a repository) and install dependencies with npm install. Then we install the process manager globally:

sudo npm install -g pm2

We launch the app under PM2. The cluster mode brings up copies of the app per CPU core and spreads incoming requests across them:

pm2 start app.js -i max --name my-ap

For the app to come back on its own after a reboot, we run pm2 startup and then pm2 save. The first command creates a startup service, the second remembers the current process list. Now, even after a sudden reboot, the API returns on its own without our help.

Step 4. Configure Nginx as a Reverse Proxy

We install the web server:

sudo apt install -y nginx

We create a site configuration that tells Nginx to accept requests for our domain and forward them to the internal port of the application. The key part of the config looks like this:

location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }

After saving, we check the configuration with sudo nginx -t and reload the server with sudo systemctl reload nginx. From this point the app is reachable by domain name, while the internal port stays hidden from the outside world.

Step 5. HTTPS Through Let's Encrypt

Plain HTTP sends data in clear text, and any node between the app and the server can read it. For a mobile API that is unacceptable, so we always encrypt the connection. We issue a free certificate through Let's Encrypt with the certbot utility:

sudo apt install -y certbot python3-certbot-nginx

sudo certbot --nginx -d api.yoursite.com

Certbot writes the certificate into the Nginx config and sets up the redirect from HTTP to HTTPS on its own, so all traffic flows over a secure channel. The certificate renews automatically and needs no separate attention.

Step 6. Firewall and Final Check

We close the server off from unnecessary connections. We turn on the built-in UFW firewall and allow only what is truly needed: SSH access and web server traffic.

sudo ufw allow OpenSSH

sudo ufw allow 'Nginx Full'

sudo ufw enable

Finally, it makes sense to reboot the server and confirm that after startup the app came back on its own through PM2 and answers at the secure address. If the response arrives, the basic deployment is done.

What Makes a Mobile App API Different

The server side of a mobile app lives by its own rules, and a few points are worth handling up front so you do not redo them later.

Data is a topic of its own. If a mobile app collects personal data of US users, hosting the backend on our own VPS keeps that data inside the country and makes it easier to meet rules like the California Consumer Privacy Act, or HIPAA for health-related data. Full control over the server means we decide where the database physically sits and who has access to it. Serverspace runs its US servers in a New Jersey data center, so data stays in the United States. This is one reason teams choose a US-based VPS over external platforms with unclear data location.

PM2 or Docker: What to Choose for Your API

As a project grows, the question comes up of whether to run the app directly through PM2 or pack it into a Docker container. It helps to understand that these tools solve different problems and often work together.

PM2 manages processes. It makes sure the app keeps running, restarts it, and spreads load across cores. For a single server and a fast start this is the simplest path.

Docker packages the environment. A container puts the app together with all its dependencies into an isolated box that runs the same way on any machine. This is handy when we need to move the app between servers or scale it across several hosts.

In practice many combine both approaches: they run PM2 inside a Docker container, getting both environment isolation and process management. The summary table below helps pick a starting point for a specific project.

Criterion PM2 Docker PM2 + Docker combo
Ease of starting High, installs with one command and runs the app right away Medium, you write a Dockerfile and learn about images Lower, needs knowledge of both tools
Environment isolation None, processes share the server system Full, app and dependencies in a separate box Full, thanks to the container
Scaling Across cores of one machine via cluster mode Across several machines and orchestrators Both across cores and across several hosts
Portability Depends on the specific server setup High, the image runs the same everywhere High, thanks to the container
Best fit Starting on one VPS and launching fast Moving between servers and growing onto several machines Mature projects needing isolation and resilience

How to Speed Up the API and Keep It from Falling Over

Once the basic launch is ready, it is worth building in a margin of safety up front. A few simple measures noticeably raise response speed and resilience.

These measures do not require rewriting the app and still give a tangible effect. We can begin with any of them and add the rest as the audience grows.

Common Deployment Mistakes and How to Avoid Them

Most early problems repeat from project to project. Let us go through them together with solutions.

Where This Is Actually Used: Typical Scenarios

To keep the picture concrete, let us look at where this deployment approach shows up most often.

  1. A startup MVP on a single server. The team tests an idea and keeps the whole app backend on one VPS. Cheap, fast, and enough for the first few thousand users.
  2. A shared backend for iOS and Android. The same Node.js API serves both versions of the app at once. There is no need to duplicate server logic; both platforms call the same addresses.
  3. A project with sensitive data. When an app handles medical, financial, or other personal data, an own server gives control over where it is stored, which matters for meeting protection rules.
  4. Smooth scaling as the audience grows. Starting on a modest plan, the project adds server resources as it grows, and later moves to containers and several machines if needed.

In all these scenarios the starting point is the same: a VPS with a clear price where we fully control the environment. For a start, renting a virtual server from Serverspace fits well, and from there the server capacity scales with the load without moving to another platform.

The Bottom Line and Where to Start

We walked the path from a clean virtual server to a working Node.js API ready to serve a mobile app. The key combination is Node.js on an internal port, PM2 for process resilience, and Nginx with a certificate for secure access from the outside. On top of that we added token authentication, request rate limiting, and the baseline protection specific to mobile clients.

The concrete next step is simple. We spin up a VPS on Ubuntu 24.04, install Node.js 24 LTS, and walk through the steps in the deployment section. Once the basic API runs at a secure address, we gradually add caching, monitoring, and containerization if needed. This order gets a working result quickly and lets us grow it without painful rewrites.

FAQ

Can we do without Docker?

Yes, for a single server the Node.js, PM2, and Nginx combination is enough. Docker makes sense to add when a need appears to move the app between machines or scale it across several hosts.

Why is PM2 better than running through systemd?

Systemd can also keep a process alive, but PM2 is tuned for Node.js: it gives cluster mode by core count, convenient log viewing, and restarts with no downtime. For Node.js apps this saves setup time.

Do we need a separate domain for the API?

It is convenient to set aside a subdomain like api.yoursite.com. This simplifies certificate issuance and separates the server side from the site, though such a split is not strictly required.

How do we update code without downtime?

We deploy the new version of the code and run pm2 reload. In cluster mode the processes restart one by one: while one updates, the rest keep answering, and users notice no downtime.