1134 Commits

Author SHA1 Message Date
Jose Diaz-Gonzalez
96d726c43c fix: retire cron containers past their active deadline
Cron containers were never reaped once they exceeded their active deadline, so a hung cron task ran indefinitely instead of being retired after 24 hours as documented. `cron:run` now also accepts a `--ttl-seconds` argument, matching the one `dokku run` already takes.
2026-08-12 13:11:34 -04:00
Jose Diaz-Gonzalez
81a929d768 fix: report traefik dns-provider env vars as global keys
The dynamic `dns-provider-*` properties were reported as `--traefik-dns-provider-<env_var>`, outside the `global`/`computed` namespace every other traefik property uses, so a consumer reading the json report could not tell which of the two a key represented. They are now reported as `--traefik-global-dns-provider-<env_var>`. The `basic-auth-password` property is a credential as well and is now masked in the default report output on the same terms as the dns provider values, meaning the raw value is returned only for `--format json` or when the flag is queried by name.
2026-08-12 03:22:04 -04:00
Jose Diaz-Gonzalez
25a5bacde9 docs: note the preserved ENV file can be deleted
The preserved copy holds everything the legacy file was not allowed to import, which includes keys that had been `config:unset` on purpose, so a revoked secret can outlive its revocation in a file `dokku config:*` no longer reads. Nothing said the copy was the operator's to remove once reviewed.
2026-08-11 19:11:06 -04:00
Jose Diaz-Gonzalez
685f16e6b1 fix: do not import an already-migrated ENV file
Releases 0.38.0 through 0.38.25 recorded the ENV file migration and deliberately left the file at the old path, so 0.38.26 treating a leftover file as a hand-edit replayed the environment as it stood at that upgrade over every `config:set` and `config:unset` made since, reinstating variables and secrets that had been deliberately unset. A file found at the old path once its migration is on record is no longer imported: it is removed when it agrees with the current config, and is otherwise moved aside to `ENV.migrated` with the keys it disagrees on named in a warning. That warning now reaches the operator during an upgrade rather than being swallowed, which is why the overwrite went unreported.
2026-08-10 17:58:10 -04:00
Jose Diaz-Gonzalez
2d34caec00 Merge pull request #8920 from dokku/8913-storage-host-directories-have-no-mode-flag-and-no-removal-path
Add storage directory mode and removal flags
2026-08-10 17:22:31 -04:00
Jose Diaz-Gonzalez
74f9acf7ba refactor: align storage:set with dokku conventions
`storage:set` now takes `<name> <property> [<value>]` like every other `:set` command, where omitting the value unsets the property. Previously it took flags and could not distinguish an empty value from an omitted one, so nothing it set could ever be cleared. The flag form keeps working and emits a deprecation warning.

Annotations and labels move to `storage:annotations:set`, `storage:annotations:report`, `storage:labels:set`, and `storage:labels:report`, matching the `scheduler-k3s` equivalents. These operate on a single key, so clearing one leaves the rest in place rather than replacing the whole map as the `--annotation` and `--label` flags do.
2026-08-10 15:00:45 -04:00
Jose Diaz-Gonzalez
87b240054f feat: add storage directory mode and removal flags
`storage:create` and `storage:set` accept a `--mode` flag that sets the octal permissions of a docker-local host directory, and `storage:destroy` accepts a `--destroy-host-dir` flag that removes the directory along with its contents. A docker-local entry also honors `--reclaim-policy Delete` at destroy time now, matching how that policy governs a k3s PersistentVolume. Both are limited to the default `/var/lib/dokku/data/storage/<name>` location, the same restriction `--chown` already carries. `storage:set` applies `--chown` and `--mode` to the directory rather than only recording them.
2026-08-10 01:23:10 -04:00
Jose Diaz-Gonzalez
b43929df27 fix: regenerate vector config on app lifecycle changes
The generated vector config is a snapshot of the app list and their sink properties, but it was only ever written by `logs:set` and `logs:vector-start`. Renaming an app left a source filtering on a label no container carries and gave the new name no source at all, so the app kept a sink with nothing feeding it. Cloning produced the same result for the clone, and destroying an app left its source and sink behind, the latter still pointing at an endpoint decommissioned along with the app. The global relabel transform embeds app names directly in generated VRL, so a rename also left behind a branch naming an app that no longer existed. Every case was silent, and the only repair was an operator running `logs:vector-start`. The `post-app-clone-setup`, `post-app-rename-setup` and `post-delete` triggers now rewrite the config, warning rather than failing so that a config write cannot abort the app operation whose state it is derived from.

