Running a NodeJS app with Postgres in Dokku

Published on December 29, 2021

I have some side projects that don’t see much traffic so I run them on a 5$ Digital Ocean droplet running Dokku.

Dokku is an open source Heroku-like platform. It’s super easy to create and maintain multiple applications on a Dokku instance. It’s perfect for solo makers.

There are plugins for most of the common services you might want like PostgreSQL or Redis.

Here’s what we’re going to do

  1. A Brief overview of Dokku
  2. How to get a Dokku instance running
  3. Create a new Dokku application
  4. Add a database service
  5. Expose the database for debugging and testing (Optional)
  6. Add a domain to the application
  7. Add any configuration variables for your application
  8. Add SSL to the application
  9. Add the Dokku remote to your application
  10. Push your application to Dokku
  11. Maintaining your Dokku server

1. Why Dokku?

I don’t have the time to worry about infrastructure for any personal projects and I don’t want to learn new tech for them. Dokku wraps up managing applications in a nice cli. DigitalOcean wraps up server management in a nice web UI and cli. It’s a perfect match!

For implementation I want monorepos and monoliths and relational databases because that’s what I know best. I’m not saying this is better than other options, it’s just the best use of my skill.

So if you also want to run monoliths, take a look at dokku!

2. Getting a Dokku instance

Digital ocean has a cheap droplet that you can use. So checkout Digital Ocean, create a new “Dokku” droplet and follow their instructions for setting up how to login to your new droplet. You can get a droplet for free using a referral link.

You will need to use SSL to connect to the DigitalOcean droplet. For this you’ll have to use an existing certificate or create a new certificate and upload the public key to the server using Digital Ocean’s UI.

# generate a new certificate
ssh-keygen -t ed25519 -C "your_email@example.com"
# when asked for a name call it "digitaloceandokku" or similar
# enter a pass phrase
# add the cert to ssh agent
ssh-add -K ~/.ssh/digitaloceandokku

Upload the public key to Digital Ocean.

Now add a config file to the ~/.ssh directory if you don’t already have one. You will need to add an entry here to have macos use keychain to store your passphrase and to give yourself a shortcut for connecting to your new server!

Host dokku-as-darragh
	HostName 123.123.123.123 # replace with your IP
	User root
	IdentityFile ~/.ssh/digitaloceandokku # replace with your certificate
        AddKeysToAgent yes
        PreferredAuthentications publickey
        UseKeychain yes
        IdentitiesOnly yes

Now you should be able to connect to your Digital Ocean droplet easily

ssh dokku-as-darragh

Done! you should see a prompt with a setup url to visit. Visit that and set up as you like. If you’re having issues with ssh you can read my article about open ssl configuration here.

I recommend adding a domain name to the Dokku instance and letting it use virtual hosts. With this configuration you will get a new url for each app e.g. my-app-name.mydomain.com.

Now you can start using dokku commands. E.g. dokku apps:list.

Note on frozen terminals and ssh

When you open an ssh connection it takes over your input so regular exit and escape commands don’t work. If you don’t use the ssh session it will be closed on the Digital Ocean side but not on your side. It appears as if the terminal has “frozen”.

You can terminate ssh with ~. and that will drop you back to your terminal.

3. Create the application

We’re going to add an application called house-lister to dokku.

dokku apps:create house-lister

4. Add a database service

If you haven’t used postgres on the instance yet, you must install the plugin

sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres

Now add a DB and link it to the new application

dokku postgres:create houselisterdatabase
dokku postgres:link houselisterdatabase house-lister

5. Expose the database for debugging and testing (Optional)

Dokku uses internal networking so your database is protected from attackers. If you want to connect to it for debugging purposes temporarily then you can expose it to the internet using the following.

dokku postgres:expose houselisterdatabase 5432

# un-expose the DB when done!
dokku postgres:unexpose houselisterdatabase

Be careful with that one. Leaving the database open all the time is unnecessary and risky.

5a. If you need Redis on Dokku

Use the same process as above but with the redis plugin

dokku redis:create houselisterredis
dokku redis:link houselisterredis house-lister

Now your application will have a redis instance available at REDIS_URL environment variable.

6. Add a domain to the application

You can add a domain name to make the application visible on the internet.

You’ll have to add a DNS record that points to the IP address of the DigitalOcean droplet. You can add a wildcard to make it easy or you can add specific addresses.

You can read more about how DNS and domains work here.

The commands for assigning the domain on Dokku are

# list existing domains
dokku domains:report house-lister

# add domains
dokku domains:add house-lister house-lister.mydomain.com

# remove any default domain you don't need - dokku allows you to have multiple domains on the instance
dokku domains:remove house-lister house-lister.anotherdomain.com

7. Add any configuration variables for your application

