Cookies ahead

Our support chat tool "Intercom" would like to collect some more data on you. See the related link for more details.

Docs

Deploying Laravel on fortrabbit

Reviewed

Created

#webdev

Markdown ↓

🚢

Ship Laravel.

Deploy a Laravel application to fortrabbit through GitHub — environment configuration, build commands, post-deploy migrations, queues and zero-downtime swaps. If you have shipped Laravel elsewhere, most of this will look familiar; we point out the differences as we go.

Laravel deployment is a solved problem on most platforms — and yet every host has its own quirks, build timing and limits. This guide is the short version of "how do I deploy a Laravel app to fortrabbit and not regret it later". We cover the path from a fresh repository to a production app with assets and queues.

This guide was written with Laravel 13 and Laravel 12 in mind. Have a look at our updated Laravel guides too.

Before you begin

You need a Laravel application that runs locally, a GitHub account, a fortrabbit account, and the fortrabbit GitHub App installed against the repos you want to deploy. The GitHub App is what turns a git push to your branch into a deploy on fortrabbit.

If you have not connected GitHub yet, install the app, then create a fortrabbit app from the repo. Each branch you connect maps to its own environment — keep main for production and add a develop environment for staging if you need one.

The rest of this post assumes the GitHub App is connected and your repo's main branch is wired to a production environment.

How fortrabbit handles a Laravel deployment

A push to the connected branch on GitHub triggers a deployment on fortrabbit. The deployment service pulls the new commit, runs your build commands (Composer, npm, anything else you configure), runs post-deploy commands, and syncs the result into the environment's storage. The previous build keeps serving traffic until the new one is ready.

A few consequences fall out of that:

  • Anything that needs to persist across deploys must live in storage/ (Laravel already does this for logs, sessions and cached views by default).
  • Build-time secrets do not need to be in your repo — they live in the app's environment variables.
  • The .env file you use locally is not copied to fortrabbit. Use the dashboard's environment variable editor instead.
  • Code travels in one direction only: from GitHub to fortrabbit. Files you change via SSH are not pulled back into Git.

Configure your environment

