Dokku is a small, open-source platform as a service that gives you Heroku-style git push deployments on a single server. It builds your app with Heroku buildpacks or a Dockerfile, runs it in Docker and routes traffic to it through Nginx. In this tutorial you will install Dokku on Ubuntu 24.04, deploy a Node.js sample app with git push, attach a PostgreSQL database through a plugin and secure the app with a Let's Encrypt certificate.

Prerequisites

To follow this tutorial you need:

  • A fresh server running Ubuntu 24.04 LTS with at least 1 GB of RAM, for example a CubePath VPS. Buildpack builds are memory hungry, so 2 GB is more comfortable if you plan to run several apps.
  • A non-root user with sudo privileges and an SSH key already authorized for that user.
  • A domain name with a wildcard A record *.your_domain pointing to your_server_ip, so each app gets its own subdomain such as myapp.your_domain. A single A record per app also works.
  • Git and an SSH key pair on your local workstation.

Step 1 - Installing Dokku

Dokku is installed with its bootstrap script, which adds the Dokku APT repository, installs Docker, Nginx and Dokku itself, and creates the dokku system user. The script is published per release, so first look up the latest release tag:

DOKKU_TAG=$(curl -fsSL https://api.github.com/repos/dokku/dokku/releases/latest | grep -oP '"tag_name": "\K[^"]+')
echo "$DOKKU_TAG"
v0.x.y

Download the bootstrap script for that release and review it:

wget -NP . "https://dokku.com/install/${DOKKU_TAG}/bootstrap.sh"
less bootstrap.sh

Run it:

sudo DOKKU_TAG="$DOKKU_TAG" bash bootstrap.sh

The installation takes a few minutes. Verify that Dokku responds:

dokku version
dokku version 0.x.y

Step 2 - Configuring SSH access and the global domain

Deployments happen over SSH as the dokku user, and Dokku only accepts keys you register explicitly. Since your own key is already authorized for your sudo user on the server, you can register the same key with Dokku:

cat ~/.ssh/authorized_keys | sudo dokku ssh-keys:add admin

If authorized_keys contains several keys, copy only the one you want to use into a file and pass that file instead. List the registered keys:

sudo dokku ssh-keys:list
SHA256:...  NAME="admin" SSHCOMMAND_ALLOWED_KEYS="none"

Now set the global domain. Every new app gets <app>.your_domain as its hostname:

sudo dokku domains:set-global your_domain

Check the setting:

sudo dokku domains:report --global
=====> Global domains information
       Domains global enabled:        true
       Domains global vhosts:         your_domain

Step 3 - Opening the firewall

Dokku's Nginx serves apps on ports 80 and 443. Allow them together with SSH:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
...

Step 4 - Creating an app and deploying with git push

Create the app on the server. The name becomes part of its URL:

sudo dokku apps:create myapp
-----> Creating myapp...

On your local workstation, clone Heroku's Node.js sample app. It includes a package.json and a Procfile, so the Node.js buildpack detects it automatically:

git clone https://github.com/heroku/node-js-getting-started.git
cd node-js-getting-started

Add your server as a Git remote. The user must be dokku, and the path is the app name:

git remote add dokku dokku@your_server_ip:myapp

Push the main branch to deploy:

git push dokku main

Dokku detects the language, builds the image, starts the container and prints the URL:

-----> Building myapp from herokuish
-----> Node.js app detected
...
=====> Application deployed:
       http://myapp.your_domain

Test it from your workstation:

curl -I http://myapp.your_domain
HTTP/1.1 200 OK
Server: nginx
...

To deploy a branch other than main, push it onto the deploy branch with git push dokku your_branch:main.

Step 5 - Managing the app

These commands run on the server. Stream the app logs:

sudo dokku logs myapp -t

See the running processes and scale them. Process types come from the Procfile:

sudo dokku ps:report myapp
sudo dokku ps:scale myapp web=2

Set environment variables. Dokku restarts the app to apply them:

sudo dokku config:set myapp NODE_ENV=production

List them to confirm:

sudo dokku config:show myapp
=====> myapp env vars
DOKKU_APP_TYPE:  herokuish
NODE_ENV:        production
...

Run a one-off command inside a new container of the app, for example to check the Node.js version:

sudo dokku run myapp node --version

Step 6 - Adding a PostgreSQL database

Databases are provided by official plugins. Install the PostgreSQL plugin:

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

Create a database service and link it to the app. Linking injects a DATABASE_URL environment variable and restarts the app:

sudo dokku postgres:create myapp-db
sudo dokku postgres:link myapp-db myapp

Verify that the variable is present:

sudo dokku config:get myapp DATABASE_URL
postgres://postgres:...@dokku-postgres-myapp-db:5432/myapp_db

The database is only reachable from the Docker network, not from the internet. To open a psql shell, use:

sudo dokku postgres:connect myapp-db

To take a backup, export the database to a file on the server:

sudo dokku postgres:export myapp-db > myapp-db.dump

The same pattern applies to the other official plugins, such as dokku-redis and dokku-mysql.

Step 7 - Enabling HTTPS with Let's Encrypt

Install the Let's Encrypt plugin and set the email address used for expiry notices:

sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
sudo dokku letsencrypt:set --global email you@your_domain

Request a certificate for the app. The app's domain must resolve to your server and port 80 must be reachable:

sudo dokku letsencrypt:enable myapp

Add a cron job so certificates renew automatically:

sudo dokku letsencrypt:cron-job --add

List certificates and their expiry:

sudo dokku letsencrypt:list

Check the app over HTTPS from your workstation:

curl -I https://myapp.your_domain
HTTP/2 200
server: nginx
...

To serve the app on another domain, create its DNS record, run sudo dokku domains:add myapp www.example.com and run letsencrypt:enable again so the certificate covers the new name.

Troubleshooting

git push fails with "Permission denied (publickey)". The key you push with is not registered with Dokku. Check sudo dokku ssh-keys:list on the server and test from your workstation with ssh dokku@your_server_ip version, which should print the Dokku version.

The build is killed or the server becomes unresponsive. The server ran out of memory during the build. Add a swap file:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

The app deploys but returns 502. The process is not listening on the expected port or crashes at startup. Check sudo dokku logs myapp and make sure the app binds to 0.0.0.0 on the port in $PORT.

Conclusion

You installed Dokku on Ubuntu 24.04, deployed an app with git push, linked a PostgreSQL database and enabled Let's Encrypt HTTPS with automatic renewal. From here you can schedule postgres:export backups with cron or a systemd timer, add Redis with the dokku-redis plugin, or add health checks with an app.json file so failed deploys never replace a working release.