Laravel / Advanced / Lesson 18
Deployment configuration and release safety
Treat an application release as code plus resolved configuration and compatible long-running processes, with explicit verification and rollback limits.
On this page
Lesson goals
- Explain cached configuration and the correct env access boundary.
- Plan compatible artifacts, workers, secrets, and schema changes.
- Verify release health without confusing boot success with dependency readiness.
A release includes more than source files
The verified local host uses Laravel 12.63.0 and PHP 8.5.12; the course syntax baseline remains PHP 8.3, and these examples are separate from an executed deployment. A release also depends on extensions, Composer-locked packages, generated assets, configuration, writable application directories, and supported services. The synthetic application has a report limit setting and a release label, with no real credentials. Build an identifiable artifact and document the environment it expects. A successful developer request does not establish that a clean release starts with the correct configuration, permissions, queue code, and database compatibility. No deployment command is executed in this lesson.
Validate the configuration your code actually receives
Environment values commonly arrive as text and need a deliberate interpretation. Casting malformed input directly to int can accept a numeric prefix and hide an invalid setting. The complete PHP model uses FILTER_VALIDATE_INT with a bounded positive range, throwing a controlled error for invalid values. It reads only provided synthetic strings, not process environment or application secrets. This is a parsing contract, not proof of Laravel bootstrap behavior. Apply equivalent validation when resolving the application setting so an invalid deployment fails clearly before serving work rather than silently choosing a dangerous limit. Preserve the exact validation rule across cached and uncached configuration paths.
<?php
declare(strict_types=1);
function reportLimit(string $raw): int
{
$value = filter_var($raw, FILTER_VALIDATE_INT, [
"options" => ["min_range" => 1, "max_range" => 5000],
]);
if ($value === false) { throw new InvalidArgumentException("invalid report limit"); }
return $value;
}
foreach (["250", "0", "25x"] as $raw) {
try { echo reportLimit($raw), PHP_EOL; }
catch (InvalidArgumentException) { echo "rejected", PHP_EOL; }
}
250
rejected
rejected
The valid limit is 250. Zero and the malformed numeric-prefix string are rejected. No environment variables or configured application are read.
Resolve environment values inside configuration files
Laravel configuration caching combines resolved configuration for later loading. After caching, the framework does not load the application .env file during requests or Artisan execution; env then relies on external system environment and its fallback behavior. Therefore call env inside configuration files and read application settings through config elsewhere. The contextual file below declares the synthetic settings, which still need the stated value validator at the application boundary. Building cached configuration in the wrong environment can bake in wrong values. Treat cache generation as part of an authorized release procedure with the required secrets available, not a step casually run against a configured application during a lesson.
<?php
return [
"release" => env("APP_RELEASE", "local"),
"max_rows" => env("REPORT_MAX_ROWS", "1000"),
];
Expected application use: read config("reports.release") and config("reports.max_rows"), then validate the limit under the declared contract. This file is not bootstrapped or cached here.
Coordinate long-running processes with code and schema
Queue workers retain booted application state and must adopt the new release through a documented lifecycle. Laravel queue restart signaling is graceful and depends on the configured cache and a process manager to bring exited workers back; a changed file does not itself restart them. Design database changes and job payloads for a rolling interval when old and new code can coexist. An expand-then-contract schema strategy can preserve compatibility, but rollback after destructive data transformation is not automatically safe. Keep application encryption keys stable unless a deliberate rotation plan covers stored ciphertext and sessions. Never generate a fresh key merely because a release is new.
Verify the exposed surface and rollback evidence
The web server should expose the intended public entry directory rather than the whole project, and production debug output must not reveal configuration or secrets. The example environment fragment contains only synthetic non-secret values. Laravel’s default health route demonstrates successful application boot; database or queue readiness needs additional intentionally bounded checks. Verify the release identifier, a representative authorized request, denied access, queued completion, and logs without issuing real admin actions in these labs. A rollback plan should distinguish reverting code from undoing data changes. Keep tested artifacts and compatible configuration available, and state exactly which checks ran instead of treating a green health response as full system recovery.
APP_ENV=production
APP_DEBUG=false
APP_RELEASE=release-20261025
REPORT_MAX_ROWS=250
These are illustrative values for an isolated fixture, not instructions to edit a configured environment. A successful release should resolve the chosen label and validated limit while keeping debug details private.
Common errors and fixes
A setting works locally but disappears after configuration caching.
- Why it happens
- Runtime code reads env instead of resolved configuration.
- How to fix it
- Move environment resolution into config and read config elsewhere.
- Prevent it next time
- Test cached and uncached configuration contracts in isolated fixtures.
New requests use a release while workers keep old behavior.
- Why it happens
- Long-running workers did not follow the release lifecycle.
- How to fix it
- Use an authorized graceful worker transition with supervision.
- Prevent it next time
- Verify queued completion and release identity after deployment.
Rolling back code does not restore application correctness.
- Why it happens
- Schema or data changes are incompatible with the previous release.
- How to fix it
- Plan compatible migrations and explicit data recovery limits.
- Prevent it next time
- Rehearse rollback assumptions with synthetic data before release.
Check your understanding
1. Classify reportLimit("1"), reportLimit("5000"), and reportLimit("5001"). Explain why a direct integer cast of "25x" would weaken the contract.
Show worked solution
One and five thousand are accepted endpoints; five thousand one is rejected. A direct cast can interpret the numeric prefix of malformed text and turn a configuration error into an unintended valid value. Keep validation before relying on the setting, and test both endpoints plus malformed strings in the same parser used by the application’s configuration-resolution path.
2. A release has a green /up response but reports are stuck in the queue. State what /up has established and the additional evidence needed.
Show worked solution
The default route establishes that application boot completed without the relevant exception; it does not prove queue workers, driver connectivity, or business completion. Inspect authorized operational signals for queue delay, worker release identity, attempts, and a synthetic end-to-end report fixture. Keep dependency probes bounded and avoid treating a health endpoint as permission to run migrations or restart production services.
Key takeaways
- Release code, resolved configuration, and workers together.
- Read env only at the documented configuration boundary.
- Boot health and rollback readiness require separate evidence.