Laravel reads configuration from environment variables via config/*.php. On fortrabbit, set these in the ENV vars editor in the dashboard. The environment variables docs cover the full lifecycle, including the difference between regular and secret values.

The minimum set for a Laravel app:

APP_KEY=base64:...
APP_ENV=production
APP_DEBUG=false
APP_URL=https://my-app.eu-1wa.frbit.app
LOG_CHANNEL=errorlog
SESSION_DRIVER=database
CACHE_DRIVER=database
QUEUE_CONNECTION=database
env

Generate APP_KEY once with php artisan key:generate --show locally and paste the result into the dashboard. Do not commit it. Set APP_URL to your environment's frbit.app hostname for now; switch it to your own domain once DNS is pointed.

LOG_CHANNEL=errorlog writes Laravel logs to the PHP error log, which fortrabbit collects and surfaces in the dashboard and over SSH. The default stack channel writes to storage/logs/laravel.log, which works but is harder to follow when you are watching multiple processes.

For the database, fortrabbit injects MYSQL_* environment variables automatically when the MySQL component is attached. Map them in config/database.php rather than copy-pasting credentials.

// config/database.php
'mysql' => [
    'host' => env('MYSQL_HOST', '127.0.0.1'),
    'port' => env('MYSQL_PORT', '3306'),
    'database' => env('MYSQL_DATABASE'),
    'username' => env('MYSQL_USER'),
    'password' => env('MYSQL_PASSWORD'),
    // …
],
php

The point of doing it this way: when you scale, swap or add an environment, the credentials change but your code does not.

Your first Laravel deploy

The flow is the same as any other GitHub-based hosting, with one extra step at app creation time:

# locally — initialize the repo and push to GitHub
git init
git add -A
git commit -m "Init"
gh repo create --source=. --private --push
bash

Then in the fortrabbit dashboard, create a new app, choose the Laravel software preset, and pick the GitHub repo and branch you pushed. The first deploy runs immediately and will be slower than the rest — Composer has no cache to lean on. The build log streams in the dashboard.

If your app needs PHP extensions beyond the defaults (Imagick, BCMath, GD …), declare them in composer.json under require:

{
  "require": {
    "php": "^8.3",
    "ext-bcmath": "*",
    "ext-gd": "*"
  }
}
json

The PHP component docs list every extension that ships out of the box.

Build commands

The build commands step runs after the deployment service has pulled the latest commit and before the build is synced into the environment. fortrabbit auto-detects composer install from composer.json and npm run build from package.json — for most Laravel apps with Vite or Mix, the defaults are correct.

If you need to customize, edit them in the dashboard per environment:

composer install --no-dev --prefer-dist --no-interaction --no-ansi --no-progress
npm ci
npm run build
bash

Node.js is available during the build step, so compiling front-end assets happens here — no GitHub Actions or pre-commit build is needed. Built files (Vite's public/build, Mix's public/css and public/js, or wherever your bundler writes) are part of the deploy package and end up in the environment alongside your PHP code.

Post-deploy commands

Post-deploy commands run after the build is synced into the environment. This is where Laravel's optimization steps belong: caching config, routes and views, and running migrations.

Set them in the dashboard under the deployment settings of the environment:

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan migrate --force
bash

A few notes on this list:

  • --force is required for migrate because the deploy environment is non-interactive. Laravel will refuse without it.
  • config:cache reads your .env once at build time and freezes it into a compiled file. Any environment variable change after that needs a redeploy or a manual config:clear.
  • view:cache and route:cache are safe and worthwhile in production — they cut request latency noticeably.

If a post-deploy command fails, the deployment is marked failed; the previous build keeps serving traffic. Fix the cause, push again.

Queues, schedulers and cron jobs

Laravel's scheduler expects a single cron entry that calls schedule:run every minute. On fortrabbit, set this up via cron jobs in the dashboard:

ScheduleCommand
Every minutephp artisan schedule:run

For queue workers, fortrabbit supports long-running PHP processes via workers. Workers are an optional component — book one for your app first, then add a worker with the command:

php artisan queue:work --sleep=3 --tries=3 --max-time=3600
bash

--max-time=3600 restarts the worker hourly, which keeps memory usage predictable and picks up code changes after a deploy without manual intervention.

If you would rather not book a worker, the cron-based fallback works for low-volume queues:

* * * * * php artisan queue:work --stop-when-empty --max-time=55
bash

This processes whatever is in the queue once a minute and exits. Not real-time, but adequate for most transactional emails and webhook fan-out.

When to reach for GitHub Actions

The deployment service handles Composer and Node out of the box. GitHub Actions is still useful when you want something the deployment service does not do for you:

  • Run your test suite on every push and only deploy if it passes
  • Lint, type-check or run static analysis as a gate
  • Build artifacts that depend on tooling fortrabbit's build environment does not include

A common pattern: run your CI on pull_request and push, and let the fortrabbit GitHub App handle the actual deploy when CI passes and the branch updates. The GitHub integration docs cover the workflow patterns; our GitHub Actions blog post walks through a CI-then-deploy example.

Zero-downtime deployments

The deployment service swaps builds atomically — the previous build serves traffic until the new build is ready, then the switch flips. For most Laravel apps, that is the zero-downtime story. The deployment strategy docs go deeper into the merge and replace strategies and what guarantees they offer.

The two cases where you can still see brief errors:

  1. Schema-breaking migrations. A request hitting a model whose underlying column was renamed mid-migration will throw. The fix is a two-step migration: deploy the additive change first (add the new column, write to both), backfill, then deploy the removal of the old column. Laravel's migration files give you the structure for this; your discipline gives you the result.
  2. Cached config drift. If you change config/*.php and ship without running config:cache in post-deploy, the live app keeps serving the old cached version until something forces a rebuild. Always include config:cache in your post-deploy list.

Troubleshooting

A short list of issues that come up often, and what they mean:

SQLSTATE[HY000] [2002] Connection refused — your app is trying to reach 127.0.0.1:3306 because a fallback in config/database.php hardcoded localhost. Fix the config to read from MYSQL_HOST with no default, and check that the MySQL component is attached.

Permission denied on storage/ — your repo committed files into storage/ with the wrong permissions, or storage/logs/laravel.log exists in git. Add storage/logs/* and storage/framework/{cache,sessions,views}/* to .gitignore, keep only .gitkeep files, and redeploy.

Composer runs out of memory — fortrabbit's build environment has a fixed memory limit. Add COMPOSER_MEMORY_LIMIT=-1 to your environment variables, or audit your dependency tree with composer why-not to find the package pulling in giants.

Asset URLs return 404 after deploy — Vite's manifest is missing because npm run build did not run, or the build output path was overridden. Confirm the build command ran in the deployment log, and that your bundler writes to public/build.

No application encryption key has been specifiedAPP_KEY is missing from your environment variables, or config:cache ran before it was set. Set the key in the dashboard, then redeploy.

These two streams answer 90% of "why is the app behaving this way" questions.

For the complete reference, the Laravel guide covers framework-specific details and edge cases this post does not — including install and deployment steps.

Where to go from here

If you have a Laravel app and an empty fortrabbit account, the practical first move is: install the GitHub App, create a fortrabbit app from your repo, set the environment variables above, and push. Most issues surface in the first deploy, and fortrabbit's error messages are direct enough to fix without a support ticket.

Once that works, the rest is the same Laravel work you already know — caching strategies, queue tuning, schema design. The platform stays out of the way and gives you predictable deploys for the cost of one git push.

Written by AI, lectured by a human.

AI usage policy