Host Your Personal Projects on a Single VPS
Is Heroku becoming too expensive? Are you looking for a cheaper alternative to host your personal projects?
What if you could save money and learn valuable skills in the process? Skills that will make you stand out in the job market among your peers.
Perhaps you've asked yourself:
How do I deploy my backend on the same site as the frontend so the API calls go through api.mydomain.com?
In this article, you'll discover three different methods of project organisation — by domain, subdomain or subdirectory. You'll see how they work behind the scenes and learn how to host multiple projects on a single server.
Why host your personal projects on a VPS?
When you use a Platform-as-a-Service (PaaS) like Heroku, AWS Elastic Beanstalk or Google App Engine, you're paying for each application which quickly gets expensive. Alternatively, static site hosting like Netlify and Vercel can't host databases and a serverless backend is limited in what it can do.
In many cases, having your own VPS is the cheapest way to host your portfolio website along with your personal projects. Using a VPS is a great way to learn how to secure a server, get more familiar with the command line and practice deploying applications. It's also tremendously fun to have your own server in the cloud to play with.
The downside of a VPS is that you are in charge of deployments and making sure your application stays alive. If you just want to quickly ship and have more time to focus on a product that generates revenue, managing a server may not be the best choice.
However, personal projects are not businesses. They're meant to showcase your skills and value to a potential employer or client. Additionally, some familiarity with Linux and deployments are valuable skills in the job market. In which case, a VPS is an excellent choice.
Where can you get a VPS
The provider landscape is split into two groups — the corporate giants and the smaller businesses. In the giant's category, there is AWS EC2 (Amazon), Google Compute Engine (Google) and Azure VM (Microsoft). On the other end, there is Digital Ocean, Linode, Vultr and a long tail of smaller providers.
Main differences between the two provider groups are the number of cloud services they offer and the user-friendliness of their products. While the smaller businesses offer fewer services, they make up for it in user-friendly UI and clear documentation.
If you're already familiar with any of the above-mentioned providers, go with that. Otherwise, I suggest choosing one of the smaller providers so you don't waste time navigating the cumbersome UI of bigger co's. The smaller providers are pretty much on par when it comes to price, support and quality so it doesn't matter which one you choose. If you're in decision paralysis, go with Digital Ocean 😉.
Organising your personal projects
There are three ways you can organise your projects — by domain, subdomain or subdirectory. Given three applications, the resulting URLs would look as follows:
- Domain - recipeapp.com, weatherapp.com, ecommerceapp.com
- Subdomain - recipeapp.yourdomain.com, weatherapp.yourdomain.com, ecommerceapp.yourdomain.com
- Subdirectory - yourdomain.com/recipeapp, yourdomain.com/weatherapp, yourdomain.com/ecommerceapp
Organising by domain involves buying separate domains for all of your applications. For most developers, that's too much hassle to go through for their personal projects. However, if you have already done some volunteer or client work, you probably went this route as it has a more professional look and it doesn't interfere with your personal domain. You can still host applications on different domains on a single VPS. You just have to make sure their DNS records point to the same IP address.
Subdomains are a great choice since you only need to buy one parent domain. But you still have to add a DNS record and configure SSL for each project, unless you use wildcard records.
Organising by subdirectory allows you to quickly add new projects since you don't need any DNS changes. The downside of this approach is you have to change all the links and routes in your application to work with the subdirectory (e.g.: /weatherapp) instead of the root path (/). This can be tricky to get right. It's also a bit more effort to extract one application out to a different server if you wish to do so in the future.
The three methods are not mutually exclusive. You can mix them together on a single VPS. In a later section, I'm going to show you how to configure for each method.
Serving multiple applications from a single server
First, if you're organising your projects by domain or subdomain, you need to make sure they all point to the same IP address. You can configure this in the dashboard of your DNS provider. The process is different for each provider, but you're looking to set a type A record with the domain/subdomain as the name
, and the IP address of your server as the content
. You can find the IP address of your server in the dashboard of your VPS provider.
If I would have a personal project named weatherapp on a separate subdomain, my DNS records would look like this:
maximorlov.com. 299 IN A 104.27.181.7
weatherapp.maximorlov.com. 299 IN A 104.27.181.7
To serve multiple applications from a single server you will need to use a reverse proxy. That's because only one application is allowed to listen on the same port at one time. When someone types a URL in the browser, the request goes to the default internet port — port 80 for HTTP or port 443 for HTTPS. It's possible to send a request from the browser to a different port if you specify it in the URL, for example, yourdomain.com:3000, but that's awkward for your visitors.
A reverse proxy takes all incoming requests on the default internet ports and routes them to the respective application. Nginx is the most popular reverse proxy in the Node.js community. You can also use a different reverse proxy, like Apache, if that's what you're familiar with.
Configuring Nginx for static file hosting and reverse proxying
I'm going to jump straight to configuring Nginx and show you some basic configurations so you get an idea of how a reverse proxy works. To get started with Nginx, I recommend this detailed installation tutorial written by the folks at Digital Ocean.
As long as there are enough resources available (CPU, RAM etc.), you can host as many applications as you want on a single VPS. Let's say we have three personal projects:
- A recipe application that's just a frontend without a backend
- A weather application with a frontend and a backend that does some API requests to a 3rd party, but no database
- An e-commerce website with a frontend, a backend and a database
As far as Nginx is concerned, it doesn't care whether the application has a database or not. I'm using these examples to show you the flexibility a VPS gives you to host whatever you want. I'll explain how to deploy each application in future articles, as that is very specific to the stack you're using.
We'll assume the Node.js servers for the weather and e-commerce apps have been started properly and listen on ports 3000 and 3001, respectively. The recipe app will be served by Nginx directly since it doesn't have a backend. Nginx can do that since it in itself, is a server.
Domain-based configuration
For domain-based organisation, we define a virtual server for each application. When a request comes in, Nginx goes through each server
block from top to bottom and compares the request Host
header with the server_name
value. Upon a match, that request is handled by the respective server block. Have a look at the configuration file below:
# Basic Nginx configuration routing traffic based on organising by domain
events {}
http {
# Configuration block for recipeapp.com
server {
listen 80; # HTTP
server_name recipeapp.com;
# Build directory of a frontend-only application
root /home/node/recipeapp;
}
# Configuration block for weatherapp.com
server {
listen 80; # HTTP
server_name weatherapp.com;
# Route all requests to port 3000 on localhost
location / {
proxy_pass http://localhost:3000;
}
}
# Configuration block for ecommerceapp.com
server {
listen 80; # HTTP
server_name ecommerceapp.com;
# Route all requests to port 3001 on localhost
location / {
proxy_pass http://localhost:3001;
}
}
}
proxy_pass
is where the actual reverse proxying is happening — Nginx hands off the request to the Node.js servers.
You'll notice recipe app doesn't have a reverse proxy configuration because that application doesn't have a backend Node.js server. Instead, Nginx will serve its files from the root
location matching the URL. As an example – given a request to recipeapp.com/images/pie.png, Nginx will look for a file at the following location: /home/node/recipeapp/images/pie.png.
Subdomain-based configuration
The configuration for organising projects by subdomain is very similar. The only difference is the server_name
values have changed to their respective subdomains:
# Basic Nginx configuration routing traffic based on organising by subdomain
events {}
http {
# Configuration block for recipeapp.yourdomain.com
server {
listen 80; # HTTP
server_name recipeapp.yourdomain.com;
# Build directory of a frontend-only application
root /home/node/recipeapp;
}
# Configuration block for weatherapp.yourdomain.com
server {
listen 80; # HTTP
server_name weatherapp.yourdomain.com;
# Route all requests to port 3000 on localhost
location / {
proxy_pass http://localhost:3000;
}
}
# Configuration block for ecommerceapp.yourdomain.com
server {
listen 80; # HTTP
server_name ecommerceapp.yourdomain.com;
# Route all requests to port 3001 on localhost
location / {
proxy_pass http://localhost:3001;
}
}
}
Subdirectory-based configuration
Configuring Nginx for projects on different subdirectories is a little bit different. Instead of multiple server
blocks, we have multiple location
blocks. Nginx decides how to process a given request by looking for a location
block that matches the URL path.
# Basic Nginx configuration routing traffic based on organising by subdirectory
events {}
http {
server {
listen 80; # HTTP
server_name yourdomain.com;
# Configuration block for yourdomain.com/recipeapp
location /recipeapp {
# Build directory of a frontend-only application minus the path
root /home/node;
# Serve files root + path location, otherwise look for an index.html file in the folder before responding with a 404 status code
try_files $uri $uri/index.html =404;
}
# Configuration block for yourdomain.com/weatherapp
location /weatherapp {
# Route all requests to port 3000 on localhost
proxy_pass http://localhost:3000;
}
# Configuration block for yourdomain.com/ecommerceapp
location /ecommerceapp {
# Route all requests to port 3001 on localhost
proxy_pass http://localhost:3001;
}
}
}
Because Nginx adds the URL path to the root when looking for a file's location, we've changed the root
value from /home/node/recipeapp to /home/node.
The try_files $uri $uri/index.html =404
directive instructs Nginx to look for files that match the URL path or an index.html file before responding with a 404 status code. This way users won't have to type yourdomain.com/recipeapp/index.html but yourdomain.com/recipeapp will work as well.
When you organise projects by subdirectory, don't forget to change the links in your application. If you previously had a link pointing to yourdomain.com/amsterdam in your weather app, it now has to be yourdomain.com/weatherapp/amsterdam. This includes static assets such as images, JavaScript and CSS files.
These basic examples give you an idea of how to host several projects on a single VPS. In production, you often want to include additional configuration for caching, security and SSL so your applications work over HTTPS.
In summary
A VPS is an excellent choice to host your portfolio website along with your personal projects. It's often cheaper than the alternatives and a great way to learn more about Linux and the command line.
You can organise your projects by domain, subdomain or subdirectory and we've seen how to configure Nginx for each method.
Now it's your turn! Go out and apply what you've just learned to your portfolio website.