All jobs

Dockerize a Laravel App

Yes. An agent writes a multi-stage Dockerfile and compose.yaml for your Laravel app, boots it with its database in a sandbox and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent clones the repository into its own sandbox and reads what the image has to carry before it writes a line of the Dockerfile. In a Laravel app like laravelio/laravel.io, composer.json pins "php": "8.4.*" with laravel/framework ^11.5, Livewire 3, Filament 4 and intervention/image 3, and package.json builds the front end with Vite 6 and Tailwind 4 ("build": "vite build"). The test workflow in .github/workflows/tests.yml is the most honest list of what the app needs to run: PHP 8.4 with the dom, curl, mbstring, zip, pcntl, pdo and gd extensions, Node 22.x for npm run build, and a mysql:5.7 service with a laravel database. There is no Dockerfile or compose file in the repository today.

From that it writes a three-stage Dockerfile. A Node 22 stage runs npm ci and npm run build so the hashed assets land in public/build; a Composer stage runs composer install --no-dev --prefer-dist --optimize-autoloader against composer.lock; and the runtime stage starts from a PHP 8.4 image, installs the extensions the workflow lists plus pdo_mysql, copies in vendor/ and public/build from the earlier stages, and never ships Node, Composer or the dev dependencies. Two details come from reading the app rather than a template. database/schema/mysql-schema.sql is a schema dump, and Laravel loads a dump with the mysql command-line client, so the runtime image needs that client or php artisan migrate fails on an empty database. And .env.example sets CACHE_STORE=file and SESSION_DRIVER=file, so storage/ and bootstrap/cache have to be writable by the user the container runs as.

Then compose.yaml: an app service built from that Dockerfile, a mysql service on the same 5.7 image CI uses with a health check, DB_HOST, DB_DATABASE, DB_USERNAME and DB_PASSWORD passed from the environment rather than baked in, and a named volume for the database. Because routes/console.php schedules commands such as lio:generate-sitemap and lio:update-article-view-counts, the agent adds a scheduler service running php artisan schedule:work from the same image, or says in the PR why it left one out. It also writes a .dockerignore so vendor/, node_modules/, .env and storage/logs stay out of the build context.

Last, it proves the thing boots. In the sandbox it runs docker compose build, starts the stack, generates an APP_KEY, runs php artisan migrate --force and php artisan db:seed inside the app container (the repository ships UserSeeder, TagSeeder, ReplySeeder and others), and opens the home page, served by HomeController::show, in the sandbox's browser. It records docker image ls for the image size, takes a screenshot of the page, and fixes and rebuilds until the page loads with seeded threads and articles instead of a 500.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Containerize this Laravel app. First read composer.json, composer.lock, package.json, .env.example, config/database.php, the CI workflow and any schema dump, and list the PHP version, PHP extensions, Node version, database and writable paths the app needs. Write a production multi-stage Dockerfile: a Node stage that builds the Vite assets, a Composer stage with --no-dev and an optimized autoloader, and a slim PHP runtime stage with only the extensions the app uses, the database client a schema dump needs, and a non-root user that can write to storage and bootstrap/cache. Add a .dockerignore and a compose.yaml with the app, the same database version CI uses with a health check and a volume, configuration from environment variables, and a scheduler service if the app schedules commands. Build it in the sandbox, start it, set an APP_KEY, run migrations and seeders inside the app container, load the home page in the browser and fix anything until it renders. Open a pull request with the image size, the screenshot, and the commands to run it locally.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on its own branch with four new files: Dockerfile, compose.yaml, .dockerignore, and a short section in the README or a docker/ note with the commands to build, migrate and run. The description lists what the agent read and why each choice follows from it (the PHP 8.4 runtime, the extensions, the MySQL version matched to CI, the database client for the schema dump), the final image size from docker image ls, and the screenshot of the home page served by the container.

The proof is in the session's transcript, which keeps every command with its output: each docker compose build, the container logs, the migrate and db:seed runs, and the browser load of the home page, including any attempt that failed before a fix. Your repository's own workflows run on the pull request too; for an app like laravel.io the Tests workflow runs Pest against MySQL on every pull request, which shows that adding the Docker files changed nothing the suite covers.

Before merging, a reviewer checks that no secret or .env is copied into the image, that the runtime stage carries no Node, Composer or dev packages, that the container runs as a non-root user, and that the MySQL version in compose.yaml matches production, not only CI. If production runs a newer MySQL than 5.7, one follow-up message to the same session changes it and boots the stack again.

Run it on every new service without a Dockerfile

A team moving several Laravel apps to containers can start one run per repository, each a POST /o/{org}/sessions with that repository in resources and the same prompt. Send an Idempotency-Key per repository so a retried request returns the run it already started rather than a second one.

A key runs up to its max_concurrent sessions at once (4 by default), each in its own sandbox, so four apps build and boot in parallel without sharing a Docker daemon. A webhook on the key posts run.completed or run.failed when each turn finishes, with the session_url that holds the result and the pull request.

The use case this job follows, with its automation and trigger: Hand off from your terminal.

Why run it on Rush?

Proving a Dockerfile works means building it, starting a database next to it, migrating and seeding, and loading a page, several times over while the image is wrong. On a laptop that fills the local Docker cache with half-built layers and can collide with a MySQL already on port 3306. In a Rush sandbox the agent has its own Docker, its own database container and its own browser, so the build loop runs where it cannot touch anything else, and the sandbox parks with its disk and memory when the turn ends.

The pull request comes with what makes it trustworthy: the build logs, the migrate output, the screenshot of the page served from the container and the image size, all in the transcript next to the commands that produced them. A reviewer can see that the stack booted from a clean checkout, not on a machine that already had PHP 8.4 and the right extensions installed.

Frequently asked questions

Will it use Laravel Sail?

Not by default. Sail is a development environment, and the prompt asks for a production image: a slim PHP runtime with no dev dependencies. If your team wants Sail for local work too, say so in the input and the agent can add it alongside, but the production Dockerfile stays separate.

Which PHP and MySQL versions does it pick?

The ones your repository already states. For laravel.io that is PHP 8.4 from composer.json and MySQL 5.7 from the CI workflow's service. If production differs from CI, name the version in the input and it uses that instead.

Does it run the queue worker and the scheduler?

It reads routes/console.php and config/queue.php if present. When the app schedules commands, it adds a scheduler service from the same image, and when jobs are queued to a real connection it adds a worker. Either way the pull request says which services exist and why.

Does it need my production secrets?

No. The compose file reads database credentials and the app key from environment variables, and the agent uses throwaway values inside the sandbox to boot the stack. If the app needs a service such as Algolia to render the home page, the agent reports that instead of guessing a key.