Closes #8918.
2026-08-09 23:53:12 -04:00
Jose Diaz-Gonzalez
5b5d3d761b fix: apply app-label-alias to shipped events
The alias was only ever used to build the `include_labels` filter on the generated vector source, while dokku labels containers with `com.dokku.app-name` unconditionally. Setting the property therefore pointed the source at a label no container carries, and log collection stopped without any error. The source now always filters the label dokku applies, and a generated remap renames the field on its way to the sink, which is what the property was documented to do. An app whose own alias differs from the global one gets a branch in the global pipeline, so a per-app value is honored even when the app ships through the global sink.

Closes #8916.
2026-08-09 14:01:43 -04:00
Jose Diaz-Gonzalez
4a1bdc9bdf style: satisfy shfmt pipeline continuation formatting 2026-08-09 02:26:58 -04:00
Jose Diaz-Gonzalez
3d02d81562 fix: stop logs tests leaking a global app-label-alias
The `app-label-alias` test left the global property set, so every later test in the file generated a vector source filtering on a label that dokku never applies to a container, silently collecting nothing. Clearing it in teardown restores log collection for the rest of the file. The cron routing test now asserts against console sinks rather than files, since the sink an event reached is identifiable from vector's own output without depending on a writable host mount, and its task sleeps either side of its output because a cron container that exits immediately is removed before vector can attach to it.
2026-08-09 02:22:13 -04:00
Jose Diaz-Gonzalez
52b26a3760 feat: add vector-cron-sink for scheduled cron task output
Scheduled cron task output previously reached only the `dokku` user's cron mail, and could not be redirected because `app.json` rejects bare shell operators in a cron `command`. Setting `vector-cron-sink` on an app or globally routes that output to a dedicated sink instead, on both the `docker-local` and `k3s` schedulers, which keeps log destinations under operator control rather than in a deployed repository. Cron events carry `dokku_app` and `dokku_cron_id` fields so a sink can give each task its own destination. This also fixes a `k3s` bug where configuring a global `vector-sink` silently removed the vector prometheus exporter sink.
2026-08-09 01:10:21 -04:00
Jose Diaz-Gonzalez
bb7335f88e feat: route wildcard domains through traefik on k3s
Traefik matches hosts exactly, so an app serving a wildcard domain under the `traefik` ingress class had a valid certificate but silently 404d on every request. Wildcard domains now render as a `HostRegexp` rule that matches a single label, the same semantics as a Kubernetes wildcard host, so both ingress classes behave the same. Those routes carry an explicit low priority so an exact domain on any app still wins over another app's wildcard, mirroring ingress-nginx.
2026-08-08 19:15:10 -04:00
Jose Diaz-Gonzalez
d134e75371 feat: support manually managed cert issuers on k3s
The `cert-issuer-name` and `cert-issuer-kind` properties point an app's generated `Certificate` at a cert-manager issuer created outside of Dokku, allowing certificates to be issued through solvers the built-in letsencrypt integration cannot use, such as `dns01` for wildcard certificates. Setting an issuer enables https on its own, as a manually managed issuer has no email for Dokku to configure. An imported certificate still takes precedence, and `letsencrypt-server false` remains the single off switch. Dokku warns before a build starts when the referenced issuer is absent from the cluster, without blocking the deploy. Wildcard domains no longer collide with their apex domain when generating ingress names, and `letsencrypt-server` values are now validated when set rather than at deploy time.
2026-08-08 15:16:12 -04:00
Jose Diaz-Gonzalez
66bcfbd1ed fix: document --global on scheduler-k3s report and set
The usage strings for `scheduler-k3s:report` and `scheduler-k3s:set` omitted the `--global` option, which is the only way to reach the scheduler-wide report since a bare `scheduler-k3s:report` iterates every app, and `:report` also omitted `--format stdout|json`. The command listing in the k3s documentation is resynced with the help output, which additionally restores flags that had been dropped from `scheduler-k3s:cluster:add`, `scheduler-k3s:cluster:list`, and `scheduler-k3s:initialize`.
2026-08-08 01:43:40 -04:00
Jose Diaz-Gonzalez
e24859bfe6 test: stage the migration marker through the trigger
The hand-written marker file was created as root, which the config-migrate-env
trigger could not overwrite when it ran under a different user. Draining once up
front records the migration through the same code path the assertion exercises.
2026-08-07 17:56:15 -04:00
Jose Diaz-Gonzalez
70dc921967 fix: migrate env files before reading deprecated vars
Install steps run in alphabetical order of the enabled plugin directory, so `apps`, `builder`, and `checks` read an app's environment before the `config` plugin had moved the `ENV` file to its new location. The read came back empty, so their deprecated `DOKKU_*` variables were never migrated to the matching plugin property and were never unset, with nothing reported either way: `dokku config:show` kept listing the variable while the plugin behaved as though it were unset. The relocation now runs before any deprecated variable is read, whatever the install order, and each old file is removed as soon as it has been drained rather than on a later install, which also covers the global file that was never removed at all. A file that reappears at the old path can only have been written by hand, so it is merged in with a warning naming its keys instead of being discarded.
2026-08-07 16:43:56 -04:00
Jose Diaz-Gonzalez
8d4e7d9793 fix: match docker options by shell word when removing
Options drained out of the pre-0.38.0 `DOCKER_OPTIONS_<PHASE>` files were copied verbatim rather than re-serialized the way `docker-options:add` stores them, so `docker-options:remove` compared the canonical string it builds against a stored value that could never match it and exited successfully without removing anything. Removal now matches stored options by shell word, and stored options are rewritten into the canonical form once on upgrade, which additionally splits an entry that carried several flags on a single line into one entry per flag so a single flag can be removed and so the readers that match on a flag prefix see one value per entry. The leftover `.migrated` sentinel drain is restored to running ahead of the global short-circuit that had made it unreachable, and the plugin's Go tests are added to the test target that had never run them.
2026-08-07 13:35:37 -04:00
Jose Diaz-Gonzalez
da085c5ebb Merge branch 'master' into 8862-move-host-crontab-generation-into-the-cron-plugin 2026-08-07 12:36:32 -04:00
Jose Diaz-Gonzalez
44cd566178 feat: manage node-level kernel sysctls on the k3s scheduler
Sysctls the kernel does not namespace, such as `vm.max_map_count`, cannot be set from a pod spec and previously had no answer beyond editing `/etc/sysctl.d` on each host by hand. `scheduler-k3s:node-sysctls:set` now applies them through a privileged daemonset, which reaches nodes joined later and reapplies after a reboot. Sysctls may be scoped to a node profile, with a profile scope inheriting the global values and overriding them on conflict so that every node is covered by exactly one daemonset. Clearing a sysctl stops dokku managing it but does not restore the previous value, which persists until the node reboots.
2026-08-07 09:10:00 -04:00
Jose Diaz-Gonzalez
cd1089500b feat: translate docker-options --sysctl on the k3s scheduler
The `docker-local` scheduler supports `--sysctl` for free because docker options are passed verbatim to `docker run`, but the k3s scheduler silently dropped it. Namespaced sysctls now render into the pod's `securityContext.sysctls` for deployments, cron jobs, and one-off runs. A sysctl the kernel does not namespace fails the deploy instead of being dropped, since it cannot take effect within a pod regardless of what was requested.
2026-08-07 05:43:00 -04:00
Jose Diaz-Gonzalez
8e17eee653 feat: add --kubelet-args to scheduler-k3s:initialize
The server node created by `scheduler-k3s:initialize` had no way to receive kubelet arguments, unlike nodes joined through `scheduler-k3s:cluster:add` or configured via `scheduler-k3s:profiles:add`. This meant settings such as `allowed-unsafe-sysctls` were unreachable on a single-node install.
2026-08-07 02:31:12 -04:00
Jose Diaz-Gonzalez
c6b4ea75c1 refactor: move host-crontab generation into cron plugin
Host-crontab generation for `app.json` cron tasks now lives in the `cron` plugin, gated by a new `scheduler-uses-host-cron` trigger that the `docker-local` scheduler answers true while self-managed schedulers such as `k3s` answer false. This lets any host-cron scheduler participate in normal `app.json` cron without coupling to `scheduler-docker-local` or duplicating the crontab writer, while the `k3s` scheduler continues to manage its own in-cluster cron jobs.

