# Moving from Laravel Vapor to Ploi Cloud

> Laravel Vapor no longer accepts new sign-ups. Your apps keep running, but if you want to move, this guide covers the full migration step by step, including how to let your AI assistant handle the setup over MCP.

Published September 26, 2026 by Dennis

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 `php artisan queue:work` against Redis or Valkey

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 `ASSET_URL`

Your app serves its own build assets

Scheduler in `vapor.yml`

The Laravel scheduler toggle

`build` and `deploy` hooks in `vapor.yml`

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 production
```

This 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=sqs`
- `CACHE_DRIVER=dynamodb`, unless you attached a Redis cache
- `SESSION_DRIVER=cookie`
- `FILESYSTEM_DISK=s3` and `AWS_BUCKET`
- `ASSET_URL`, pointing at CloudFront
- `MAIL_MAILER=ses` on environments named `production`
- The `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 calls `Vapor.asset()` or `Vapor.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 that `laravel/vapor-core` registers. 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 generates `http://` URLs for forms and redirects. The snippet is in our [Laravel documentation](/documentation/laravel/laravel-applications).
- **The Vapor packages.** Once nothing references them anymore, remove `laravel/vapor-core` and the `laravel-vapor` npm 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 `build` steps from `vapor.yml`, usually `composer install --no-dev` and `npm ci && npm run build`, become your build commands.
- The `deploy` steps, usually `php 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](/blog/managed-database-or-host-it-yourself-the-difference-explained).
- 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=redis
```

A 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_DRIVER` instead of `CACHE_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_IP
```

In 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.sql
```

Then import it. For a MySQL service, use the [import feature](/documentation/services/import-mysql-database): 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](/documentation/object-storage/laravel-integration), 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-cloud
```

## Step 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

1. A day before, lower the TTL on your DNS records to 300 seconds.
2. Put Vapor in maintenance mode with `vapor down production`, and wait until your SQS queues are empty.
3. Take a final database dump and import it. If you're moving files, run the `s3 sync` once more.
4. Add your domain in Ploi Cloud and update your DNS records. Ploi Cloud requests the SSL certificate as soon as DNS points to it.
5. 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](/documentation/ai/claude-code). In Claude Code, connect it with one command:

```
claude mcp add --transport http ploi-cloud https://ploi.cloud/mcp
```

In 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](/documentation/ai/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](/contact). We're happy to look at your `vapor.yml` with you.
