Server and Infrastructure: A VPS as the Foundation

Server and Infrastructure: A VPS as the Foundation

Date
1/25/2026

If you’ve ever worked with all-in-one solutions like Vercel or Firebase Hosting, you know the convenience: one click, one deploy, and your app is live on the web. Sounds great – but it comes at a price. As soon as you want to host more complex projects consisting of multiple components (website, backend, mobile app), you quickly hit limitations.

That’s why I chose a VPS (Virtual Private Server). A VPS gives you full control over your system. You decide which services run, how they communicate, and how you scale your app. Sure, it’s a bit more hands-on – but that’s exactly where the learning effect and flexibility come in.

Personally, I use Hostinger VPS Hosting. I’ve had very good experiences with it. Setup is quick, prices are fair, and the interface is clear even for beginners. So I can highly recommend using it for this and other personal projects. For small to medium-sized projects like ours, the cheapest plan is more than enough. You can order it directly here. Otherwise, you’ll find here an overview of other Hostinger packages. The important thing is to select a VPS hosting plan.

During your order, you’ll need to specify which operating system your VPS should use. Here, you can simply choose the latest version of Ubuntu. This is supported by all common frameworks and is perfect for our use case.

Why VPS instead of All-in-One?

Before we get started, a quick comparison:

  • Vercel, Netlify & Co.

    • Quick setup, minimal configuration

    • Ideal for small projects and pure frontend hosting

    • Little control over server architecture, databases & security

  • VPS (Virtual Private Server)

    • You control everything: routing, ports, processes

    • One infrastructure for web app, mobile app, and backend

    • Cheaper as you scale up

    • Better learning curve (because you really understand how deployment works)

Of course, it always depends on your personal goals. But if you want to build a real infrastructure from various apps and a stable backend, I can only highly recommend a VPS.

I use Hostinger VPS Hosting. It’s relatively inexpensive, fast, and stable. For starters and smaller projects, the cheapest plan is more than enough. You can find it here. Before purchasing, Hostinger will ask how you want your server configured. It’s best to use a simple operating system and select Ubuntu. In the popup, you can simply choose the latest version of Ubuntu. Then you enter a few personal details and voilà – your VPS is ready.

Setting up your VPS

  1. First login via SSH

    • Once the VPS is set up, you’ll receive an IP address.

    • You’ll find this at Hostinger under VPS.

    • In your VPS overview at Hostinger, you’ll find the command to establish an SSH connection with root access. You can also change your SSH login password there. Use the following command to connect:

    ssh root@[your server IP]

    • Enter password → done.

  2. Update the system

    sudo apt update && sudo apt upgrade -y

    This ensures all packages are up to date.

Install Nginx & Connect Domain

Now we’ll install nginx. Nginx is a reverse proxy web server. It’s basically the bouncer for your server. Whenever someone accesses one of your domains, nginx receives the request and forwards it to the appropriate place (e.g., your NestJS backend). This way, you can make content available on specific ports or, for example, allow access to files in a specific folder on your server. Nginx is truly an all-rounder. Later, nginx will also manage our SSL certificates (HTTPS) to ensure secure access to our domains.

  1. Installation

    sudo apt install nginx -y

    Afterwards, you can immediately check if nginx is running:

    systemctl status nginx

    If you now open your server’s IP in the browser, you should see the default nginx page.

    Hooray! Your server is now accessible from the internet! 🎊 🎉

  2. Point your domain to the server

    If you already own a domain or buy one, you can easily connect it to your server. To do this, you need to adjust the DNS records in your domain dashboard. You’ll usually find this where you bought the domain, such as Hostinger.

    There, you need to create a new A record. Set the name to @ and the new entry should point to your server’s IP address.

    After creating the new entry, you’ll need to wait a few minutes or hours, and then it should be accessible in the browser. Again, you’ll initially just see the default nginx page.

  3. Prepare nginx
    Next, we’ll create a simple server configuration so we can later access our API (Application Programming Interface), i.e., our backend. For this, we’ll create a new folder called sites-available in the /etc/nginx directory on our server. Later, we’ll list all processes managed by nginx there. In summary, we’ll store a configuration for our NestJS app and one for our NextJS app there. The trick is that we can create as many processes as we want here, but they won’t be automatically considered by nginx. If we want nginx to consider one of the configurations, we can simply create a symlink to this config file in /etc/nginx/sites-enabled. This allows us to test different processes and roll them back at any time without having to delete them completely. So you can easily add more sites to your server.

    First, we’ll create the config file for our NestJS app. Since it’s an API for a ToDo app, we’ll call it todo-api.conf. The command sudo nano opens the new file in a text editor so we can edit it.

    sudo nano /etc/nginx/sites-available/todo-api.conf

    Set the content of this file as follows:

    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;
        }
    }

    Here’s a brief explanation of the most important parts of the config file:

    listen 80; --> This line tells nginx to listen on port 80. That’s the standard port for HTTP.

    server_name --> This is the domain name nginx should listen to. So in this case, nginx says: “your-domain.com is called, so I’ll forward this request.” If you don’t want to use a domain, you can simply enter your server’s IP address here.

    location / --> This block defines how nginx should handle requests to the root path (“/”). It sets up a reverse proxy to forward requests to a local service running (in this case) on port 3000.

    proxy_pass http://localhost:3000; --> This line specifies which local service the request should be forwarded to. We need to make sure our NestJS app later runs on port 3000.

    Finally, we can activate our nginx forwarding. As mentioned, we do this by creating a symlink of our config file in the sites-enabled folder. With the second command (sudo nginx -t), we check the syntax of our nginx configuration. Finally, we restart our nginx server (sudo systemctl reload nginx) and our configuration should be active.

    sudo ln -s /etc/nginx/sites-available/todo.conf /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx

    Now, whenever we receive requests to your-domain.com, they’ll automatically be forwarded to http://localhost:3000 on our server. Now we just need to have our NestJS app running there.

