Moving from Laravel Vapor to Ploi Cloud
This week Taylor Otwell announced that Laravel Vapor no longer accepts new sign-ups. Vapor was getting around five new customers a week, and the Laravel team would rather send new customers to Laravel Cloud. If you're already on Vapor, nothing breaks. The platform stays online and the team keeps shipping bug fixes to keep it stable.
So there's no reason to pack up tonight. Still, a platform that stopped taking new customers is a platform in maintenance mode, and most teams would rather plan a move than rush one. If Ploi Cloud is on your list, this guide covers the whole migration. Most of it is configuration. Your Laravel code barely changes.
What changes when you leave Lambda
Vapor runs your app as AWS Lambda functions and wires a handful of AWS services around it. On Ploi Cloud your app runs as long-lived containers, with its services running next to it. This is how the parts map:
On Vapor | On Ploi Cloud |
|---|---|
Lambda functions handling HTTP requests | Application instances, with automatic scaling if you turn it on |
SQS queues processed by a queue Lambda | A worker running |
DynamoDB cache | A Redis or Valkey service |
RDS database | A MySQL or PostgreSQL service, or a managed database |
S3 bucket | Your existing S3 bucket, or Ploi Cloud object storage |
CloudFront serving assets through | Your app serves its own build assets |
Scheduler in | The Laravel scheduler toggle |
| Build commands and init commands |
Vanity and custom domains | Custom domains with automatic Let's Encrypt certificates |
The 15-minute Lambda limit goes away too. A queued job can run as long as it needs, and you can delay a job by more than 15 minutes again. If you ever chopped a long import into small jobs just to fit inside Lambda, you can stop doing that.
Step 1. Take stock of your Vapor environment
Open vapor.yml and write down the PHP runtime, the memory settings, the build and deploy steps, your queue names, the database and cache you attached, and your domains. Then pull the environment variables for the environment you're moving:
vapor env:pull productionThis writes a .env.production file to your project root. Keep it out of Git.
That file doesn't hold everything, though. Vapor injects a set of variables at runtime that never show up in it, and your app has been relying on them without you setting anything. The ones that matter for a move:
QUEUE_CONNECTION=sqsCACHE_DRIVER=dynamodb, unless you attached a Redis cacheSESSION_DRIVER=cookieFILESYSTEM_DISK=s3andAWS_BUCKETASSET_URL, pointing at CloudFrontMAIL_MAILER=seson environments namedproductionThe
DB_*variables for your RDS database
On Ploi Cloud you set these yourself, so decide on each one now. It's a lot less confusing than finding out after the move that your cache was sitting in DynamoDB.
Step 2. Remove the Vapor-specific code
Most apps only need a few small changes here.
Assets. Drop
ASSET_URL. If your JavaScript callsVapor.asset()orVapor.withBaseAssetUrl(), replace those with regular paths or Vite's own asset handling.Direct uploads.
Vapor.store()uploads straight to S3 through a signed URL route thatlaravel/vapor-coreregisters. On Lambda that route signs with the function's IAM role. Anywhere else it needs AWS access keys in the environment. Test uploads before you switch DNS, or move uploads to a regular controller endpoint.HTTPS behind the load balancer. Ploi Cloud terminates TLS at the load balancer. Configure
TrustProxies, otherwise Laravel generateshttp://URLs for forms and redirects. The snippet is in our Laravel documentation.The Vapor packages. Once nothing references them anymore, remove
laravel/vapor-coreand thelaravel-vapornpm package. Leaving them installed during the move does no harm.
Step 3. Create the application
Create a new Laravel application in Ploi Cloud and connect your repository. Pick a region close to your users, or close to the AWS region you run in today. You can choose Amsterdam, Frankfurt, London or Chicago. Set the PHP version to the one your Vapor runtime uses.
Then carry over your hooks:
The
buildsteps fromvapor.yml, usuallycomposer install --no-devandnpm ci && npm run build, become your build commands.The
deploysteps, usuallyphp artisan migrate --force, become init commands. They run before the new version takes traffic. If a migration fails, the new version doesn't start.
Step 4. Add services, workers and secrets
Add a MySQL or PostgreSQL service. If you want daily backups with point-in-time recovery and more than one node, create a managed database instead. We wrote about when to pick which.
Add a Redis service for cache, sessions and queues. Valkey works too. Turn on "Expose env variables as Redis" and Laravel connects without any config changes.
Add a worker for your queues, for example
php artisan queue:work --queue=emails,invoices --tries=3. One worker per queue works as well, if some queues need more capacity than others.Enable the Laravel scheduler in the application settings if your app has scheduled tasks.
Now add the secrets from .env.production. Skip the DB_* and REDIS_* variables, because Ploi Cloud injects those when you add the services. Then set the ones Vapor used to inject for you:
QUEUE_CONNECTION=redis
CACHE_STORE=redis
SESSION_DRIVER=redisA few things to watch:
Copy your
APP_KEY. Ploi Cloud generates a fresh key for every new Laravel app. Overwrite it with the key from Vapor, or anything you encrypted with the old key becomes unreadable.Sessions. Moving from cookie sessions to Redis logs everyone out once. If that's a problem, keep
SESSION_DRIVER=cookie.Older Laravel versions. On Laravel 10 and older, use
CACHE_DRIVERinstead ofCACHE_STORE.SES and other AWS services. On Lambda, the IAM role handed out credentials. If you keep sending mail through SES, create an IAM user and add its access keys as secrets.
Step 5. Move your data
The database
If your RDS database is publicly accessible, you can dump it directly. Most Vapor databases are private, though, and then you go through a jumpbox. Create one with vapor jump my-jumpbox, save the private key Vapor gives you, and open a tunnel as ec2-user:
ssh -i jumpbox.pem -N -L 3307:your-db.abc123.eu-west-1.rds.amazonaws.com:3306 ec2-user@JUMPBOX_IPIn a second terminal, dump the database through the tunnel:
mysqldump -h 127.0.0.1 -P 3307 -u vapor -p \
--single-transaction --routines --triggers \
your_database > dump.sqlThen import it. For a MySQL service, use the import feature: Give it the credentials of a publicly reachable source database and it streams the data over. On PostgreSQL, use pg_dump -Fc through the same tunnel and pg_restore on the Ploi Cloud side. Alternatively you can enable Debug access to import the dump file directly from your own computer.
Files
The quickest option is to keep your S3 bucket. Create an IAM user with access to that bucket and add AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION and AWS_BUCKET as secrets. Your code keeps working as it is.
If you'd rather have everything in one place, create Ploi Cloud object storage, which is S3-compatible, and copy the files over:
aws s3 sync s3://your-vapor-bucket ./bucket-copy
aws s3 sync ./bucket-copy s3://your-new-bucket \
--endpoint-url https://your-storage-id.ploi-cloud-storage.com \
--profile ploi-cloudStep 6. Test on the preview URL
Deploy, and your app goes live on its own preview.ploi.it address. Before any real traffic arrives, go through it properly. Log in, upload a file, dispatch a job and check the worker logs, and confirm your scheduled tasks show up. If your preview app points at a copy of production data, set MAIL_MAILER=log while you test, so real customers don't get test emails.
Step 7. Switch over
A day before, lower the TTL on your DNS records to 300 seconds.
Put Vapor in maintenance mode with
vapor down production, and wait until your SQS queues are empty.Take a final database dump and import it. If you're moving files, run the
s3 synconce more.Add your domain in Ploi Cloud and update your DNS records. Ploi Cloud requests the SSL certificate as soon as DNS points to it.
Check the site, the workers and the scheduler.
If something's off, vapor up production and a DNS change put you back where you started. That's why the low TTL matters.
Step 8. Clean up AWS
Keep the Vapor environment around for a week or two as a fallback. After that, delete the environment, the RDS database, the jumpbox and any NAT gateway Vapor created for private resources. These bill your own AWS account directly, and a NAT gateway costs money every hour even with zero traffic. Then cancel your Vapor subscription.
Or let your AI assistant do the setup
Steps 3 and 4 are mostly clicking through forms, and an AI assistant can do that for you through the Ploi Cloud MCP server. In Claude Code, connect it with one command:
claude mcp add --transport http ploi-cloud https://ploi.cloud/mcpIn Cursor, Windsurf or any other MCP-compatible tool, add https://ploi.cloud/mcp as an MCP server. The first time your assistant calls a Ploi Cloud tool, your browser opens to authorize access.
Then, from your project directory, give it something like this:
Read vapor.yml and .env.production and set up this app on Ploi Cloud in Frankfurt. Use a MySQL service, Redis for cache, sessions and queues, a worker for the emails and invoices queues, and turn on the scheduler. Copy the secrets except DB_* and Vapor-specific ones like ASSET_URL, and keep the existing APP_KEY. Show me the plan before you create anything, then deploy and give me the preview URL.
The assistant reads your Vapor config, shows you what it's going to create, and then creates the app, the services, the worker and the secrets through the Ploi Cloud API. It deploys, follows the build and gives you the preview URL. If the build fails, ask it why, and it pulls the deployment logs into your chat. With our pc-deploy skill installed, it also fixes common failures on its own, like missing PHP extensions or running out of memory.
Two notes. Your .env.production holds production credentials, and the assistant reads that file. If your company policy doesn't allow that, add the secrets yourself in the panel and let the assistant handle everything else. And we'd do the final database import and the DNS switch by hand. Those are the steps where you want to see every command before it runs.
Take your time
Vapor isn't going anywhere tomorrow, so there's no need to migrate in a weekend. A good first step is moving a staging environment, and moving production once that has run for a while without surprises. If you run into something this guide doesn't cover, get in touch. We're happy to look at your vapor.yml with you.
Related posts
Share this post
Ready to deploy your applications?
Join thousands of developers who trust Ploi Cloud to deploy and manage their containerized applications.
Get started for free