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
sudoprivileges and an SSH key already authorized for that user. - A domain name with a wildcard A record
*.your_domainpointing toyour_server_ip, so each app gets its own subdomain such asmyapp.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.
TipIf your repository contains a
Dockerfileat its root and no buildpack files, Dokku builds it with Docker instead of buildpacks. Buildpack apps must listen on the port in thePORTenvironment variable, which Dokku sets for you.
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.