Create a first small NestJS app

To test the nginx setup later, we need a first app. NestJS is perfect for this because it enables structured APIs in TypeScript and can integrate OpenAPI (Swagger) out of the box.

At this point, I recommend accessing your server with a code editor like Visual Studio Code. Visual Studio Code, for example, offers remote SSH connections, allowing you to develop your apps directly on the server.

NestJS is a NodeJS framework. The first step is to install NodeJS on our server. We do this as follows:

apt install nodejs npm -y

To use NestJS now, we need the NestJS CLI (Command Line Interface). We install it as follows:

npm i -g @nestjs/cli

Now we can create a new NestJS project via the command line. It’s best to go to the /var/www folder by entering the following command:

cd var/www

Here we create our NestJS app:

nest new todo-api
cd todo-api

To get a visual confirmation, we’ll enable Swagger (OpenAPI). Open the main.ts file and adjust it as follows:

import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { NestFactory } from '@nestjs/core';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('ToDo API')
    .setDescription('API documentation for our ToDo app')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

Now start the app:

npm run start:dev

If you now access your domain via your-domain.com/api or your server’s IP address via [IP address]/api in the browser, you should see the Swagger docs. Perfect! The API is running locally on the server.

Manage processes with PM2

As soon as you close your terminal, you won’t be able to access your domain anymore. That’s because closing the command line also stops the NestJS service. To prevent this, we’ll use PM2. PM2 is designed to run and manage multiple NodeJS services in the background. So we can easily run both our NestJS app and our NextJS app on the server without having to keep a terminal open. PM2 also allows us to disable certain processes at any time if we no longer need them, without having to delete them completely.

First, we install PM2:

cd ~
sudo npm install -g pm2

Then we go to our app’s directory, if you’re not already there:

cd /var/www/todo-api

Now we can start our first PM2 process. We do this with the following command:

pm2 start npm --name "todo-api" -- start

This command creates a PM2 process named "todo-api". You can give the process any name, but I recommend choosing a descriptive name so you can easily find it later. After running the command, you should see an overview of your running PM2 processes. There’s probably only your newly created process, and it should have the status "online".

You can view your processes at any time with the following command:

pm2 status

You can stop your process with the following command:

pm2 stop todo-api

And you can restart your process with the following command:

pm2 restart todo-api

If the process is stopped, you shouldn’t be able to access the API page in the browser anymore. Play around with the different commands and test it yourself to get a better understanding of the topic.

Encrypt your app (Optional)

As I mentioned earlier, nginx also manages SSL certificates for your domains. So you can easily and freely equip every domain and subdomain with an SSL certificate, making them accessible via HTTPS. All you need is certbot. Install it as follows:

apt install certbot python3-certbot-nginx -y

Next, run the following command:

sudo certbot --nginx

Here, first select all domains for which you want to request SSL certificates and confirm your selection with Enter. If you then look at your nginx config file again, you’ll notice a lot has changed. These changes were made automatically by certbot and ensure that all traffic to your domain is redirected to HTTPS. Your domain is now accessible and secure! 🎊 🎉

Conclusion

In this article, we have:

  • set up a VPS,

  • installed nginx as the “bouncer” for requests,

  • created a small NestJS app with Swagger,

  • set up PM2 to manage your NodeJS processes,

  • made the API cleanly accessible via our domain,

  • and requested an SSL certificate for our domain.

That lays the foundation. In the next part, we’ll focus on expanding the backend step by step – with authentication via Firebase Authentication and the first ToDo endpoints.

👉 Don’t forget: If you want to try a VPS yourself, I recommend Hostinger VPS Hosting.

Comments

Please sign in to leave a comment.

Host NestJS on a VPS with nginx and pm2