Laravel Cloud
Laravel Cloud is our deployment platform, replacing Laravel Vapor for hosting Laravel services. Not all projects are on Cloud yet - we're migrating incrementally.
Current State
Services
| Service | Status | Notes |
|---|---|---|
| farfalla | Vapor | Core monolith |
| farfalla-https-guard | Cloud | First migrated service |
| farfalla-integrations | Vapor | |
| medusa | Vapor | |
| coniglio | Vapor | |
| castoro | Vapor |
How We Deploy
Deployments are triggered via deploy hooks from GitLab CI:
deploy:staging:
script:
- curl -X POST "$LARAVEL_CLOUD_STAGING_DEPLOY_HOOK"
deploy:production:
script:
- curl -X POST "$LARAVEL_CLOUD_PRODUCTION_DEPLOY_HOOK"
Deploy hooks are async - the curl returns immediately and the actual deployment happens in the background. No post-deploy health checks.
CI variables per project:
LARAVEL_CLOUD_STAGING_DEPLOY_HOOKLARAVEL_CLOUD_PRODUCTION_DEPLOY_HOOK
Build & Deploy Commands
Configured in the Laravel Cloud dashboard per environment.
Build commands (typical):
composer install --no-dev --optimize-autoloader
php artisan event:cache
php artisan config:cache
php artisan route:cache
Deploy commands:
php artisan migrate --force
php artisan optimize
Custom Domains
We point custom domains to Laravel Cloud via Cloudflare CNAME (proxy ON), then disable the default .laravel.cloud domain.
Monitoring
Server-side PHP monitoring uses Laravel Nightwatch (auto-injected env vars by Cloud). Keep Sentry JS SDK for frontend monitoring.
Configuration Reference
Persistent DB Connections
Enabled for better performance, but disabled in tests (breaks parallel execution and Bus::batch()):
// config/database.php
'options' => [
PDO::ATTR_PERSISTENT => app()->environment('testing') ? false : true,
],
Valkey (Redis-compatible)
Cloud uses Valkey (Redis-compatible) with ACL auth. Needs both username and password:
// config/database.php, redis connection
'username' => env('REDIS_USERNAME'),
Symptom: Horizon can't connect to Valkey. Cause: Vapor didn't need a username. Cloud uses Valkey ACL requiring both.
Queue Connection
Cloud does NOT auto-set QUEUE_CONNECTION. Must add QUEUE_CONNECTION=redis to env vars manually, otherwise jobs run synchronously.
Symptom: Jobs not appearing in Horizon despite worker being active.
Cause: Defaults to sync, so jobs run inline instead of being queued.
Horizon
- Move
laravel/horizontorequire(notrequire-dev) - Cloud does NOT auto-protect Horizon - add HTTP Basic Auth middleware
- Configure supervisors per environment in
config/horizon.php - Create a Worker Cluster in Cloud dashboard with
php artisan horizon - Schedule
horizon:snapshotfor non-local environments
Note: Vapor auto-protected Horizon in production. Cloud doesn't - you must add auth middleware yourself.
artisan optimize
Requires resources/views directory to exist. Create an empty one if your project doesn't have views.
Symptom: php artisan optimize fails with view:cache error.
Deploy Hooks Are Async
Deploy hooks return immediately; deployment happens in the background. Remove any post-deploy health check scripts that expect the deploy to be done.
Persistent DB Breaks Tests
Persistent connections cause issues with parallel test execution. Bus::batch() inserts and reads immediately, fails when connections are shared across test processes.
Fix: Disable in test environment (see Persistent DB Connections config above).