Serverless quietly breaks most of what you assume about monitoring a Laravel app. There's no box to SSH into, no long-running process to attach an agent to, no steady state to measure against — every request is a fresh (or recently-thawed) Lambda, and the cold ones can be five times slower than the warm ones for reasons that have nothing to do with your code. The tools and habits that work on Forge or a VPS mostly don't transfer, so it's worth being deliberate about what you actually watch on Vapor.
Why Vapor is different
Each request runs in a Lambda function. Two meaningful consequences for monitoring:
- No persistent process. Agents that buffer telemetry in-process and flush via a background thread fit PHP-FPM / Octane, not Lambda. Lambda's execution model assumes the function returns, memory evaporates, next invocation starts fresh (or reuses a warm container).
- Cold starts dominate p95. Lambda cold starts on PHP add 500-2000ms depending on layer size. A route that looks slow at p95 might be 95% fast + 5% cold-starting — a very different bug than being uniformly slow.
- CloudWatch is the default. All stdout/stderr ends up in CloudWatch Logs. Powerful but expensive at scale; queries (CloudWatch Insights) cost per GB scanned.
Tracking cold starts
Detect cold starts by checking whether a Lambda invocation is handling its first request. Record alongside route and duration:
app/Http/Middleware/TrackColdStart.php
use Closure;
use Illuminate\Http\Request;
class TrackColdStart
{
private static bool $invoked = false;
public function handle(Request $request, Closure $next)
{
$isColdStart = !self::$invoked;
self::$invoked = true;
$request->attributes->set('is_cold_start', $isColdStart);
return $next($request);
}
}
Push is_cold_start to your telemetry. Split p95 by cold vs warm — a slow cold-path is a bundle-size problem (reduce Vapor package), a slow warm-path is an actual code/DB problem.
CloudWatch → log aggregator
Raw CloudWatch gets expensive for search. Stream logs out via a subscription:
Lambda logs
↓
CloudWatch Logs
↓ (subscription filter)
Kinesis Data Firehose / Lambda forwarder
↓
Axiom / Loki / Papertrail / S3 + AthenaBetter Stack, Axiom, and Papertrail all have AWS CloudWatch integrations. For self-hosted: a small Lambda forwarder that ships logs to your Loki or OpenSearch.
Queue monitoring
Vapor queues are SQS + Lambda consumers. Key metrics:
- ApproximateAgeOfOldestMessage — SQS metric, surfaces queue lag
- ApproximateNumberOfMessagesVisible — queue depth
- Lambda Errors / Throttles — job failures / concurrency limit hit
- Lambda Duration — per-job runtime
All visible in CloudWatch metrics. Alert on rising age-of-oldest or throttle rate.
Scheduled tasks
Vapor's scheduler runs via EventBridge Rule → Lambda. Each invocation logs to CloudWatch. For structured tracking:
app/Console/Kernel.php
$schedule->command('reports:send')
->daily()
->onSuccess(fn() => /* push metric */)
->onFailure(fn() => /* push metric + alert */);Vapor-compatible APM options
- Laravel Nightwatch Cloud — first-party Laravel APM, designed with Vapor as a first-class target
- Sentry — HTTP-based SDK, Lambda-safe. Tracks errors + performance spans
- AppSignal — also HTTP-based, Lambda-safe
- Datadog APM — Lambda Extension handles the agent-process problem
NightOwl currently assumes a persistent agent process and doesn't fit Lambda. If you're committed to Vapor, pick from the options above.