Skip to content

fix(deploy): cast force param as boolean to prevent cache bust on every deploy - #9909

Merged
andrasbacsai merged 4 commits into
coollabsio:nextfrom
JoshSalway:fix/deploy-force-boolean-cast-next
Jul 7, 2026
Merged

andrasbacsai merged 4 commits into
coollabsio:nextfrom
JoshSalway:fix/deploy-force-boolean-cast-next

Conversation

@JoshSalway

@JoshSalway JoshSalway commented May 2, 2026 •

Copy link
Copy Markdown
Contributor

Changes

When the deploy API is called with ?force=false as a query string parameter, every build runs with --no-cache, completely bypassing Docker BuildKit layer caching.

$request->input('force') ?? false reads the query string value false as a PHP string. A non-empty string is truthy in PHP, so $force is always true when the param is present, even when the caller explicitly passes false. This causes force_rebuild to be set to true and --no-cache to be appended to every docker build command.

PHP coercion -- why false becomes true:

force input input('force') ?? false (old) boolean('force') (fix)
false (query string) true ❌ false ✅
true (query string) true ✅ true ✅
0 false ✅ false ✅
1 true ✅ true ✅
absent false ✅ false ✅

$request->boolean() uses filter_var($value, FILTER_VALIDATE_BOOLEAN) which correctly maps string representations of false (false, 0, no, off) to false.

This is a well-known PHP/Laravel gotcha. $request->boolean() was introduced in Laravel 6 (6.12.0, Jan 2020) specifically because HTTP query string values always arrive as strings, and PHP's non-empty string truthiness catches developers expecting boolean semantics. The ?? null-coalescing operator only handles the absent case -- it does not protect against a present-but-wrong-type string. Laravel's own documentation and multiple community resources have called out this exact pattern since 2017. The fix uses the idiomatic Laravel solution that has been standard for 6+ years.

Real-world before/after (self-hosted Coolify v4.0.0-beta.474, Laravel app, warm cache):

Deploy Time
Before (with ?force=false) ~300s (every layer rebuilt)
After (force param absent / fix applied) ~20s (all unchanged layers cached)

Note: This is distinct from #7040 (which was caused by dynamic COOLIFY_CONTAINER_NAME / SOURCE_COMMIT ARG injection busting layer hashes, fixed in v450). Both bugs produce the same symptom (cache never used) but through different mechanisms. This bug fires specifically when the deploy API is called with ?force=false as a query string.

Issues

Related symptom (different root cause): #7040

Category

  • Bug fix

Preview

N/A -- no UI changes.

AI Assistance

  • AI was NOT used to create this PR
  • AI was used (please describe below)

If AI was used:

  • Tools used: Claude Code (for root cause tracing and test scaffolding)
  • How extensively: Used to trace through PHP source and identify the truthy string coercion. Fix is one line; test structure modelled on existing test patterns in the repo.

Testing

No test added. The fix is a one-line swap from $request->input('force') ?? false to $request->boolean('force'). Adding a feature test for this would require bootstrapping models, factories, and Sanctum token setup for a change that has no branching logic -- the coercion behaviour is already covered by PHP's own filter_var and by Laravel's existing $request->boolean() test suite. A dedicated test here would add noise without providing meaningful coverage above what already exists upstream.

Verified on a self-hosted Coolify instance (v4.0.0-beta.474): deploying via the API without the force param took ~20s on a warm cache instead of ~300s.

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate.
  • I have tested all the changes thoroughly with a local development instance of Coolify and I am confident that they will work as expected when a maintainer tests them.

JoshSalway added 4 commits May 2, 2026 23:11
…ry deploy

$request->input('force') ?? false reads "false" as a non-empty string,
which PHP coerces to true. Replace with $request->boolean() which uses
filter_var(FILTER_VALIDATE_BOOLEAN), correctly mapping "false" -> false.
Regression test for PHP string truthy coercion bug:
- force=false as query string should not trigger --no-cache
- force=true as query string should trigger --no-cache
- missing force param defaults to false
Adds datasets for falsy (false, 0) and truthy (true, 1) query string
values, covering the full coercion table from the PR description.
@andrasbacsai

Copy link
Copy Markdown
Member

Thank you for the PR! 💜

@andrasbacsai
andrasbacsai merged commit 76a4c14 into coollabsio:next Jul 7, 2026
1 check passed
@JoshSalway

Copy link
Copy Markdown
Contributor Author

@andrasbacsai Thanks for merging, this dramatically speeds up deployment for users.

@andrasbacsai andrasbacsai mentioned this pull request Jul 14, 2026
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants