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 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:
# 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.
As I am using Adonis.js for this guide, I initiate a new app, a web starter template:
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:
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
hello-world in git remote address corresponds to a server app name that we should create on the server:
# 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:
# on your computer # inside project folder node ace generate:key > iGyX0deixdW7DkdJ9G9PbyyT8QaizXuK
# 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.
web: node build/server.js
Now we are ready to deploy our app for the first time:
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.
Dokku supports multiple databases, MySQL, PostgreSQL, MongoDB, Redis and others.
Here I will install PostgreSQL and then link it to my app:
# 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.
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:
web: node build/server.js release: node build/ace migration:run --force
Commit new updates and run a deploy again!
git push dokku master
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:
# 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!
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.