Deploy your Node.js app without a hassle

Created on 11 Jul 20211029 words・5 min read

The process of app deployment takes some time, you have to configure the server, find all the information and be ready to handle the issues, but there is an easier way.

This guide is relevant literally for any app, for Ruby and Rails, for Python and Django, for PHP and Laravel, for Go and micro-services, you can deploy easily anything, but I will use Adonis.js as an example.

I guess any developer knows what is Heroku, they've been de facto a standard of Platform-as-a-Service and showed the world how an app deployment should really work, easily and fast, requiring zero configuration for most cases.

Heroku is not a cheap service, their prices are pretty high and aren't great for pet projects or small services with zero revenue.

There are some cheaper alternatives like Render, Railway or Digital Ocean App platform, but there is a self-hosted solution that works almost like Heroku and is free of charge, you only have to bring your own server.

Dokku

Dokku is the smallest PaaS implementation that allows you to build, manage and deploy your apps on your own server, that may cost you as little as $3.50 per month(I use and recommend Hetzner Cloud, it is a referral link that will give you €20 in credit).

It uses heroku buildpacks and is able to deploy your app using dockerfile as well, it configures nginx as a proxy server, you can install databases and connect them to your app, you can install Let's Encrypt for SSL certificates, you can deploy monorepos, you can mount local storage, there are multiple plugins that handle most use cases and require almost zero configuration.

To install Dokku, you have to allocate a server from you favorite provider, with a minimum requirement of 1 core and 1GB of RAM, then login into your new shiny server and run two commands:

bash
# for latest tag check Dokku git repo
# or https://dokku.com website

wget https://raw.githubusercontent.com/dokku/dokku/v0.24.10/bootstrap.sh
sudo DOKKU_TAG=v0.24.10 bash bootstrap.sh

Then go to your server's IP and follow the web installer process.

Now you have a working self-hosted PaaS that is ready to build and deploy your code.

Deploying an app

As I am using Adonis.js for this guide, I initiate a new app, a web starter template:

bash
npm init adonis-ts-app@latest hello-world

# If using yarn
yarn create adonis-ts-app hello-world

Then we have to initiate git repository for this app:

bash
cd hello-world

# initiate git
git init

# stage all files
git add .

# create first commit
git commit -m 'Init commit'

# add Dokku server as a remote
git remote add dokku dokku@<YOUR_SERVER_IP_ADDRESS_OR_DOMAIN>:hello-world

The part hello-world in git remote address corresponds to a server app name that we should create on the server:

bash
# on the Dokku host
dokku apps:create hello-world
-----> Creating hello-world...

For Adonis.js to start we have to set some environment variables, Dokku provides an easy to use command for this:

bash
# on your computer
# inside project folder
node ace generate:key
> iGyX0deixdW7DkdJ9G9PbyyT8QaizXuK
bash
# on the Dokku host
dokku config:set hello-world \
  HOST=0.0.0.0 \
  APP_KEY=iGyX0deixdW7DkdJ9G9PbyyT8QaizXuK \
  APP_NAME='Hello World' \
  CACHE_VIEWS=true \
  SESSION_DRIVER=cookie

Just before the deploy it is a good idea to create a Procfile and describe how our PaaS should start our app, this file is a very common configurational file, read more about it here. Don't forget to commit it to the repo.

procfile
web: node build/server.js

Now we are ready to deploy our app for the first time:

bash
git push dokku master

After a successful deployment Dokku will print you the address where you can access your app, if you chose port based deployments, your address will look like http://<YOUR_SERVER_IP_ADDRESS>:<PORT>, if you chose hostname based deployments, it will look like this: http://<APP_NAME>.<YOUR_SERVER_DOMAIN>. You can add a domain to the app later if needed.

Adding database

Dokku supports multiple databases, MySQL, PostgreSQL, MongoDB, Redis and others. Here I will install PostgreSQL and then link it to my app:

bash
# on the Dokku host
# install the postgres plugin
# plugin installation requires root, hence the user change
sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git

# create a postgres service
dokku postgres:create hello-world-database

# on the Dokku host
# each official datastore offers a `link` method to link a service to any application
dokku postgres:link hello-world-database hello-world

Linking adds a new environment variable to the app, DATABASE_URL that is a database connection string with all the credentials we need.

For Adonis.js to work with database we should install Lucid ORM and configure it properly.

bash
npm install @adonisjs/lucid@latest
# or
yarn add @adonisjs/lucid

# and then
node ace configure @adonisjs/lucid

After installation don't forget to add new database environment variables to Dokku, you can use DATABASE_URL that was provided before or split it to separated values, connection string url follows a well known format, so it is not a problem to identify credentials and host address.

To run database migrations on each deploy we should update our Procfile by adding new values:

procfile
web: node build/server.js
release: node build/ace migration:run --force

Commit new updates and run a deploy again!

bash
git push dokku master

Encrypting connection

Dokku is built on top of plugins. One of them uses Let's Encrypt to provide SSL certificates to Nginx proxy server.

Installing the plugin is as simple as running a command:

bash
# on Dokku host
sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

To encrypt the connection of your app run dokku letsencrypt hello-world and then dokku letsencrypt:cron-job --add to add a crontab job that will renew certificates when needed.

That's it, your app is now served using SSL!

Conclusion

Dokku is a great Heroku-like tool for build and deploy automation, it is universal and can be used almost by any company. It does not support clustering and brings some Docker problems with it, but well, if you need clustering for your app and have that many requests per second, then you may also have money for Heroku or others, or even your personal DevOps engineer.

You may read more about dokku command and possibilities on their website.