Application requirements
Everything an application must satisfy to be deployable on this PaaS. If you know the twelve-factor app model this will look familiar — the platform is Heroku-style and enforces roughly the same contract.
1. A valid app name
App names must match ^[a-z0-9][a-z0-9.-]*$:
- start with a lowercase letter or digit
- then only lowercase letters, digits, dots, and hyphens
- no underscores, no uppercase — the platform rejects them at app-creation time
Good: my-first-app, api.v2, web1. Bad: my_app, MyApp, -web.
2. Listen on the port the platform assigns
The platform injects a PORT environment variable into your app's
container and routes HTTP traffic to it. Your web process must:
- read
PORTfrom the environment and bind to it (0.0.0.0, all interfaces — not127.0.0.1) - defaulting to
5000whenPORTis unset is the convention and makes the app runnable locally as-is
An app that listens on a hardcoded port, or only on loopback, will
build successfully and then fail every request (502) at its URL.
3. Speak plain HTTP — TLS is the platform's job
Your app always serves plain HTTP on its assigned port. When HTTPS is
enabled for your app, TLS terminates at the platform edge (nginx with a
Let's Encrypt certificate) and your app continues to see HTTP. Don't
run your own TLS listener, and don't hardcode https:// self-redirects
that break the plain-HTTP health and routing path.
4. Stateless processes
Every deploy, config change, or datastore link replaces your app's container. The local filesystem inside the container is ephemeral — anything written to disk vanishes on the next deploy or restart.
Therefore:
- keep no state in the local filesystem (no SQLite files, no uploaded files, no session stores on disk)
- use Postgres for structured data, Redis for cache/sessions/queues (see Datastores below)
- treat each process as disposable: it must boot cleanly from scratch on every deploy
5. Configuration via environment variables
All environment-specific values — database URLs, API keys, feature flags — come from the environment, never from files committed to the repo:
ssh dokku@54.170.127.122 config:set my-first-app GREETING="Hello" API_KEY=...
Setting, changing, or unsetting config restarts the app so the new
values take effect. Two variables every app sees, set globally by the
platform: CURL_CONNECT_TIMEOUT=90 and CURL_TIMEOUT=600 (they tune
the platform's own outbound HTTP calls; harmless to your code).
6. Self-contained, buildable source
What you push is what gets built — the platform runs no pre-steps. Your repository must be deployable as-is from its root, using one of two build conventions:
A. Heroku-compatible buildpack (recommended for supported runtimes). The platform uses the herokuish buildpack runtime, the same technology as Heroku. Include:
- a
Procfileat the repo root declaring the web process, e.g.web: node server.js— declare it explicitly and never rely on buildpack defaults - the runtime's manifest so the right buildpack is detected:
package.json(Node.js),requirements.txtorPipfile(Python),Gemfile(Ruby),go.mod(Go),pom.xml/build.gradle(Java),composer.json(PHP), …
The build host is x86_64; buildpacks fetch and compile your
dependencies at push time.
B. Dockerfile. A Dockerfile at the repo root takes over the build:
the platform builds the image itself instead of using a buildpack. Use
this for anything the buildpacks don't cover. The container must still
obey requirements 2–5 (listen on $PORT, stay stateless, config from
the environment).
7. Push to the deploy branch
The platform deploys only what lands on its deploy branch, which is
master by default. If your local branch is main (or anything else),
push it across:
git push dokku main:master
Pushing to another branch is accepted by git but deploys nothing — the push output warns you when that happens.
8. Boot and stay up (health)
Deploys are zero-downtime: the platform starts the new container and verifies it before switching traffic. With no extra configuration the check is simple: the container must still be running 10 seconds after start. Old containers get a 60-second grace period to finish in-flight requests before they're retired.
Requirements that follow from this:
- your app must boot and bind its port quickly (seconds, not minutes)
- a crash on boot fails the deploy instead of taking the old version down — fix and re-push
Recommended: expose a health endpoint such as /health returning
HTTP 200 with a small JSON body. It gives operators (and you) a cheap
liveness signal, and it can be enforced at deploy time by adding a
healthchecks section to an app.json at the repo root:
{
"healthchecks": {
"web": [
{ "type": "startup", "path": "/health", "attempts": 3 }
]
}
}
With that in place, the platform curls /health (on your app's port)
during every deploy and only switches traffic once it answers 200.
9. Log to stdout and stderr
Write logs to stdout/stderr, unbuffered. The platform captures them; you read them with:
ssh dokku@54.170.127.122 logs my-first-app # recent logs
ssh dokku@54.170.127.122 logs my-first-app -t # stream live
Don't write log files inside the container (they're ephemeral and invisible to the platform) and don't run logging daemons — one process per container, logging to the console.
Datastores (optional but managed)
Two datastore services run on the platform, ready to link to any app:
| Service | Kind | Version | Link command (injects) |
|---|---|---|---|
demo-db |
Postgres | 18.6 | ssh dokku@54.170.127.122 postgres:link demo-db <app> → DATABASE_URL |
demo-cache |
Redis | 8.10.2 | ssh dokku@54.170.127.122 redis:link demo-cache <app> → REDIS_URL |
Linking restarts the app and the connection URL is then in its environment — parse it with your language's usual URL-parsing/database library. Both services are dumped to off-host storage every night, but their data is shared across all linked apps — use your own schema / key prefix per app.
Pre-deploy checklist
- App name matches
^[a-z0-9][a-z0-9.-]*$ - Web process binds
0.0.0.0on$PORT(default 5000 locally) -
Procfilewith aweb:process + runtime manifest — or a rootDockerfile - No state on the local filesystem; Postgres/Redis used for data
- All config and secrets come from environment variables
- Local branch pushed as
git push dokku <branch>:master - Boots in seconds and stays running;
/healthreturns 200 - Logs go to stdout/stderr
Next: Deploy your first app, step by step — or back to Getting started.