Skip to content

0.38.0 Migration Guide

Changes

  • Dokku now generates a minimal nginx configuration for apps without running web processes (undeployed apps, apps with no web process type, or apps with stopped web processes). This configuration returns 502 Bad Gateway responses, ensuring the app's domain resolves and monitoring tools can detect non-200 status codes. The configuration is automatically replaced with the full proxy configuration once the app is deployed with running web processes. See the nginx documentation for more details.
  • Users with custom nginx.conf.sigil templates that reference DOKKU_APP_WEB_LISTENERS should be aware that this variable may now be empty when the template is rendered for apps without running web processes. Custom templates should handle this case gracefully, for example by using a conditional to serve an error page instead of proxying:

    location / {
    {{ if $.DOKKU_APP_WEB_LISTENERS }}
      proxy_pass  http://{{ $.APP }}-{{ $upstream_port }};
    {{ else }}
      return 502;
    {{ end }}
    }
    
  • The path on disk to both the global ENV file and app ENV files have been moved. Users should reference environment variables via the provided plugin triggers rather than directly sourcing the ENV files. Existing ENV files are left untouched and will be removed on the subsequent Dokku install.

  • During a fresh apt install, the upstream nginx default vhost files (/etc/nginx/sites-enabled/default, /etc/nginx/sites-available/default, and /etc/nginx/conf.d/default.conf) are renamed to ${path}.dokku-disabled (not deleted) to avoid a duplicate default server for 0.0.0.0:80 error. Operators with local customizations can recover them by inspecting the .dokku-disabled siblings. Upgrade-in-place installs do not touch any existing nginx files.
  • Fresh apt installs now ship a catch-all default site at /etc/nginx/conf.d/00-default-vhost.conf that rejects requests with unknown Host headers using ssl_reject_handshake on (HTTPS) and return 444 (HTTP). This replaces the manual workaround previously documented in the nginx docs. The behavior can be opted out at install time via the dokku/install_default_site debconf prompt. See the Default site documentation.
  • The docker-local scheduler now sends SIGTERM to old containers immediately after a successful deploy, rather than waiting wait-to-retire seconds before signaling. This matches Heroku's graceful-shutdown contract and lets applications begin draining in-flight work as soon as proxy traffic switches. The wait-to-retire grace period and stop-timeout-seconds hard-stop continue to apply as before. See the zero downtime deploys documentation for more details.
  • The docker-local scheduler no longer queues an image for retirement when another running container of the same app still uses it. This fixes the case where a ps:rebuild against an image-based deploy (git:from-image) produced an identical-SHA image and the dokku-retire cron timer would log Image ... has running containers, skipping rm on every run. Stuck entries from prior versions are pruned automatically on the next ps:retire run.
  • All :report subcommands now accept the --global flag, which scopes the report to globally-configured properties. The flag composes with --format json, so a JSON report of global properties can be obtained via, for example, dokku scheduler:report --global --format json. Previously, combining --global with --format json was rejected with an "info flag" error, and --global on its own was treated as an unknown flag.
  • The scheduler-k3s plugin now manages env config and the dokku-generated image pull Secret as their own helm releases with stable names (config-{app} and pull-secret-{app}) rather than bundling them into the app helm chart with a per-deploy timestamp suffix (env-{app}.{ts} / ims-{app}.{ts}). This fixes two bugs: a helm rollback of the app chart no longer deletes Secrets that older ReplicaSets still reference, and the Deployment's imagePullSecrets list no longer accumulates references to nonexistent Secrets across deploys. The next deploy of an app switches the Deployment's envFrom and imagePullSecrets references to the stable names and prunes any leaked entries; existing live Deployments do not need to be patched manually. App rename now also uninstalls the old tls-{app}, config-{app}, and pull-secret-{app} releases under the previous app name; the new name's releases are recreated on the next deploy or certs sync.
  • The storage plugin now treats persistent volumes as named, scheduler-aware first-class resources via storage:create, storage:mount, storage:set, and storage:destroy. The legacy storage:mount <app> <host>:<container> colon form continues to work on docker-local apps but is deprecated; on k3s apps it is rejected. Existing colon-form mounts are migrated automatically the first time the new storage plugin runs (during the install trigger) - they appear as legacy-<hash> entries in storage:list-entries. The migration is idempotent and tied to a per-app flag file at $DOKKU_LIB_ROOT/config/storage/.migrated/<app>; deleting that file forces a re-scan on the next install. The storage:ensure-directory command keeps working but now emits a deprecation warning - prefer storage:create <name> [<path>] (the path defaults to the same $DOKKU_LIB_ROOT/data/storage/<name> location). Storage entry names must now be DNS-1123 labels of 45 characters or less so they can be used verbatim as Helm release and Kubernetes resource names; underscores and uppercase characters that the older ensure-directory validator accepted are rejected for new names. The migration synthesizer always uses lowercase hex hashes so existing data is never locked out.

