Skip to content

How to Deploy Dokku on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Install Dokku on an Ubuntu VPS — a single-server, git-push PaaS with no web UI — then set a global domain, deploy a first app, add Let's Encrypt, and back up the directories that hold everything.

Before you start
  • A VPS with at least 1 GB RAM (more if apps build on the server)
  • A fresh Ubuntu 22.04, 24.04 or 26.04 server with root/sudo SSH access, dedicated to Dokku
  • An SSH key on your own computer, and git
  • A domain where you can create a wildcard A record (recommended)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Dokku is

Dokku is the smallest of the self-hosted platforms: a set of shell scripts and plugins, MIT-licensed, that turns one server into a Heroku-style PaaS. You git push an app to the server and Dokku builds it — with a Dockerfile if the repo has one, or by detecting the language with buildpacks — starts it in a container, and routes a hostname to it through nginx. Databases, Let's Encrypt and more come as official plugins.

There is no web UI. Everything is a dokku command, either on the server or over SSH from your laptop. That is the main thing to decide on: if you want a dashboard, CapRover, Dokploy or Coolify give you one, and Dokku vs CapRover compares the two approaches. If you like Heroku's CLI workflow, Dokku is the closest match.

Dokku installs as system packages and runs nginx on the host, owning ports 80 and 443. Give it a dedicated server.

Server sizing

Dokku's docs set the minimum at 1 GB of system memory with the default Docker scheduler, or less with swap added. Upstream notes that Dokku doesn't run daemons of its own; the memory goes to your apps and, above all, to builds. Buildpack builds of Node, Ruby or Java apps are the spikes that push a 1 GB box into swap, so:

  • 1 GB for a few small apps, ideally deployed from prebuilt images.
  • 2–4 GB if the server builds apps from source.
  • Disk: images and build caches accumulate; start with 40 GB+.

Dokku supports AMD64 and ARM64 servers.

Prepare the server

Dokku's installer installs Docker if it is missing. Open SSH and the web ports:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose

Your pushes go over SSH on port 22 to the dokku user, so there is nothing else to open.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Dokku

The official bootstrap script, pinned to a release (this is the version the docs listed when we wrote this; check the installation page for the current one):

cd ~
wget -NP . https://dokku.com/install/v0.38.31/bootstrap.sh
sudo DOKKU_TAG=v0.38.31 bash bootstrap.sh

On Debian and Ubuntu it installs Dokku as an apt package, along with its helpers. Upstream says it takes 5–10 minutes. Confirm it worked:

sudo dokku version
systemctl is-active nginx

SSH key and global domain

Dokku authorises pushes by SSH key. Your key is usually already in the admin user's authorized_keys, so add it to Dokku under the name admin. ssh-keys:add takes exactly one key, and some providers put comment lines in that file (Google Cloud adds # Added by Google), which it rejects as "not a valid ssh public key" — so feed it the first real key line only:

sudo dokku ssh-keys:list 2>/dev/null | grep -q 'admin' || \
  grep -v '^#' ~/.ssh/authorized_keys | head -n 1 | sudo dokku ssh-keys:add admin
sudo dokku ssh-keys:list

If you log in with several keys, add each one under its own name.

Now the global domain. With a wildcard A record (*.example.com pointing at the server), each app gets its own subdomain — myapp.example.com. With a single domain or a bare IP, apps are reached on random ports instead, which is much less pleasant. Set it (replace example.com with your domain):

sudo dokku domains:set-global example.com
sudo dokku domains:report --global

No domain yet? Upstream suggests SERVER_IP.sslip.io (your IP with .sslip.io on the end) as a stand-in that still gives every app a subdomain.

Deploy a first app

The fastest test deploys a prebuilt image with git:from-image, which builds the app as if its repo held only FROM <image>. traefik/whoami is a tiny web server that echoes the request:

sudo dokku apps:exists hello 2>/dev/null || sudo dokku apps:create hello
sudo dokku git:from-image hello traefik/whoami:latest
sudo dokku ps:report hello | grep -i "running"

Check nginx routes the hostname to it (this works before DNS exists, by sending the Host header by hand):

curl -s -H "Host: hello.example.com" http://127.0.0.1/ | head -3

You should see Hostname: and the container's details. For your own code, the usual workflow runs on your computer:

