> ## Documentation Index
> Fetch the complete documentation index at: https://docs.suga.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy Laravel with MySQL on Suga

> Deploy a Laravel app on Suga with a private MySQL database, automatic builds from GitHub, and a public HTTPS URL.

[Laravel](https://laravel.com/) apps deploy on Suga straight from a GitHub repo, with MySQL running alongside as a private container. Suga detects PHP from your `composer.json`, so no Dockerfile is needed.

This guide explains how to deploy a Laravel app with MySQL in two ways:

1. [Step-by-step via the canvas](#deploy-using-the-canvas)
2. [Using a coding agent](#deploy-using-an-agent)

Your repo needs a standard Laravel layout: a `composer.json` requiring `laravel/framework`, an `artisan` file at the root, the usual `app/`, `config/`, `public/`, and `routes/` folders, and a `.env.example` listing the DB and app config variables you use.

The guide assumes your app reads `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, and `DB_PASSWORD` from the environment (Laravel's default `config/database.php` does), and `APP_KEY`, `APP_ENV`, `APP_DEBUG`, and `APP_URL` for app-level config.

## Deploy using an Agent

To deploy with a coding agent such as [Claude Code](https://claude.com/product/claude-code), [Codex](https://openai.com/codex/), or [OpenCode](https://opencode.ai/), follow these steps:

<Steps>
  <Step title="Connect the Suga MCP">
    If you haven't already, connect your agent to the [Suga MCP server](/agents/mcp).
  </Step>

  <Step title="Ask your agent to deploy">
    Ask your agent to deploy your repo to Suga. Here's a basic prompt you can use, with your repo and branch filled in:

    ```markdown Prompt icon="sparkles" wrap expandable theme={null}
    Deploy this Laravel app on Suga with a MySQL database, using the Suga MCP.
    Repo: <owner/repo>  Branch: <branch>

    Ask which organization to use and create a new project for this deployment. Add two containers:

    - mysql: image `mysql:8.4`, private on port 3306. Set `MYSQL_DATABASE` and `MYSQL_USER` to something matching the app name. Leave `MYSQL_PASSWORD` and `MYSQL_ROOT_PASSWORD` blank and marked sensitive. Add a volume mounted at `/var/lib/mysql`.
    - the app container: no image, private port 8000, public HTTPS endpoint on 8000. Connect the repo as the build source (auto-detect, no Dockerfile).

    On the app container, set:
    - `PORT=8000` as a literal so Caddy binds to the container's exposed port.
    - `DB_CONNECTION=mysql`, `DB_HOST=mysql`, `DB_PORT=3306`, `DB_DATABASE=<mysql-database>`, `DB_USERNAME=<mysql-user>` as literals.
    - `DB_PASSWORD` as a reference to the mysql container's `MYSQL_PASSWORD`.
    - `APP_ENV=production`, `APP_DEBUG=false`, `LOG_CHANNEL=stderr` as literals.
    - `APP_URL` to `https://{{<app-container-id>.variables.SUGA_PUBLIC_HOSTNAME}}`.

    Suga's Laravel auto-detect runs migrations, `storage:link`, and `php artisan optimize` at boot before starting FrankenPHP, so no start command override is needed.
    ```

    The agent will give you a link to the new environment on the Suga canvas. Open it to review the setup.
  </Step>

  <Step title="Set the sensitive values">
    Set `MYSQL_PASSWORD` and `MYSQL_ROOT_PASSWORD` on the mysql container: click each value input and paste values generated with `openssl rand -hex 32`. On the app container, add `APP_KEY` as a **Sensitive** env var (generate one locally with `php artisan key:generate --show` and paste the value).

    <Note>
      Save all three values somewhere secure, you'll want them later.
    </Note>
  </Step>

  <Step title="Apply to deploy">
    Click **Apply** in the top right. Suga clones the app repo, runs `composer install`, applies migrations, links storage, runs `php artisan optimize`, and boots FrankenPHP on port 8000, serving `public/` as the web root. Open the public URL and you'll get your app's home route.
  </Step>
</Steps>

## FAQ

<AccordionGroup>
  <Accordion title="My deploy fails with 'could not find driver' or 'Table sessions doesn't exist'">
    Two Laravel scaffolding gotchas most often show up here:

    * `could not find driver` at runtime means the PDO extension for your database isn't installed in the build. Add `"ext-pdo_mysql": "*"` to `composer.json`'s `require` block (or `ext-pdo_pgsql` for Postgres), run `composer update`, and redeploy. Production Laravel apps that shipped on Heroku almost always have this; fresh Laravel 12+ scaffolds don't.
    * `Table 'sessions' doesn't exist` means `SESSION_DRIVER=database` is set (the fresh-Laravel default in the `.env.example`) but no sessions migration lives in `database/migrations/`. Either run `php artisan session:table` locally to generate the migration and commit it, or set `SESSION_DRIVER=file` on the app container if your app doesn't need database-backed sessions.
  </Accordion>

  <Accordion title="Do I need a Dockerfile to deploy Laravel on Suga?">
    No. Suga detects PHP from your `composer.json` and runs the standard install and start flow. If you already have a Dockerfile, you can tell the agent to use it instead; otherwise auto-detect handles the common case.
  </Accordion>

  <Accordion title="Which PHP versions does Suga support?">
    Suga uses the version pinned in `composer.json` under `require.php`, and defaults to PHP 8.4 when there isn't one.
  </Accordion>

  <Accordion title="How do I set APP_KEY?">
    Generate the key locally with `php artisan key:generate --show` and add the returned string (starts with `base64:`) as a **Sensitive** env var named `APP_KEY` on the app container. Laravel uses it for session encryption and other cryptographic operations, and refuses to run without it.
  </Accordion>

  <Accordion title="How do I run migrations before the app starts?">
    Suga's Laravel auto-detect runs `php artisan migrate --force` at every container boot, so migrations happen without any extra configuration. Storage symlinks and `php artisan optimize` also run automatically.
  </Accordion>

  <Accordion title="Can I skip auto-migrations at boot?">
    Yes. Set `RAILPACK_SKIP_MIGRATIONS=true` as an env var on the app container. Run migrations manually instead — from the container's shell in the dashboard, or from your CI pipeline before you Apply.
  </Accordion>

  <Accordion title="Can I use Laravel Octane?">
    Yes, though FrankenPHP already runs the app as a persistent server. For Swoole or RoadRunner, add `laravel/octane` and the matching extension (`ext-swoole`) to `composer.json`, then pass `RAILPACK_START_CMD=php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000` as a build arg.
  </Accordion>

  <Accordion title="How do I run queue workers or scheduled tasks?">
    Add a second app container on the canvas with the same repo but a different start command, for example `RAILPACK_START_CMD=php artisan queue:work` for a worker or `RAILPACK_START_CMD=php artisan schedule:work` for the scheduler. Set the same env vars and leave it with no public endpoint. For Redis-backed queues, add a Redis container from the template and set `QUEUE_CONNECTION=redis` with a reference to the Redis password.
  </Accordion>

  <Accordion title="What if my asset pipeline uses Vite or Mix?">
    Nothing to do for Vite. When your repo has a `package.json`, Suga installs the Node dependencies and runs its `build` script as part of the build. For Mix, whose script is usually named something else, point `RAILPACK_BUILD_CMD` at it as a build arg, for example `npm run production`. Compiled assets land in `public/build/` and FrankenPHP serves them alongside the app.
  </Accordion>

  <Accordion title="Where do uploaded files go?">
    Configure `FILESYSTEM_DISK` and mount a managed volume at `/var/www/html/storage/app` (or wherever your app writes files). For public assets on a multi-replica deployment, an S3-compatible bucket via Laravel's `s3` disk is a better fit than local storage.
  </Accordion>

  <Accordion title="Can I use a database other than MySQL?">
    Yes. Swap the `mysql:8.4` container for a MariaDB or Postgres image. Update `DB_CONNECTION`, the driver-specific env vars, and the PHP extension in your `composer.json` (`ext-pgsql` for Postgres, and so on) to match.
  </Accordion>

  <Accordion title="Can I use a custom domain?">
    Yes. Attach your domain to the app container in the dashboard once the app is deployed. Update `APP_URL` to the new domain so signed URLs and email links resolve correctly.
  </Accordion>

  <Accordion title="What does deploying Laravel plus MySQL cost on Suga?">
    The Free tier fits a small Laravel plus MySQL stack, including the build history and volume. Pro is per-seat with hosting credits that offset compute and storage. Full pricing at [suga.app/pricing](https://suga.app/pricing).
  </Accordion>
</AccordionGroup>