Closes #8862.
2026-07-22 08:35:11 -04:00
Jose Diaz-Gonzalez
9c61031f3e docs: add docs and tests for this feature 2026-07-22 03:45:32 -04:00
Jose Diaz-Gonzalez
dc802ddd19 feat: support per-app letsencrypt emails on k3s
The `letsencrypt-email-prod` and `letsencrypt-email-stag` properties can now be set per app in addition to globally, resolving app-level before the global value for the app's selected `letsencrypt-server`. An app that sets its own email renders a namespaced cert-manager `Issuer` using that email, while apps without an override continue to use the shared `ClusterIssuer` with the global email.
2026-07-19 03:31:55 -04:00
Jose Diaz-Gonzalez
1a376c3622 fix: prevent command injection via docker options eval
Values supplied through docker options, `--ttl-seconds`, and `-e` flowed into a Bash `eval` during build, deploy, and run, letting a low-privileged user execute arbitrary commands on the host as the dokku user. These arguments are now tokenized and passed through to the container verbatim, without shell expansion. A one-time migration repairs stored labels whose backticks were saved with a stray backslash so Traefik-style rules stay valid on the next deploy.
2026-07-19 01:42:12 -04:00
Jose Diaz-Gonzalez
5496029e07 fix: parse cert CN and subject on OpenSSL 3.x
The `certs` plugin extracted a certificate's Common Name and formatted its subject using string assumptions that only held for pre-3.x OpenSSL output, so a certificate with only a Common Name and no Subject Alternative Name reported no hostnames from `certs:report` and was not recognized during nginx config generation, while the subject report retained the `subject=` prefix and used the wrong separators. Normalizing the subject with `-nameopt` before parsing makes the extraction version independent across OpenSSL and LibreSSL.
2026-07-10 15:05:27 -04:00
Jose Diaz-Gonzalez
611cb89711 Merge pull request #8805 from RichardDorian/master
Allow custom values for chown
2026-07-08 18:09:31 -04:00
Jose Diaz-Gonzalez
c37ba0f255 Merge pull request #8810 from dokku/8797-network-list-expose-whether-a-network-was-created-by-dokku
Expose whether a network was created by dokku
2026-07-08 14:52:32 -04:00
RichardDorian
513b200f5c test(storage): add out-of-bounds chown coverage for go code and script 2026-07-08 19:38:08 +02:00
Jose Diaz-Gonzalez
ac4b86fd8e feat: expose whether a network was created by dokku
`network:list` and `network:info` now expose a `DokkuManaged` boolean derived from the `com.dokku.network-name` label that `network:create` applies, and `network:list` gains a `--dokku-managed` flag to restrict output to dokku-created networks. This lets tooling distinguish networks dokku created from Docker built-ins and networks created by other tooling such as compose.
2026-07-08 00:03:29 -04:00
Jose Diaz-Gonzalez
1ee462dc3c docs: document nginx validate-config load_module override
The nginx deploy-time pre-validation runs `nginx -t` against a minimal wrapper that omits the global `load_module` directives, so a custom `nginx.conf.sigil` using a directive from a dynamically loaded module fails validation even though it is valid against the running server. Document overriding the `validate-config` template through the `nginx-app-template-source` trigger as the supported workaround.
2026-07-07 23:33:59 -04:00
Jose Diaz-Gonzalez
24e3809a34 Merge pull request #8807 from dokku/8802-buildpacks-allow-setting-the-entire-ordered-buildpack-list-in-one-command
Allow replacing buildpack list atomically
2026-07-07 22:13:59 -04:00
Jose Diaz-Gonzalez
62164181e0 Merge pull request #8808 from dokku/8799-docker-options-report-expose-options-as-a-structured-list
Expose docker-options as structured lists in JSON report
2026-07-07 21:29:56 -04:00
Jose Diaz-Gonzalez
4652749b5b feat: expose docker-options as structured lists in JSON report
Add parallel -list keys to docker-options:report --format json so export
tools can round-trip options without splitting space-joined strings.

Closes #8799
2026-07-07 19:49:17 -04:00
Jose Diaz-Gonzalez
16f8b25bda feat: allow replacing buildpack list atomically
Add buildpacks:set --replace so callers can replace an app's complete ordered buildpack list in one command while preserving existing single-buildpack and --index behavior.

Closes #8802
2026-07-07 18:37:38 -04:00
Jose Diaz-Gonzalez
b2c4f61387 feat: expose scheduler-k3s autoscaling-auth state for read-back
Align autoscaling-auth:report with annotations and labels reporting so export tools can recover configured trigger auth via flat JSON keys and info flags, while stdout stays secret-safe unless --include-metadata is used.

Closes #8800
2026-07-07 18:07:39 -04:00
RichardDorian
a0361d59c1 test(storage): add unit test for custom chown values 2026-07-07 20:48:34 +02:00
Jose Diaz-Gonzalez
cf15fb139e Merge remote-tracking branch 'origin/master' into parallel-bash-reports
# Conflicts:
#	go.work
2026-07-07 10:38:57 -04:00
Jose Diaz-Gonzalez
8d0c36bad6 feat: port checks and domains :report subcommands to golang
The bash :report implementations for the checks and domains plugins are replaced with a compiled golang binary that collects report keys in parallel and marshals json directly. The domains global report header now matches the shared renderer used by every other golang report, and its bats assertion is updated accordingly.
2026-07-07 06:43:31 -04:00
Jose Diaz-Gonzalez
5faabea3d4 feat: port vhost proxy :report subcommands to golang
The bash :report implementations for the caddy, haproxy, openresty, and traefik proxy plugins are replaced with a compiled golang binary that collects report keys in parallel and marshals json directly. The traefik dns-provider values keep their masking behaviour, remaining hidden in the default stdout report while surfacing for --format json or an explicit flag query. These four plugins previously had no unit tests, so a bats suite covering the report matrix is added for each.
2026-07-07 06:29:01 -04:00
Jose Diaz-Gonzalez
9c819cfebc feat: add --format json support to plugin:list
Adds a `--format json` flag to `plugin:list` whose output includes each plugin's install source - for git-based third-party plugins, the git remote URL, the checked-out commit, and the followed branch - so the set of installed plugins can be reconstructed elsewhere.

Closes #8798.
2026-07-07 05:17:40 -04:00
Jose Diaz-Gonzalez
857d115fdd feat: add --format json support to ps:scale
The `ps:scale` command now accepts a `--format` flag that defaults to `stdout` and can be set to `json` to emit the current formation as a JSON array of process type and quantity objects, matching the JSON output the `:report` subcommands already provide. The flag only applies when displaying the current formation; it is ignored when process types are supplied for scaling. When no scale has been set for the app, the JSON output is an empty array.
2026-07-06 13:57:19 -04:00
Jose Diaz-Gonzalez
21c27e99cc feat: add --format json support to apps:list
The `apps:list` command now accepts a `--format` flag that defaults to `stdout` and can be set to `json` to emit the app names as a JSON array, matching the JSON output the `:report` subcommands already provide. When no apps exist, the JSON output is an empty array.
2026-07-06 11:50:56 -04:00
Jose Diaz-Gonzalez
ac6e4f5793 test: allow multiple 'has been added' matches in preview test 3
For an undeployed app, the additive diff covers every chart resource, so
the literal 'has been added' header appears once per resource - six times
in the python test app's chart. `assert_output_contains` defaults to
exact-count 1, which fails. Pass -1 (at least 1) to match the actual
semantics of the test.
2026-06-03 01:53:30 -04:00
Jose Diaz-Gonzalez
496ede44b7 feat: add scheduler-k3s:preview subcommand
Adds a `scheduler-k3s:preview <app>` command that renders a unified diff
between the manifests currently stored in the live Helm release for an app
and the manifests that the next deploy would roll out. The diff is produced
entirely via the Helm Go SDK that scheduler-k3s already uses, with no
external `helm` binary or `helm-diff` plugin install required. The
underlying diff and parse logic is vendored from `databus23/helm-diff`
(Apache 2.0) into a new internal package, trimmed of three-way-merge,
release-ownership tracking, and the non-default output formatters.