TLS handshake behavior change

With the new catch-all installed, an HTTPS request to a hostname that matches a configured dokku app but where the app has no TLS certificate configured will have its TLS handshake rejected by the catch-all (via ssl_reject_handshake on). Previously, nginx fell through to the lexicographically first port-443 server block and presented that block's certificate, producing a cert-mismatch error on the client. The new behavior is a correctness improvement, but operators who deliberately relied on the old fall-through certificate (for monitoring probes, for example) need to either configure a certificate for the target app or remove the catch-all on that host. Existing apps that already have certificates configured are unaffected: nginx selects the right server block via SNI before TLS completion, so the catch-all is never consulted for legitimate requests.

Environment variables migrated to plugin properties

A number of DOKKU_* config environment variables have been replaced with properly-namespaced plugin properties. Existing values are migrated automatically the first time dokku runs after the upgrade, and the original config variable is unset. No manual action is required.

Going forward, configure these settings using the property commands listed below. Setting the deprecated env vars via dokku config:set will no longer have any effect.

Deprecated Env Var Replacement Command
DOKKU_APP_PROXY_TYPE dokku proxy:set <app> type <value>
DOKKU_APP_RESTORE dokku ps:set <app> restore <true\|false>
DOKKU_APP_SHELL dokku scheduler:set <app> shell <value>
DOKKU_CHECKS_DISABLED dokku checks:disable <app> [proctypes]
DOKKU_CHECKS_ENABLED dokku checks:enable <app> [proctypes]
DOKKU_CHECKS_SKIPPED dokku checks:skip <app> [proctypes]
DOKKU_CHECKS_WAIT dokku checks:set <app> wait <value>
DOKKU_CHECKS_TIMEOUT dokku checks:set <app> timeout <value>
DOKKU_CHECKS_ATTEMPTS dokku checks:set <app> attempts <value>
DOKKU_DEFAULT_CHECKS_WAIT dokku checks:set --global default-wait <value>
DOKKU_DISABLE_APP_AUTOCREATION dokku apps:set --global disable-autocreation <true\|false>
DOKKU_DISABLE_PROXY dokku proxy:disable <app> / dokku proxy:enable <app>
DOKKU_DOCKERFILE_START_CMD dokku ps:set <app> dockerfile-start-cmd <value>
DOKKU_PROXY_PORT dokku proxy:set <app> proxy-port <value>
DOKKU_PROXY_SSL_PORT dokku proxy:set <app> proxy-ssl-port <value>
DOKKU_SKIP_ALL_CHECKS dokku checks:disable <app>
DOKKU_SKIP_CLEANUP dokku builder:set <app> skip-cleanup <true\|false>
DOKKU_SKIP_DEFAULT_CHECKS dokku checks:skip <app>
DOKKU_SKIP_DEPLOY dokku ps:set <app> skip-deploy <true\|false>
DOKKU_START_CMD dokku ps:set <app> start-cmd <value>

DOKKU_PARALLEL_ARGUMENTS is removed entirely; it has no replacement.

DOKKU_SKIP_CLEANUP continues to be honored when set in /etc/environment or ~dokku/.dokkurc/* so that bootstrap-time configuration keeps working, but the builder skip-cleanup property is the canonical interface and takes precedence when set.