News
Serverspace Expands LLM Selection: Claim 25% Off New Models
AC
Artemis Cooper
June 9 2026
Updated June 30 2026

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

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.

  • A VPS running Ubuntu 24.04 LTS. This is a recent long-term-support release that most current tutorials target. An entry-level server will do, and we cover the exact resource requirements below.
  • A domain name. To issue a free certificate and get clean addresses like api.yoursite.com, we need a domain with an A record pointing to the server IP address.
  • SSH access. This is the secure way to connect to the server remotely from a terminal and run commands as if we were sitting right at it.
  • A Node.js version. For a production server in 2026 we go with Node.js 24 LTS. Version 26 currently holds Current status and it is too early to put in production, while version 22 has already moved into the Maintenance phase. The long-term-support line ships security updates over a long horizon, which is exactly what a server meant to live for years needs.

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.

  • HTTPS is strictly required. Modern iOS and Android refuse by default to reach insecure addresses. That means the certificate step from the instructions above is mandatory for a mobile backend; without it the app simply cannot talk to the server.
  • Token-based authentication with JWT. Mobile apps work poorly with the cookies that sites rely on. So login is usually built on JSON Web Tokens: after login the server issues the app a token, and the app attaches it to every request in a header. A pair of a short-lived access token and a long-lived refresh token works well for renewing access.
  • Request rate limiting. To guard against password guessing and abuse, sensitive addresses like login and token refresh get a cap on the number of requests from one address over a time window. In Node.js this is done with the ready-made express-rate-limit library.
  • API versioning. Addresses like /api/v1/ let us ship new server versions without breaking older app versions on users who have not updated yet. This saves us from a server update suddenly knocking out part of the audience.
  • Input validation and security headers. We check any data from the app before processing with ready-made validators, and set service security headers with the Helmet library. Secret keys and database passwords live in a separate environment file and never go into the code repository.
  • Push notifications go separately. Pushes themselves are usually sent through Apple and Google services (APNs and FCM), so there is no need to keep a separate push server of our own. Our API only calls these services when it wants to send a notification.

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.

  • Caching with Redis. If the app often asks the database for the same data, we keep it in the fast Redis store in memory. On real projects, moving heavy repeated queries into a cache cut response time several times over, for example from hundreds of milliseconds down to tens.
  • Response compression. Gzip compression enabled in Nginx shrinks the volume of transferred data. Mobile clients unpack it automatically, while traffic and load time drop.
  • Cluster mode in PM2. Running the app as several processes per core lets it handle more concurrent requests and use the server CPU more fully.
  • Monitoring and log rotation. It is worth watching the load and setting up automatic log rotation so logs do not fill the disk. PM2 can rotate logs through an extra module.

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.

  • Installing Node.js from the standard apt. It carries an outdated version. Solution: install from the NodeSource repository or through nvm, as described above.
  • Running the app as root. This opens full access to an attacker at the first vulnerability. Solution: create a separate user with limited rights.
  • Exposing the Node.js port directly to the internet. An app open to the outside with no reverse proxy stays without encryption and protection. Solution: always put Nginx in front and keep the internal port closed.
  • Secrets in the repository. Keys and passwords that land in git leak sooner or later. Solution: keep them in an environment file and add that file to gitignore.
  • No request rate limiting. Without limits the login address is open to password guessing. Solution: connect a limiter on sensitive addresses.
  • Manual edits straight on the production server. Changes made on the fly over SSH and recorded nowhere break predictably and are hard to fix. Solution: make changes through a proper code deployment process.
  • Forgotten PM2 autostart. If we skip pm2 startup and pm2 save, the app stays down after a server reboot. Solution: set up autostart right at the first deployment.

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.

You might also like...

We use cookies to make your experience on the Serverspace better. By continuing to browse our website, you agree to our
Use of Cookies and Privacy Policy.