# on the server first: sudo dokku apps:create myapp
cd ~/code/myapp
git remote add dokku dokku@example.com:myapp
git push dokku main

Dokku detects a Dockerfile or picks a buildpack, builds, starts the app, and prints its URL.

HTTPS + domain

Let's Encrypt comes from the official dokku-letsencrypt plugin. Install it, set the contact email once, and add the renewal cron job:

sudo dokku plugin:installed letsencrypt || \
  sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
sudo dokku letsencrypt:set --global email you@example.com
sudo dokku letsencrypt:cron-job --add

Then, once hello.example.com resolves to the server and port 80 is reachable, enable it per app:

sudo dokku letsencrypt:enable hello
curl -sI https://hello.example.com/

Dokku rewrites the app's nginx config to serve HTTPS and redirect HTTP. The cron job renews certificates before they expire.

Databases and persistent data

Datastores are plugins too. For Postgres, the official plugin creates a database container and links it to an app, which sets DATABASE_URL:

sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git
sudo dokku postgres:create mydb
sudo dokku postgres:link mydb myapp

App containers are replaced on every deploy, so files an app writes need storage mounts. Upstream recommends app-specific directories under /var/lib/dokku/data/storage, mounted with dokku storage:mount, so the standard backup below includes them.

Securing it

  • Anyone with a key in dokku ssh-keys can deploy — and run commands. Keep that list short, and remove keys you no longer use with dokku ssh-keys:remove <name>.
  • Keep the OS patched. Dokku's upgrade docs recommend unattended security upgrades, and Docker updates when they are released.
  • Don't expose database ports. Linked datastores are reachable by the app over Docker's network; postgres:expose publishes one to the world, and Docker-published ports bypass ufw.

Backups

Dokku's documented backup is a tarball of the directories that hold its state, taken while no Dokku command or deploy is running:

export BACKUP_TIME=$(date +%Y-%m-%d-%H-%M)
sudo mkdir -p /var/lib/dokku/services
sudo chown dokku:dokku /var/lib/dokku/services
mkdir -p /tmp/dokku-backups/
sudo tar -czf "/tmp/dokku-backups/${BACKUP_TIME}.tar.gz" /home/dokku /var/lib/dokku/config /var/lib/dokku/data /var/lib/dokku/services /var/lib/dokku/plugins
ls -lh /tmp/dokku-backups/

Move it off the server. Two caveats from upstream: a file-level copy of /var/lib/dokku/services can catch a database mid-write, so also take logical dumps (dokku postgres:export mydb > mydb.dump); and apps deployed from images may need their original deploy command re-run after a restore. Restoring is sudo tar -xzf backup.tar.gz -C / on a fresh Dokku server, then starting each datastore and rebuilding the apps.

Upgrades

Dokku is an apt package, so upgrades are apt upgrades. Read the migration guide for every version between yours and the target first, and if Docker or herokuish are being upgraded too, stop the apps and rebuild afterwards:

sudo dokku ps:stop --all
sudo apt-get update
sudo apt-get --no-install-recommends install dokku herokuish sshcommand plugn gliderlabs-sigil dokku-update dokku-event-listener
sudo dokku ps:rebuild --all

Troubleshooting

git push asks for a password. Your key isn't in dokku ssh-keys:list, or you are pushing to the wrong user. The remote must be dokku@your-server:app, not your own username.

A build dies partway through. Usually out of memory on a small server. Check free -h and dmesg | tail; add swap, give the server more RAM, or build the image elsewhere and deploy it with git:from-image.

The app runs but its domain shows another app or a default page. The global domain or the app's domains are wrong. Check sudo dokku domains:report myapp and that the DNS record points here.

Let's Encrypt fails. The domain must resolve to this server and port 80 must be reachable; the plugin's output names the failing check.

Verification + next steps

You're done when: git push dokku main from your laptop deploys an app, it answers at https://myapp.example.com with a valid certificate, the renewal cron job is in place, and a backup tarball has been copied off the box.

From there, move apps over one at a time, link a datastore where needed, and remove the hello test app with sudo dokku apps:destroy hello. For hosting picks, see Best VPS for Docker and Best VPS for Node.js.

Next steps

How to self-host Dokku →More self-hosted platform-as-a-service tools →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.