📚 Docs

Deploy your first app, step by step

From an empty directory to a live URL on the PaaS. We build a tiny Node.js service — the same shape as the sample apps that ship with the platform — then configure it, give it a database, and finish with logs and HTTPS. No prior platform knowledge needed; every step says what it does and what you should see.

Prerequisites

Step 0 — Set up the deploy key

chmod 600 ~/.ssh/dokku-deploy.pem

Optional but recommended — teach SSH about the key once:

cat >> ~/.ssh/config <<'EOF'
Host dokku-paas
  HostName 54.170.127.122
  User dokku
  IdentityFile ~/.ssh/dokku-deploy.pem
EOF

The rest of this guide uses the dokku-paas alias. Without it, add -i ~/.ssh/dokku-deploy.pem to every ssh command and use dokku@54.170.127.122 instead of dokku-paas in the git remote.

Step 1 — Scaffold the app

mkdir my-first-app && cd my-first-app

server.js — a dependency-free HTTP service with a /health endpoint. Note the two platform contracts: it reads PORT from the environment and it reads its config (GREETING) from the environment too.

'use strict';

const http = require('http');

const APP_NAME = 'my-first-app';
const PORT = process.env.PORT || 5000;
const GREETING = process.env.GREETING || 'Hello from the PaaS!';

const server = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ status: 'ok', app: APP_NAME }) + '\n');
    return;
  }
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(
    JSON.stringify({
      app: APP_NAME,
      message: GREETING,
      database: process.env.DATABASE_URL ? 'linked' : 'not linked',
    }) + '\n'
  );
});

server.listen(PORT, () => {
  console.log(`${APP_NAME} listening on port ${PORT}`);
});

package.json — the manifest that tells the platform this is a Node.js app:

{
  "name": "my-first-app",
  "version": "1.0.0",
  "private": true,
  "main": "server.js",
  "scripts": { "start": "node server.js" }
}

Procfile — declares the web process the platform runs:

web: node server.js

.gitignore:

node_modules/

Step 2 — Test it locally

node server.js &          # starts on port 5000
curl http://localhost:5000/
curl http://localhost:5000/health
kill %1

You should see the JSON greeting and {"status":"ok","app":"my-first-app"}. If it works locally, it satisfies the platform's build requirements.

Step 3 — Commit it

git init -b main
git add -A
git commit -m "Initial commit"

Step 4 — Create the app on the platform

ssh dokku-paas apps:create my-first-app

The name must match ^[a-z0-9][a-z0-9.-]*$ (lowercase, digits, dots, hyphens — no underscores). Creating the app reserves the name and its URL; nothing runs yet.

Step 5 — Push and deploy

git remote add dokku dokku-paas:my-first-app
git push dokku main:master

Why main:master? The platform deploys only its master branch. Your local branch is main, so push it across. If you forget the :master, the push succeeds but nothing deploys (the output warns you).

Watch the output — it is the whole deploy pipeline:

  1. -----> Cleaning up / -----> Building — the Node.js buildpack is detected and your app is built into a container image
  2. -----> Releasing — the new container starts
  3. -----> Deploying — zero-downtime checks: the platform confirms the new container is up before switching traffic to it
  4. -----> Deployed! followed by the app's URL

Step 6 — Verify it's live

curl https://my-first-app.54.170.127.122.sslip.io/
curl https://my-first-app.54.170.127.122.sslip.io/health

Same JSON as locally, now served by the platform. DNS for *.54.170.127.122.sslip.io resolves automatically (wildcard DNS), so the URL works the moment the first deploy finishes. If you get a TLS error instead, the app has no certificate yet — step 10 enables it with one operator command; until then these same curls work over http://.

Step 7 — Change configuration without touching code

ssh dokku-paas config:set my-first-app GREETING="Hello, configured world"
curl https://my-first-app.54.170.127.122.sslip.io/

config:set restarts the app with the new environment — the greeting changes with no commit and no push. Inspect everything set on the app with ssh dokku-paas config:show my-first-app (values are shown; treat the output as sensitive).

Step 8 — Give it a database

ssh dokku-paas postgres:link demo-db my-first-app
curl https://my-first-app.54.170.127.122.sslip.io/

Linking injects DATABASE_URL into the app's environment and restarts it — the response's database field flips to linked. The same works for Redis: ssh dokku-paas redis:link demo-cache my-first-app injects REDIS_URL. Unlink with postgres:unlink demo-db my-first-app.

Your code never hardcodes a host, port, or password: it parses DATABASE_URL, so the same artifact runs anywhere.

Step 9 — Read its logs and manage the process

ssh dokku-paas logs my-first-app          # recent logs
ssh dokku-paas logs my-first-app -t       # stream live (Ctrl-C to stop)
ssh dokku-paas ps:report my-first-app     # process state
ssh dokku-paas ps:restart my-first-app    # restart without a deploy

Everything the app prints to stdout/stderr shows up in logs.

Step 10 — Turn on HTTPS (operator step)

HTTPS uses a free Let's Encrypt certificate, enabled per app by a platform operator (the platform's pre-installed apps already have it):

sudo dokku letsencrypt:enable my-first-app    # run on the host

From then on the app answers at https://my-first-app.54.170.127.122.sslip.io and the certificate renews itself automatically (the platform runs a renewal job daily). Your app changed nothing — it still serves plain HTTP internally; TLS terminates at the platform edge.

Step 11 — Ship a change

# edit server.js, then:
git add -A && git commit -m "Change the greeting"
git push dokku main:master

Each push is a zero-downtime deploy: the new version is built and verified before traffic switches, and the old version drains for 60 seconds before retiring. If the new version crashes on boot, the deploy fails and the old version keeps serving.

Troubleshooting

Symptom Likely cause Fix
Please specify a valid app name / create fails uppercase, underscores, or a leading dot/hyphen in the name rename to match ^[a-z0-9][a-z0-9.-]*$
Push succeeds but the site never changes pushed to main, not the deploy branch git push dokku main:master
Push asks for a password / Permission denied (publickey) deploy key not used ssh-add ~/.ssh/dokku-deploy.pem, the ~/.ssh/config alias from step 0, or GIT_SSH_COMMAND="ssh -i ~/.ssh/dokku-deploy.pem" git push …
ssh/push times out your network isn't allowlisted on the platform firewall ask your operator to add your CIDR
URL returns 502 Bad Gateway app crashed or isn't listening on $PORT / bound to 127.0.0.1 ssh dokku-paas logs my-first-app; fix the bind to 0.0.0.0:$PORT
Deploy fails at the checks step app exits within ~10 s of boot (crashes on start) read the push output and logs; fix the boot crash and re-push
App can't reach Postgres/Redis service not linked postgres:link demo-db my-first-app / redis:link demo-cache my-first-app

Where to go next