Laravel Vapor to Cloud Migration Guide
Step-by-step guide for migrating Laravel projects from Vapor to Cloud. For Cloud-specific configuration patterns and known gotchas, see the Configuration Reference in the Laravel Cloud overview.
Migration Checklist​
1. Initial Setup​
- Create app in Laravel Cloud (connect GitHub repo)
- Set environment name (staging/production)
- Configure branch (Cloud defaults to "master"; set to
mainfor migrated repos) - Disable Push-to-deploy in the environment settings if CI is going to trigger deploy hooks (otherwise every push fires two deploys)
- Copy env vars from Vapor equivalent environment
APP_KEY, APP_DEBUG, DB_*, etc.warningVapor secrets are NOT exported. Add them manually in Cloud.
- Add AWS credentials (see "AWS - vapor-laravel user, for Laravel Cloud" in 1Password)
- Use public endpoint for external databases (Cloud has no VPC peering with external providers like SingleStore)
- Create managed Valkey resource (Cache) in the Cloud dashboard if the project uses Redis
- Enable Scheduler in the environment settings (separate toggle, not enabled by default)
- Deploy and verify on
.laravel.clouddomain
2. Code Changes​
Remove Vapor:
- Delete
vapor.yml,vapor.staging.yml,vapor.production.yml - Delete
vapor.sh,deploy.sh,vapor-base.Dockerfile, post-deploy scripts - Remove
laravel/vapor-cliandlaravel/vapor-corefrom composer - Clean up
.gitignore(remove Vapor, Homestead, Vagrant entries) - Remove orphaned vapor-ui public assets if present
- If
public/vendor/horizon/is tracked in git:git rm -r public/vendor/horizonand add/public/vendor/horizonto.gitignore. The Cloud build runsphp artisan horizon:publish, which regenerates these assets on every deploy. Leaving them committed is a landmine (stale assets keep the dashboard "working" until a Horizon upgrade desyncs them).
Update CI/CD:
- Add Laravel Cloud deploy hooks to CI (see How We Deploy for the hardened curl snippet). A bare
curl -X POST "$HOOK"silently swallows non-2xx responses; always include--fail-with-body --retry 3 --retry-all-errors --connect-timeout 10 --max-time 30. - Append
?commit_hash=<sha>to the hook URL so the Cloud dashboard shows the real commit per deploy - Remove the Vapor deploy job (for production, keep a manual-trigger backup until Cloud is validated)
- Remove post-deploy health checks (hooks are async)
- Add
LARAVEL_CLOUD_STAGING_DEPLOY_HOOKandLARAVEL_CLOUD_PRODUCTION_DEPLOY_HOOKas CI secrets. On GitHub, scope them to the matching environment (staging,production) so they're only exposed to jobs declaring that environment. - Add a
workflow_dispatchtrigger so deploys can be re-fired manually when the hook is async and a single retry isn't enough
Configure Build/Deploy Commands: set in the Laravel Cloud dashboard per environment. See Build & Deploy Commands for typical values.
3. Optimizations​
- Configure faster CPU (if needed for workload)
- Enable persistent DB connections - disable in test environment
- Add
php artisan optimizeto deploy commands (see artisan optimize) - Replace Sentry with Nightwatch (server-side PHP only). Do these in order:
- Switch
config/logging.phpstack from a hardcoded array to'channels' => explode(',', env('LOG_STACK', 'stderr,single'))so Cloud can append the nightwatch channel via env without a code change. A hardcodedchannelsarray silently drops it. - Remove the
sentrychannel definition fromconfig/logging.php. If the LOG_STACK default still containedsentry, your first boot after deploy would fail referencing a missing channel; keeping the two steps in this order avoids the gap. - Remove
sentry/sentry-laravelfromcomposer.jsonand deleteconfig/sentry.php - Remove Sentry deploy notifications from CI
- Install
laravel/nightwatch - Create project/environment in Nightwatch and copy the token
- Activate Nightwatch integration in the Cloud environment with the token
- Keep Sentry JS SDK if the project has a frontend (e.g., Farfalla)
- Switch
Once activated, Nightwatch SDK env vars are auto-injected by Cloud. No further app config needed.
4. Queue/Horizon Setup (if applicable)​
See Horizon, Queue Connection, and Valkey in the Configuration Reference.
- Move
laravel/horizonfromrequire-devtorequire(if dev-only) - Add
QUEUE_CONNECTION=redisto env vars (not auto-set by Cloud!) - Verify
REDIS_USERNAMEis auto-injected by Cloud (set when creating the managed Valkey resource) - Configure Horizon supervisors for each environment in
config/horizon.php - Enable
horizon:snapshotfor non-local environments - Add HTTP Basic Auth middleware for Horizon (Cloud doesn't auto-protect)
- Create Worker Cluster in Laravel Cloud dashboard
- Add background process:
php artisan horizon - Configure compute size for queue workload
- Add background process:
5. Pre-cutover Validation​
Smoke test on the .laravel.cloud domain before swapping DNS:
- Verify the app responds (main routes return 200)
- Test admin panel login (Nova/Filament/etc.)
- Verify DB reads and writes
- Confirm migrations applied (
migrate:statusis green) - Open Horizon dashboard at
/horizonand confirm auth protection - Dispatch a test job and confirm it processes (no jobs piling up in
pending) - Test S3 reads/writes (upload + signed URL)
- Verify scheduled tasks running (
schedule:listshows next run timestamps) - Confirm Nightwatch is reporting traces
- Confirm Sentry JS is reporting frontend errors (if applicable)
6. Domain Swap​
- At least a day before the swap: lower the DNS TTL on the domain record (Cloudflare: 30-60s). Without this, propagation can take hours; with it, the swap effectively completes in minutes.
- Add custom domain in Laravel Cloud
- Create CNAME in Cloudflare pointing to Laravel Cloud (proxy ON)
- Wait for DNS propagation. Practical signal: watch Vapor Lambda invocations in CloudWatch drop to ~0 for 5-10 min consecutive while Cloud request volume rises in Nightwatch.
- Test custom domain
- Disable the
.laravel.clouddomain - Restore the DNS TTL to its normal value once the cutover is stable
7. Final Cleanup​
-
Monitor for issues in Nightwatch
-
Remove the Vapor deploy job from CI (if kept as backup during transition)
-
Complete the Remove Vapor steps (if deferred from Section 2)
-
Remove any unused health check commands
-
Update documentation:
Docs to update
- Laravel Cloud Overview - mark service as Cloud in the services table
- AWS Infrastructure Overview - says infra is orchestrated through Vapor
- File Storage Architecture - S3 bucket naming references Vapor
- Caddy HTTPS Servers - links to Vapor custom domains article
Resources​
- Laravel Cloud Docs
- Vapor to Cloud Migration Guide
- Latency Benchmarks - Performance comparison data