In Laravel 13, what can the Cloud facade tell your code, and what does the framework configure automatically when an app runs on Laravel Cloud?
answer
- Illuminate\Support\Facades\Cloud, added 13.27
- hosted() wraps laravel_cloud()
- usesManagedQueues(): cloud connection driver
- queue() throws RuntimeException if unmanaged
- disks, read replica, Postgres pooler
basics
~20 sLaravel 13's Cloud facade offers hosted(), usesManagedQueues(), queue() and isManagedQueue(); separately, on Cloud the framework reads platform-injected variables to set up object-storage disks, a read replica, Postgres pooling, a managed cloud queue connection, logging and exception reporting.
solid answer
~30 sIn Laravel 13 (since 13.27.0), `Illuminate\Support\Facades\Cloud` proxies `CloudManager`. `Cloud::hosted()` says whether the app runs on Laravel Cloud (it wraps the `laravel_cloud()` helper); `usesManagedQueues()` checks that `queue.connections.cloud.driver` is `cloud`; `queue()` returns that managed connection or throws a `RuntimeException`; `isManagedQueue('emails')` (13.32.0) checks a queue name against the managed list. Separately, the framework's Cloud bootstrapper reads variables the platform injects: `LARAVEL_CLOUD_DISK_CONFIG` becomes `s3` or `scoped` disks, `DB_READ_HOST` becomes the default connection's read host, a Cloud Postgres pooler host gains a `pgsql-unpooled` connection that migrations use, `LARAVEL_CLOUD_MANAGED_QUEUES_CONFIG` becomes the `cloud` queue connection, and a `laravel-cloud-socket` log channel appears. None of it needs edits in `config/`.
code
php · 10 lines<?php
use Illuminate\Support\Facades\Cloud;
use Illuminate\Support\Facades\Log;
if (Cloud::hosted() && Cloud::isManagedQueue('reports')) {
$pending = Cloud::queue()->pendingSize('reports');
Log::info('Managed reports queue backlog', ['pending' => $pending]);
}go deeper
Recall that Laravel 13 has a Cloud facade and that Cloud::hosted() tells code whether it runs on Laravel Cloud.
Explain the four facade methods, which one throws, and what the bootstrapper sets up from injected variables: disks, read replica, managed queues, logging.
Explain the pooler versus direct Postgres endpoints and why migrations use pgsql-unpooled, and how to keep Cloud-specific calls from breaking local runs and tests.
Weigh the convenience of platform-injected configuration against coupling the codebase to one host, and decide where Cloud-specific code may live.
## Two separate pieces **Laravel Cloud** is Laravel's fully managed hosting platform. Laravel 13 carries two pieces of first-party integration for it, and interviewers who ask about Cloud usually want to know you can tell them apart: - the **`Cloud` facade**, a small API your own code can call; - the framework's **Cloud bootstrapper**, which quietly rewrites configuration during boot from variables the platform injects. Both arrived during the 13.x series: the facade in 13.27.0, `isManagedQueue()` in 13.32.0, and read-replica and opt-in Postgres pooling support in 13.33.0. ## The facade `Illuminate\Support\Facades\Cloud` resolves `Illuminate\Foundation\Cloud\CloudManager` from the container, like any Laravel facade. | Method | Returns | Notes | |---|---|---| | `Cloud::hosted()` | `bool` | Delegates to the `laravel_cloud()` helper | | `Cloud::usesManagedQueues()` | `bool` | True when `queue.connections.cloud.driver` is `cloud` | | `Cloud::queue()` | the managed queue connection | Throws `RuntimeException` ('Laravel Cloud managed queues are not configured for this application.') otherwise | | `Cloud::isManagedQueue(string $queue)` | `bool` | False, not an exception, when managed queues are off | `CloudManager` is `Macroable`, so teams can add their own helpers. The managed queue object exposes counts such as `size()`, `pendingSize()`, `delayedSize()` and totals across every managed queue (`totalSize()`, `totalPendingSize()`). ## What the bootstrapper configures When the corresponding variable is present, the bootstrapper changes configuration after config files load: - **Disks.** `LARAVEL_CLOUD_DISK_CONFIG` (JSON) becomes entries under `filesystems.disks`: `s3`-driver disks for object-storage buckets, or `scoped` disks with a prefix. A disk marked default becomes `filesystems.default` unless `FILESYSTEM_DISK` names another disk. - **Read replica.** `DB_READ_HOST` is written into the default database connection's `read.host`, so Laravel's read/write split sends selects to the replica. - **Managed queues.** `LARAVEL_CLOUD_MANAGED_QUEUES_CONFIG` becomes `queue.connections.cloud`, with `after_commit` defaulting to `CLOUD_QUEUE_AFTER_COMMIT` (false) and optional overflow and credential-cache settings. At provider boot the `cloud` driver is registered with a connector that wraps Laravel's SQS connector, and the failed-job provider is decorated to report to the platform. - **Logging.** A `laravel-cloud-socket` channel is added (and a `cloud` channel, if the app has none) that writes JSON to the platform's log socket, and the `stderr` channel includes stack traces. - **Exception reporting.** When `LARAVEL_CLOUD_EXCEPTIONS` is set, a reporter is registered that by default does not capture request payloads and redacts fields such as `password` and `_token` and headers such as `Authorization` and `Cookie`. ## Postgres pooling and migrations Cloud's Postgres offers a **pooler endpoint** (host ending in `-pooler`) in front of the database. A transaction-level pooler may send consecutive statements to different server connections, which breaks server-side prepared statements and session state that DDL and migrations rely on. The framework handles this in two ways: 1. With `DB_POOLING` unset and a `pgsql` host on the pooler, it copies the connection into `pgsql-unpooled` pointing at the direct host, turns on PDO emulated prepares for the pooled `pgsql` connection, and makes the migrator resolve `pgsql` as `pgsql-unpooled`. 2. With `DB_POOLING` set (13.33.0), `true` keeps Cloud Postgres connections on the pooler endpoint and `false` points them at the direct endpoint; a `pgsql-unpooled` connection is still defined. ## Pitfalls interviewers look for - Calling `Cloud::queue()` without checking `usesManagedQueues()` throws outside Cloud, including in local development and CI. - Hard-coding bucket credentials or replica hosts in `config/` fights the bootstrapper; let the injected variables win. - The same app may still carry `laravel/vapor-core` from an earlier host. Its service provider returns early from `register()` and `boot()` when `laravel_cloud()` is true, so Vapor's routes and SQS and DynamoDB settings stay out of the way on Cloud. ## Keeping Cloud-specific code contained Because `Cloud` is an ordinary facade over `CloudManager`, code that branches on it behaves predictably everywhere: - outside Cloud, `Cloud::hosted()` returns false, and `Cloud::usesManagedQueues()` stays false unless you configure a `cloud` queue connection yourself; - `Cloud::isManagedQueue()` is the safe probe, because it returns false instead of throwing; - keeping Cloud-specific calls in one small class (for example a service that reports queue backlog) means the rest of the app never needs to know where it runs. On the bootstrapper side, the disk, read-replica and managed-queue steps each return early when their injected variable is absent, so the same code base runs unchanged on a laptop or another host.
- What happens if code calls Cloud::queue() in local development?`CloudManager::queue()` first checks `usesManagedQueues()`; locally there is no `cloud` queue connection with the `cloud` driver, so it throws a `RuntimeException` saying managed queues are not configured for this application. Guard the call with `Cloud::usesManagedQueues()` or `Cloud::isManagedQueue($name)`, which return false instead of throwing.
- Why do migrations on Laravel Cloud Postgres run through pgsql-unpooled?With `DB_POOLING` unset and a pooler host, the framework defines `pgsql-unpooled` against the direct endpoint and registers a migrator resolver that swaps `pgsql` for it. A transaction-level pooler can split statements across server connections, which breaks prepared statements and session state that schema changes need, so DDL goes straight to the database while normal traffic uses the pooler.
The bootstrapper is like a hotel room that is already set up when you check in: the safe, the minibar and the wake-up call are configured by the hotel, and the facade is the room phone you use to ask reception what is available.
saying these in an interview costs you the question
- Cloud::hosted() decides by reading APP_ENV.
- Cloud::queue() returns null when managed queues are not configured.
- You must add Cloud buckets and replica hosts to config files by hand.
- Migrations should go through the Postgres pooler like every other query.
- The Cloud facade has shipped with every release since Laravel 11.