There may be some env vars you need to add for dokku plugins. See Let’s encrypt below for example.

Be careful with spaces in the dokku environment variable values. If you have a space in the value you will need to wrap the value in quotes.

dokku config:set house-lister APP_ENVIRONMENT_VAR_ONE=‘the value’ APP_ENVIRONMENT_VAR_TWO=‘the value’

You can avoid restarting the dokku application by using the --no-restart flag.

dokku config:set house-lister APP_ENVIRONMENT_VAR_ONE=‘the value’ APP_ENVIRONMENT_VAR_TWO=‘the value’ --no-restart

8. Add SSL to the application

This is optional but SSL is recommended these days and it’s free.

Dokku has a plugin for let’s encrypt. You have to install it if it’s not already there.

sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

There are env vars you need to configure so let’s encrypt knows your email address. The plugin docs are worth reading: https://github.com/dokku/dokku-letsencrypt

Don’t forget to set up a cron to update your certs automatically every 30 days or so.

# Enable SSL for the application
dokku letsencrypt:enable house-lister

9. Add the Dokku remote to your application

Dokku uses Git to deploy. You push your application to the dokku remote and that triggers a build.

Run this on your local computer while in the code for your application to create the new remote.

Note that it’s not the root user this time

Note this is NOT run on the Digital Ocean droplet - run this on your local computer in the application repository.

git remote add dokku dokku@dokku-as-darragh:house-lister

10. Push your application to Dokku

Run this on your local machine

git push dokku master:master

of if you use main or any other branch name

git push dokku main:master

Done! You should see the application being deployed successfully.

A note on cert errors

If you get this error when setting up a cert you might have tried to add it before setting a domain for the application.

nginx: [emerg] cannot load certificate "/home/dokku/house-lister/tls/server.crt": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/home/dokku/house-lister/tls/server.crt','r') error:2006D080:BIO routines:BIO_new_file:no such file)

This scenario sets the application networking in a bad state. You can clean things up with the following commands

# clean up
dokku proxy:ports-remove myapp 443
dokku domains:set myapp myapp.com.br www.myapp.com.br

# now add the cert again
dokku letsencrypt myapp

11. Maintaining your Dokku server

You’ll have to maintain the droplet yourself. As often as you remember login and apt update && apt upgrade.

You should also occasionally update your dokku instance.

sudo apt-get update
dokku apps:list
dokku ps:stop <app> # repeat to shut down each running app
sudo apt-get install -qq -y dokku herokuish
dokku ps:rebuild --all # restart all applications

Follow up dokku upgrades with plugin upgrades

dokku plugin:update postgres

Summary

Dokku makes it easy to run web apps for personal projects. The $5 per month is astounding value for what you get from Digital Ocean. You can get a droplet for free using a referral link.

Most of our personal projects have low enough usage that the droplet is plenty of power to run multiple apps.

Hit me up on Twitter if anything in the article isn’t working!

Darragh ORiordan

Hi! I'm Darragh ORiordan.

I live and work in Sydney, Australia building and supporting happy teams that create high quality software for the web.

I also make tools for busy developers! Do you have a new M1 Mac to setup? Have you ever spent a week getting your dev environment just right?

My Universal DevShell tooling will save you 30+ hours of configuring your Windows or Mac dev environment with all the best, modern shell and dev tools.

Get DevShell here: ✨ https://usemiller.dev/dev-shell


Read more articles like this one...

List of article summaries

#nodejs

npmrc authentication for a private scoped organisation package

When you have to login to npm for multiple organisations it can be easier to use an .npmrc file that you move around rather than npm login command.

#nodejs

Semantic versioning javascript projects but skipping NPM publish

If you want to use semantic versioning and automate release versions using semantic-release for your front-end client application you probably don’t want to actually publish it to npm.

Here is how to use semantic-release while not releasing to npm.

#nodejs

Avoid these issues when using new ECMAScript modules in your Node.js application

ECMAScript modules are the official standard format to package JavaScript code for reuse in the future. Es6 modules now have full support in Node.js 12 and above so it’s time to start using them.

JavaScript developers and node libraries have typically used commonjs for modules up to now. If you’ve used typescript in the past few years you will be familiar with the module import syntax in you application. Instead of commonjs require("module") most typescript applications use some variation of import module from "module".

Typescript will then transpile this import syntax into commonjs require statements for you. This step is not necessary in modern Node.js applications. You can just use import module from "module" directly in your transpiled code.

If you use typescript you can change just change your tsconfig settings to output ECMAScript es6 modules and you will be good to go. If you don’t use typescript you might have to do some rewriting if you want to get your app updated.

Here are solutions to the issues that took me a bit of time and investigation to figure out when I was upgrading my Node.js application to use ECMAScript modules like configuring typescript, setting up jest, configuring the package.json correctly and more.

Comments