Self-Hosting Node.js Apps:
The Nicer Way

I'm always interested in how other people solve problems or technical challenges they face. Since I want to be a more active part of the ones sharing their solutions your are more than welcome to read how I do self host (some) of my Node.js applications with Docker, GitLab and Caddy.

The basis is a VPS with an Ubuntu (20.04 LTS) installation and some common security messures enabled to secure the server.
What is nice about this setup: it get’s you started for about 5$ per month. All the other tools/software mentioned in this article is open source and can be used (in the beginning) on a free plan.

Components

Docker & Docker Compose

That should not be a surprise to anyone at this point: far better than having to install the runtime environment directly on the server and therefore running into potential issues later on (e.g. that different applications require different runtime versions). So let’s have everything running in an isolated Docker container.

For a small Node.js application this is a simplified template for a Dockerfile to get you started:

[object Object]

Note: this is a simplified Dockerfile. Please read

why you should not be using it like this in production

.

Since I usually run more complex applications with multiple services (e.g. the frontend and an API providing the backend) I use Docker Compose to manage and run them.

[object Object]

The code sample above is a basic docker-compose.yml file for running a single service/application.
I basically just define the Docker image that should be used to run the service and the port of the host machine through which the service should be accessible.
Continue reading and check the .docker-deploy template for a reference to the given variables.

GitLab - Docker Registry & CD Pipeline

While speaking about Docker – GitLab has its own Docker registry. Which fits perferctly in the stack since we are using the GitLab CI/CD pipeline for the deployment of the application.

[object Object]

This is the job template I am using to build docker images of tags being pushed to the GitLab repository.
The support is already “baked in” since we can use the $CI_JOB_TOKEN variable to authenticate against the registry and push the built Docker image from the GitLab runner.

After the image was built the GitLab runner does SSH into the host machine, pulls the updated docker image and restarts the Docker container.

[object Object]

Caddy

What we are left with is the need for a proxy server to make our application available to the outside world.

A task I did use Nginx for in the past. But this came with the need to handle TLS certificates via Let’s Encrypt etc.
When I learned about

Caddy

this was a task I was more that happy to get rid of. It is an open source web server written in Go that does a lot of nice things for you by default (e.g. TLS certificate handling).

So in our example it just takes three lines of configuration to map our domain to the port where our app is accessible on:

[object Object]

That’s it! 🙏
With this setup I get a push to deploy time of around 2-3 minutes. Absolutely fine for what I am doing with it right now.

Ansible

To make the whole VPS setup less tedious I use

Ansible

(an automation software for IT infrastructure). This takes care of the basic server setup (e.g. security measures, configuring the firewall, installing Docker, etc.). This way I am able to duplicte the VPS setup quickly in case I want to switch the VPS provider or scale the application.
Because Caddy could as well

be your load balancer

for multiple instances of your application.

Outlook

The current setup is bound to tags beings pushed. What would be nice is to have a per branch preview sometimes. This is where

GitLab review apps

could come into play.
But these are tasks for future Nils 🔮