Skip to content

Deployment · Open source

Kiln

Artisan commands and an optional signed HTTP route to clear, warm and inspect OPcache when you deploy.

Laravel 12 and 13 PHP 8.3+ v1.0.0 Free · MIT
Kiln: Artisan commands and an optional signed HTTP route to clear, warm and inspect OPcache when you deploy.

What it solves

With opcache.validate_timestamps=0, the recommended production setting, a Laravel app keeps serving stale bytecode after a deploy until the process pool restarts.

Resetting OPcache from php artisan does not fix it, because the CLI and the FPM worker pool do not share the same OPcache.

Kiln adds kiln:clear, kiln:warm, kiln:status and kiln:config, plus an optional route that runs the clear or warm inside an FPM worker. The route only accepts temporary signed URLs, and clear and warm are POST-only.

  • Four Artisan commands: kiln:clear, kiln:warm, kiln:status and kiln:config
  • kiln:warm compiles files with opcache_compile_file(), which does not run their top-level code
  • Optional HTTP route that clears or warms OPcache inside an FPM worker
  • The route refuses any signed URL without an expiry, even if the signature is valid
  • clear and warm are POST-only, so link prefetchers and scanners cannot trigger them
  • Warming over HTTP stops after 20 seconds by default and reports partial progress

Install

Requires PHP 8.3+ and Laravel 12 and 13.

composer require amjadiqbal/kiln

Usage

// .env: KILN_ROUTE_ENABLED=true
use Illuminate\Support\Facades\URL;

$url = URL::temporarySignedRoute('kiln.clear', now()->addMinutes(5));

// Then, from your deploy script: curl -fsS -X POST "$url"

Why Kiln exists

appstract/laravel-opcache did this job for years, but its last release, 4.0.2, shipped in December 2020. It requires guzzlehttp/guzzle: ^6.3.1|^7.0, and a fresh Laravel 13 app ships Guzzle 8, so 4.0.2 cannot be installed there. A plain composer require appstract/laravel-opcache on Laravel 13 does not fail, though: Composer quietly falls back to 2.0.5 (December 2019) plus appstract/lush-http, which Packagist lists as abandoned. On Laravel 12 (Guzzle 7) 4.0.2 still installs. Kiln is a from-scratch replacement for PHP 8.3+ and Laravel 12/13. It sets no Guzzle constraint of its own.

Setup

Package auto-discovery registers the service provider. Publish the config to change the warmed paths or enable the HTTP route.

  1. 1 composer require amjadiqbal/kiln
  2. 2 php artisan vendor:publish --tag=kiln-config

Artisan commands

A php artisan process has its own OPcache, separate from the FPM worker pool's. Run from the CLI, kiln:clear and kiln:warm only affect the CLI's OPcache and print a warning saying so. --time-limit bounds a warm run; on the CLI there is no limit by default.

php artisan kiln:clear     # opcache_reset() in this process
php artisan kiln:warm      # compile every file under config('kiln.warm_paths') into OPcache
php artisan kiln:status    # hit rate, memory usage, cached script count
php artisan kiln:config    # dump the relevant opcache.* ini directives, flag risky settings

php artisan kiln:warm --time-limit=15

The HTTP route for FPM deploys

Set KILN_ROUTE_ENABLED=true to register POST /kiln/opcache/clear, POST /kiln/opcache/warm and GET /kiln/opcache/status. Every route needs a temporary signed URL; a URL made with URL::signedRoute() has no expiry and is refused. Clearing once resets the shared OPcache for every worker in that pool. warm is bounded by kiln.warm_time_limit (20 seconds by default) and reports "timed_out": true with partial progress when it hits the limit.

url=$(php artisan tinker --execute="echo URL::temporarySignedRoute('kiln.clear', now()->addMinutes(5));")
curl -fsS -X POST "$url"

Behind a reverse proxy

If every call returns 403 Invalid signature although the URL and APP_KEY are correct, the app is probably not seeing the public scheme and host, usually because TrustProxies is not set up. Fix TrustProxies first. Otherwise set KILN_ABSOLUTE_SIGNATURE=false and generate every URL with absolute: false; the flag must match on both ends.

URL::temporarySignedRoute('kiln.clear', now()->addMinutes(5), [], absolute: false)

Hire me

Need this built into your app, or something custom?

I wrote Kiln. I can wire it into your Laravel app, extend it for your case, or build the feature around it. Tell me what you need and you'll get a plan and a quote, free.

Support

Found a bug or want a feature? Open an issue on GitHub. For help adding it to your own project, use the quote form above.

Need something built?

Free quote in 60 seconds