The chart-construction half of `TriggerSchedulerDeploy` has been extracted
into a reusable `BuildAppChart` helper so deploy and preview share the
same chart materialization without duplicating template logic. Secret
values are redacted by default in preview output; `--show-secrets` and
`--show-secrets-decoded` flags match the upstream `helm diff` UX. Only the
main app helm release is compared in this iteration; auxiliary releases
for config vars, image-pull secrets, and TLS certificates are out of scope
and documented as such.
2026-06-02 20:28:31 -04:00
Jose Diaz-Gonzalez
21ed5fb738 fix: persist scheduler-k3s annotations and labels as JSON maps
Annotation and label values containing a newline previously round-tripped
as two adjacent `key: value` lines, and the second line then failed the
`SplitN(line, ": ", 2)` parse on read. Move the per-`(processType,
resourceType)` files to `PropertyMap*` storage so `\n` in values and `/`
in keys are preserved verbatim. An idempotent `TriggerInstall` migration
walks `--global` and every app, converting any legacy line-formatted file
in place by probing `PropertyMapGet` first and rewriting via
`PropertyListGet` only when the probe fails. Property names are
unchanged, so the annotations/labels report scanner and the
`reservedAnnotationPrefixes` filter keep working without modification.

Closes #8719.
2026-05-31 02:38:35 -04:00
Jose Diaz-Gonzalez
b8929712d4 fix: persist scheduler-k3s chart overrides as JSON maps
Chart property names containing `/` failed because per-key flat-file storage interpreted the slash as a filesystem path separator, and any move to line-based storage would silently truncate multi-line values. A new `PropertyMap*` helper family in `common` persists each map as a single JSON file so both `/` in keys and `\n` in values round-trip losslessly. Chart overrides move to `chart-overrides.<chart>` and an idempotent `TriggerInstall` migration rewrites any legacy `chart.<chart>.<key>` files into the new layout. The deprecated `scheduler-k3s:set --global chart.*` form is rerouted through the same map storage so it does not silently orphan writes. Closes #8717.
2026-05-31 01:54:28 -04:00
Jose Diaz-Gonzalez
5dbb2951bb feat: add scheduler-k3s annotations:report and labels:report
The scheduler-k3s plugin exposes `annotations:set` and `labels:set` but no matching report subcommands, so scripts could not inspect the configured state for idempotent management without reading the property files directly. The new `scheduler-k3s:annotations:report` and `scheduler-k3s:labels:report` surface the configured entries with stdout, JSON, single-flag query, and `--process-type` / `--resource-type` filtering, mirroring the recently added `scheduler-k3s:charts:report`. The literal `GlobalProcessType` value `--global` is rendered as `global` in report keys to avoid leading dashes. The `scheduler-k3s:autoscaling-auth:report` command is updated alongside to loop over every app when no app and no `--global` flag is provided, matching the convention used by `scheduler-k3s:report`.
2026-05-30 20:12:00 -04:00
Jose Diaz-Gonzalez
4346a18194 feat: add scheduler-k3s:charts:set and :charts:report
Helm chart overrides on the scheduler-k3s plugin were previously set
through the generic `scheduler-k3s:set --global chart.<chart>.<property>`
interface, which mixed chart-level configuration into the same command
that manages scheduler properties and offered no focused way to inspect
which overrides were configured. A dedicated `scheduler-k3s:charts:set`
sets and clears chart-specific helm values, and a complementary
`scheduler-k3s:charts:report` surfaces configured overrides per chart
with optional JSON output and single-field flag queries. The legacy form
on `scheduler-k3s:set` continues to work but now emits a deprecation
warning pointing users at the new subcommand.
2026-05-30 19:35:20 -04:00