Merge pull request #9225 from dokku/9224-add-a-way-to-rebuild-an-app-without-build-cache-on-demand

Add --no-cache flag to ps:rebuild
This commit is contained in:
Jose Diaz-Gonzalez
2026-10-29 21:05:57 -04:00
committed by GitHub
27 changed files with 318 additions and 9 deletions

View File

@@ -35,3 +35,5 @@ Building containers with buildpacks currently results in a persistent `cache` di
```shell
dokku repo:purge-cache node-js-app
```
To purge the cache and rebuild the app in a single command, see the [rebuilding without the build cache](/docs/processes/process-management.md#rebuilding-without-the-build-cache) documentation.

View File

@@ -167,6 +167,8 @@ To create a custom builder, the following triggers must be implemented:
- arguments: `BUILDER_TYPE` `APP` `IMAGE_AG`
- description: A post-build, pre-release trigger that can be used to post-process the image. Usually simply tags and labels the image appropriately.
When an app is rebuilt via `ps:rebuild --no-cache`, the `DOKKU_BUILD_NO_CACHE` environment variable is set to `true` during the `builder-build` trigger. Custom builders should skip any build cache they use when this variable is set.
Custom plugins names _must_ have the prefix `builder-` or builder overriding via `builder:set` may not function as expected.
Builders can use any tools available on the system to build the docker image, and may even be used to schedule building off-server. The only current requirement is that the image must exist on the server at the end of the `builder-build` command, though this requirement may be relaxed in a future release.

View File

@@ -35,7 +35,7 @@ While pack is well-supported in Dokku, there are a number of caveats. Please not
- A future release will add support for injecting extra arguments during the build process.
- The default process type is `web`.
- Build cache is stored in Docker volumes instead of on disk. As such, `repo:purge-cache` currently has no effect.
- A future version will add integration with the `repo` plugin.
- The build cache may be cleared for a single rebuild via `ps:rebuild --no-cache`. See the [rebuilding without the build cache](/docs/processes/process-management.md#rebuilding-without-the-build-cache) documentation for more information.
- `pack` is not currently included with Dokku, nor is it added as a package dependency.
- A future version will include it as a package dependency.

View File

@@ -177,7 +177,7 @@ dokku config:set --global CURL_CONNECT_TIMEOUT=180
### Clearing buildpack cache
See the [repository management documentation](/docs/advanced-usage/repository-management.md#clearing-application-cache) for more information on how to clear buildpack build cache for an application.
See the [repository management documentation](/docs/advanced-usage/repository-management.md#clearing-application-cache) for more information on how to clear buildpack build cache for an application. The cache may also be purged as part of a rebuild via `ps:rebuild --no-cache`. See the [rebuilding without the build cache](/docs/processes/process-management.md#rebuilding-without-the-build-cache) documentation for more information.
### Specifying commands via Procfile

View File

@@ -86,6 +86,8 @@ Disable cache using the [docker-options] plugin:
dokku docker-options:add node-js-app build "--no-cache"
```
To disable the cache for a single rebuild without persisting a setting, see the [rebuilding without the build cache](/docs/processes/process-management.md#rebuilding-without-the-build-cache) documentation.
### Displaying builder-nixpacks reports for an app
You can get a report about the app's storage status using the `builder-nixpacks:report` command:

View File

@@ -98,6 +98,8 @@ Disable cache using the [docker-options] plugin:
dokku docker-options:add node-js-app build "--no-cache"
```
To disable the cache for a single rebuild without persisting a setting, see the [rebuilding without the build cache](/docs/processes/process-management.md#rebuilding-without-the-build-cache) documentation.
### Displaying builder-railpack reports for an app
You can get a report about the app's storage status using the `builder-railpack:report` command:

View File

@@ -2490,6 +2490,22 @@ APP="$1";
# TODO
```
### `repo-purge-cache`
- Description: Deletes the contents of the build cache stored in the repository for an app
- Invoked by: `builder-herokuish` when rebuilding with `ps:rebuild --no-cache`
- Arguments: `$APP`
- Example:
```shell
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
APP="$1";
# TODO
```
### `report`
- Description: Allows you to report on any custom configuration in use by your application

View File

@@ -5,7 +5,7 @@
```
ps:inspect <app> # Displays a sanitized version of docker inspect for an app
ps:rebuild [--parallel count] [--all|<app>] # Rebuilds an app from source
ps:rebuild [--parallel count] [--no-cache] [--all|<app>] # Rebuilds an app from source
ps:report [<app>] [<flag>] # Displays a process report for one or more apps
ps:restart [--parallel count] [--all|<app>] [<process-name>] # Restart an app
ps:restore [<app>] # Start previously running apps e.g. after reboot
@@ -58,6 +58,29 @@ Finally, the number of parallel workers may be automatically set to the number o
dokku ps:rebuild --all --parallel -1
```
#### Rebuilding without the build cache
> [!IMPORTANT]
> New as of 0.38.34
An app may be rebuilt without using the build cache by specifying the `--no-cache` flag. This applies only to the triggered rebuild, and no setting is persisted for later deploys.
```shell
dokku ps:rebuild --no-cache node-js-app
```
The `--no-cache` flag may also be combined with the `--all` and `--parallel` flags.
```shell
dokku ps:rebuild --all --no-cache
```
The effect of the flag depends on the builder in use:
- `dockerfile`, `nixpacks`, and `railpack`: The build is run with `--no-cache`. For the `dockerfile` builder, [BuildKit directory caches](/docs/deployment/builders/dockerfiles.md#buildkit-directory-caching) are not cleared.
- `pack`: The build is run with `--clear-cache`.
- `herokuish`: The build cache is purged before the build, in the same way as `repo:purge-cache`.
A missing linked container will result in failure to boot apps. Services should all be started for apps being rebuilt.
### Restarting apps

2
dokku
View File

@@ -194,7 +194,7 @@ execute_dokku_cmd() {
fi
if [[ "$PLUGIN_NAME" == "git-hook" ]] || [[ "$PLUGIN_NAME" == "ps:rebuild" ]] || [[ "$PLUGIN_NAME" == "ps:restart" ]]; then
dokku_setup_build_capture "$2" "$PLUGIN_NAME"
dokku_setup_build_capture "$(fn-build-capture-app-name "$@")" "$PLUGIN_NAME"
fi
if [[ -x $PLUGIN_ENABLED_PATH/$PLUGIN_NAME/subcommands/default ]]; then

View File

@@ -279,6 +279,10 @@ trigger-builder-dockerfile-builder-build() {
esac
done
if [[ "$DOKKU_BUILD_NO_CACHE" == "true" ]] && ! fn-in-array "--no-cache" "${DOCKERFILE_ARGS[@]}"; then
DOCKERFILE_ARGS+=("--no-cache")
fi
eval "$(config_export app "$APP")"
local DOCKER_CONFIG
DOCKER_CONFIG="$(fn-registry-docker-config-dir "$APP")"

View File

@@ -77,6 +77,10 @@ trigger-builder-herokuish-builder-build() {
done < <(fn-docker-args-split "$DOCKER_ARGS")
local DOKKU_CONTAINER_EXIT_CODE=0
if [[ "$DOKKU_BUILD_NO_CACHE" == "true" ]]; then
dokku_log_info1 "Clearing build cache"
plugn trigger repo-purge-cache "$APP"
fi
fn-builder-herokuish-ensure-cache "$APP"
if ! CID=$("$DOCKER_BIN" container create "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS -v "cache-$APP:/cache" --env=CACHE_PATH=/cache "${ARG_ARRAY[@]}" "$IMAGE" /build); then
plugn trigger scheduler-register-retired "$APP" "$CID"

View File

@@ -244,6 +244,10 @@ trigger-builder-nixpacks-builder-build() {
esac
done
if [[ "$DOKKU_BUILD_NO_CACHE" == "true" ]] && ! fn-in-array "--no-cache" "${NIXPACKS_ARGS[@]}"; then
NIXPACKS_ARGS+=("--no-cache")
fi
eval "$(config_export app "$APP" --merged)"
local DOCKER_CONFIG

View File

@@ -282,6 +282,10 @@ trigger-builder-pack-builder-build() {
esac
done
if [[ "$DOKKU_BUILD_NO_CACHE" == "true" ]] && ! fn-in-array "--clear-cache" "${PACK_ARGS[@]}"; then
PACK_ARGS+=("--clear-cache")
fi
if [ -n "$BUILDPACK_URL" ]; then
PACK_ARGS+=("--builder")
PACK_ARGS+=("$BUILDPACK_URL")

View File

@@ -112,7 +112,7 @@ trigger-builder-railpack-builder-build() {
RAILPACK_ARGS+=("--start-cmd" "${1#--start-cmd=}")
shift 1
;;
---show-plan | --error-missing-start)
---show-plan | --error-missing-start | --no-cache)
RAILPACK_ARGS+=("$1")
shift 1
;;
@@ -126,6 +126,10 @@ trigger-builder-railpack-builder-build() {
esac
done
if [[ "$DOKKU_BUILD_NO_CACHE" == "true" ]] && ! fn-in-array "--no-cache" "${RAILPACK_ARGS[@]}"; then
RAILPACK_ARGS+=("--no-cache")
fi
eval "$(config_export app "$APP" --merged)"
local DOCKER_CONFIG

View File

@@ -942,6 +942,30 @@ merge_dedupe_list() {
echo "$merged_list"
}
fn-build-capture-app-name() {
declare desc="outputs the app name from the arguments of a build-capturing command, skipping any flags"
shift 1
while [[ $# -gt 0 ]]; do
case "$1" in
--all)
return 0
;;
--parallel)
shift 1
[[ $# -gt 0 ]] && shift 1
;;
-*)
shift 1
;;
*)
echo "$1"
return 0
;;
esac
done
}
dokku_setup_build_capture() {
declare desc="generate a build-id, write a record, and redirect output to log + journald"
declare APP="$1" SOURCE="${2:-unknown}"

View File

@@ -19,7 +19,7 @@ Additional commands:`
helpContent = `
ps:inspect <app>, Displays a sanitized version of docker inspect for an app
ps:rebuild [--parallel count] [--all|<app>], Rebuilds an app from source
ps:rebuild [--parallel count] [--no-cache] [--all|<app>], Rebuilds an app from source
ps:report [<app>] [<flag>], Displays a process report for one or more apps
ps:restart [--parallel count] [--all|<app>] [<process-name>], Restart an app
ps:restore [<app>], Start previously running apps e.g. after reboot

View File

@@ -27,9 +27,10 @@ func main() {
args := flag.NewFlagSet("ps:rebuild", flag.ExitOnError)
allApps := args.Bool("all", false, "--all: rebuild all apps")
parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to rebuild in parallel, -1 to match cpu count")
noCache := args.Bool("no-cache", false, "--no-cache: rebuild without using the build cache")
args.Parse(os.Args[2:])
appName := args.Arg(0)
err = ps.CommandRebuild(appName, *allApps, *parallelCount)
err = ps.CommandRebuild(appName, *allApps, *parallelCount, *noCache)
case "report":
args := flag.NewFlagSet("ps:report", flag.ExitOnError)
format := args.String("format", "stdout", "format: [ stdout | json ]")

View File

@@ -3,6 +3,7 @@ package ps
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
@@ -26,7 +27,11 @@ func CommandInspect(appName string) error {
}
// CommandRebuild rebuilds an app from source
func CommandRebuild(appName string, allApps bool, parallelCount int) error {
func CommandRebuild(appName string, allApps bool, parallelCount int, noCache bool) error {
if noCache {
os.Setenv("DOKKU_BUILD_NO_CACHE", "true")
}
if allApps {
return common.RunCommandAgainstAllApps(Rebuild, "rebuild", parallelCount)
}

View File

@@ -1,5 +1,5 @@
SUBCOMMANDS = subcommands/gc subcommands/purge-cache
TRIGGERS = triggers/post-stack-set triggers/post-delete
TRIGGERS = triggers/post-stack-set triggers/post-delete triggers/repo-purge-cache
BUILD = commands subcommands triggers
PLUGIN_NAME = repo

View File

@@ -24,6 +24,9 @@ func main() {
case "post-delete":
appName := flag.Arg(0)
err = repo.PurgeCache(appName)
case "repo-purge-cache":
appName := flag.Arg(0)
err = repo.PurgeCache(appName)
default:
err = fmt.Errorf("Invalid plugin trigger call: %s", trigger)
}

View File

@@ -345,6 +345,31 @@ EOF
assert_output "['task.py', 'some', 'cron', 'task']"
}
@test "(builder-dockerfile) ps:rebuild --no-cache" {
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" -1
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" 1
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" -1
}
cron_run_wrapper() {
local APP="$1"
local APP_REPO_DIR="$2"

View File

@@ -253,6 +253,50 @@ teardown() {
assert_output "['task.py', 'some', 'cron', 'task']"
}
@test "(builder-herokuish) ps:rebuild --no-cache" {
run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "docker volume inspect --format '{{.CreatedAt}}' cache-$TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
cache_created_at="$output"
sleep 1
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Clearing build cache" 0
run /bin/bash -c "docker volume inspect --format '{{.CreatedAt}}' cache-$TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "$cache_created_at"
sleep 1
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Clearing build cache"
run /bin/bash -c "docker volume inspect --format '{{.CreatedAt}}' cache-$TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
[[ "$output" != "$cache_created_at" ]]
}
cron_run_wrapper() {
local APP="$1"
local APP_REPO_DIR="$2"

View File

@@ -229,6 +229,30 @@ teardown() {
assert_success
}
@test "(builder-nixpacks) ps:rebuild --no-cache" {
run /bin/bash -c "dokku builder:set $TEST_APP selected nixpacks"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" -1
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "unpacking 'https://github.com/NixOS/nixpkgs" -1
}
cron_run_wrapper() {
local APP="$1"
local APP_REPO_DIR="$2"

View File

@@ -326,6 +326,32 @@ teardown() {
assert_success
}
@test "(builder-pack) ps:rebuild --no-cache" {
run /bin/bash -c "dokku builder:set $TEST_APP selected pack"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt_cnb
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Using cached pip download/wheel cache"
assert_output_contains "Skipping buildpack layer analysis" 0
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Skipping buildpack layer analysis"
assert_output_contains "Using cached pip download/wheel cache" 0
}
cron_run_wrapper() {
local APP="$1"
local APP_REPO_DIR="$2"

View File

@@ -240,6 +240,53 @@ teardown() {
assert_success
}
@test "(builder-railpack) ps:rebuild --no-cache" {
run /bin/bash -c "dokku builder:set $TEST_APP selected railpack"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" -1
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Collecting flask" -1
}
@test "(builder-railpack) docker-options build --no-cache" {
run /bin/bash -c "dokku builder:set $TEST_APP selected railpack"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku docker-options:add $TEST_APP build --no-cache"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Collecting flask" -1
}
cron_run_wrapper() {
local APP="$1"
local APP_REPO_DIR="$2"

View File

@@ -91,6 +91,36 @@ EOF
assert_output_contains "git-hook"
}
@test "(builds:report) records a ps:rebuild called with flags before the app name" {
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild --no-cache $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "builds-record" 0
run /bin/bash -c "dokku builds:report $TEST_APP --build-source"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "ps:rebuild"
run /bin/bash -c "dokku builds:report $TEST_APP --build-status"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "succeeded"
run /bin/bash -c "[[ ! -e $DOKKU_LIB_ROOT/data/builds/--no-cache ]]"
echo "output: $output"
echo "status: $status"
assert_success
}
@test "(builds:list) shows an abandoned record when the PID is gone but the record is still running" {
write_running_record "$TEST_APP" "ghost001" 99999 "git-hook"

View File

@@ -196,3 +196,16 @@ teardown() {
echo "output: ($output)"
assert_output 'web: 1'
}
@test "(ps:rebuild) --all --no-cache" {
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:rebuild --all --no-cache"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "CACHED" 1
}