PHPackages                             provydon/laravel-scale - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [DevOps &amp; Deployment](/categories/devops)
4. /
5. provydon/laravel-scale

ActiveLibrary[DevOps &amp; Deployment](/categories/devops)

provydon/laravel-scale
======================

Scale your Laravel app. A set of libraries: Laravel Octane (FrankenPHP), Docker, and a stateless web + worker setup for Render, Fly.io, Railway.

1.17.0(2mo ago)18227↓66.7%1MITPHPPHP ^8.2CI passing

Since Feb 24Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/provydon/laravel-scale)[ Packagist](https://packagist.org/packages/provydon/laravel-scale)[ RSS](/packages/provydon-laravel-scale/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (18)Used By (0)

Laravel Scale
=============

[](#laravel-scale)

[![GitHub release (latest SemVer)](https://camo.githubusercontent.com/1032238e51e830106a570dc93e0d4cdcabd3f2986a567157f6bdbf0d333d89b0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f70726f7679646f6e2f6c61726176656c2d7363616c653f7374796c653d666c61742d737175617265)](https://github.com/provydon/laravel-scale/releases)

Scale your Laravel app with one install: it comes with **Laravel Octane (FrankenPHP)**, a production-ready Docker setup, and a stateless web + worker layout that easily runs your Laravel app on Render, Laravel Cloud, Fly.io, Railway, **AWS ECS &amp; EKS**, **Google GKE &amp; Cloud Run**, and other container platforms.

---

[![Buy me a coffee](https://camo.githubusercontent.com/ee9441e9a0ef5dbb47f33a8cc4343f1d1e5a8f83c92557239d0adb11aceae43e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275795f6d655f615f636f666665652d4646444430303f7374796c653d666f722d7468652d6261646765266c6f676f3d6275792d6d652d612d636f66666565266c6f676f436f6c6f723d626c61636b)](https://buymeacoffee.com/provydon)

---

Why You Need Laravel Scale
--------------------------

[](#why-you-need-laravel-scale)

Most Laravel apps run on a single server with fixed PHP-FPM workers—often just one or two processes serving all requests. That works until traffic spikes (launch, campaign, viral moment). Then requests queue, response times blow up, users hit timeouts, and revenue is lost. You can scale up the server, but you still have the same one or two processes and there's always a CPU and memory ceiling.

Cloud platforms scale differently: they replicate your app across many instances and distribute traffic automatically (**horizontal autoscaling**). Laravel isn't ready for that out of the box. You need containerization, web + worker separation, stateless sessions, external cache/queues/storage, and a runtime like Octane. Getting that right takes time.

**Laravel Scale gives you that autoscaling setup with just one install—no Kubernetes knowledge required.** Your app will run in Docker, deploy to platforms like Render, AWS, GCP, Railway, and autoscale with demand instead of collapsing under spikes.

Move from **Old Fixed server capacity** → **New Cloud-native autoscaling.** If you're building something that could spike, go viral, or serve millions and want to stay in Laravel. This helps your Laravel app be ready when it happens.

---

Getting started
---------------

[](#getting-started)

In your project folder on your local machine, download the package and run `scale:install`—that's it.

```
composer require provydon/laravel-scale --dev --with-all-dependencies
php artisan scale:install
```

Next, commit and push the files and folders the command added to your project—they're needed for autoscaling:

- `docker/`
- `.dockerignore`
- `app/Providers/ForceHttpsServiceProvider.php`
- `app/Http/Middleware/ForceHttpsMiddleware.php`
- `bootstrap/providers.php`
- `bootstrap/app.php`
- `config/octane.php`

Deploying on a Cloud Platform (eg Render.com)
---------------------------------------------

[](#deploying-on-a-cloud-platform-eg-rendercom)

### 1. Create a Web Service (Docker)

[](#1-create-a-web-service-docker)

1. In [Render Dashboard](https://dashboard.render.com), click **New +** → **Web Service**.
2. Connect your repo (GitHub/GitLab).
3. Configure:
    - **Name**: e.g. `myapp-web`
    - **Region**: choose one
    - **Environment**: **Docker**
    - **Dockerfile Path**: **`docker/Dockerfile`** (required—set in **Advanced** if not visible).
    - **Instance Type**: pick size (e.g. Starter or higher).
    - Leave the rest default or set if you need to.
4. **Environment variables** (Add Environment Variable):
    - `APP_KEY` (generate with `php artisan key:generate --show` locally).
    - `APP_ENV=production`, `APP_DEBUG=false`. If you leave `APP_ENV=local` or leave it unset, the app may generate `http://` asset URLs and the page can appear blank (Mixed Content blocked by the browser).
    - `APP_URL` set to your production URL with `https://` (e.g. `https://myapp.onrender.com` or your custom domain). Use your own domain; platform defaults like `*.onrender.com` can cause session issues.
    - `APP_NAME`: if the name contains spaces, wrap the value in an extra single quotation so it is parsed correctly, e.g. `APP_NAME='"Digitalize with AI"'`.
    - `DEPLOYMENT_TYPE=web`.
    - Database: `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`. Use any database (PostgreSQL, MySQL, SQLite, etc.); on Render you can add a PostgreSQL instance and copy its env vars.
    - Stateless: For **session**, set only these three and remove any other `SESSION_*` from `.env.example` and your platform env so Laravel defaults apply (avoids session issues when deployed): `SESSION_DOMAIN=.example.com` (replace with your root domain, e.g. `.yourdomain.com`), `SESSION_DRIVER=database`, `SESSION_LIFETIME=120`. Set **`LOG_STACK=single,stderr`** so logs go to both file and stderr (your platform can capture stderr). Also set `CACHE_STORE=database`, `QUEUE_CONNECTION=database`, and if using S3: `FILESYSTEM_DISK=s3`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET`.
    - Optional—broadcasting: `BROADCAST_CONNECTION=pusher` (or reverb/ably) and driver-specific env vars (e.g. `PUSHER_APP_ID`, `PUSHER_APP_KEY`, etc.) on both Web and Worker.
5. **Port**: set **Port** to **8000** (Octane listens on 8000).
6. Save and deploy. Render will build the Docker image and start the web service.

**Set `APP_URL` correctly.** Use either the URL your platform gives you (e.g. `https://myapp.onrender.com`) or your custom domain (e.g. `https://app.yourdomain.com`). If `APP_URL` is wrong, the app can load over HTTPS but request assets over HTTP. The browser will block those requests (Mixed Content), and the page will appear blank—e.g. *"The page at '' was loaded over HTTPS, but requested an insecure resource '...'. This request has been blocked."* Fix it by setting `APP_URL` to the exact public URL (including `https://`) in the Web Service environment variables, then redeploy.

If **`npm run build` fails** (e.g. Vite/Wayfinder errors): add a Docker build argument **`SKIP_FRONTEND`** = **`1`** in the service's Environment so the image builds without frontend assets. See **docker/README.md** for details.

### 2. Create a Worker Service (optional)

[](#2-create-a-worker-service-optional)

1. **New +** → **Background Worker**.
2. Same repo; **Environment**: **Docker**.
3. **Dockerfile Path**: **`docker/Dockerfile`** (same as Web).
4. **Build Command** (optional, for slimmer image): ```
    docker build -f docker/Dockerfile --build-arg DEPLOYMENT_TYPE=worker -t app-worker:latest .
    ```
5. **Start Command**: leave empty (entrypoint uses `DEPLOYMENT_TYPE` to start queue + scheduler).
6. **Environment variables**: same as web (DB, Redis if used, `APP_KEY`, etc.) plus:
    - `DEPLOYMENT_TYPE=worker`
7. No port needed. Deploy.

### 3. Render-specific env vars

[](#3-render-specific-env-vars)

Render injects some vars automatically (e.g. `RENDER_EXTERNAL_URL`, `RENDER_INSTANCE_ID`). The Docker entrypoint skips copying `RENDER_*` from host env into `.env` so they don't overwrite; your app can still read them from `getenv()` or `$_ENV` if needed.

### 4. Database

[](#4-database)

Use any database Laravel supports: **PostgreSQL**, **MySQL**, or **SQLite**. The Docker image includes all three drivers by default—no Dockerfile edits needed. Set `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, and `DB_PASSWORD` in your Web and Worker services. On Render, create a PostgreSQL or MySQL instance (or use SQLite for a single instance) and add its connection vars.

> **Important: make your database reachable and grant access**
>
> Your app runs inside containers. For migrations and normal requests to work, two things must be true.
>
> **1. The database must be reachable from your containers.**
> If the DB runs on a separate host (managed service or your own server), it has to accept connections from the internet or your platform's private network. The right **port must be open**: **3306** for MySQL, **5432** for PostgreSQL. On Render, attaching a managed PostgreSQL or MySQL instance does this for you. For a self-hosted or external DB, open that port in the firewall and allow your platform's outbound IPs (or put the DB and app on a private network).
>
> **2. The database user must have access to the database.**
> The user in `DB_USERNAME` must be allowed to connect to the host and must have privileges on the database named in `DB_DATABASE` (e.g. `SELECT`, `INSERT`, `UPDATE`, `DELETE`, and for migrations `CREATE`, `ALTER`, etc.). On managed services this is usually set when you create the DB and user; on your own server, grant the user access to the database and ensure it can connect from the app's network.
>
> **If the container fails to start or you see "No open ports":** Check the deploy logs. The entrypoint runs migrations and other steps before starting the web server; if **migration fails** or another step fails, the container exits and no port is opened. The entrypoint prints a clear error (e.g. "ERROR: Migration failed…") and a short hint on common causes (database not reachable, DB user has no access, missing env vars). Fix the cause (open port 3306/5432, grant the user access to the database, or set APP\_KEY and DB\_\*), then redeploy.

### 5. Redis or key-value cache (optional)

[](#5-redis-or-key-value-cache-optional)

If you use Redis for cache, session, or queue, add the PHP Redis extension to the Dockerfile first: in the `install-php-extensions` block, add `redis \` on its own line before the other extensions. Then deploy a Redis or key-value cache service (e.g. Render's Redis add-on, Upstash, or similar) and point the web service at it via env configs. Set `CACHE_STORE=redis`, `SESSION_DRIVER=redis`, and/or `QUEUE_CONNECTION=redis`, then add the connection vars (`REDIS_HOST`, `REDIS_PASSWORD`, `REDIS_PORT`, etc.) to your Web and Worker services. No code changes—Laravel reads these from the environment.

### 6. Custom domain and HTTPS

[](#6-custom-domain-and-https)

Use **your own domain or subdomain** for the app instead of the platform's default (e.g. `*.onrender.com`). Platform default domains can cause session handling issues and sessions may invalidate unexpectedly. Add your domain in the Web Service → **Settings** → **Custom Domains**; Render provides TLS. **Set `APP_URL` to the exact URL your app's DNS points to** (e.g. `https://app.yourdomain.com`). When the app is behind a load balancer, `APP_URL` must match the public URL or links, redirects, and assets can break.

`scale:install` adds **`trustProxies(at: '*')`** and **`statefulApi()`** to `bootstrap/app.php` so that behind a reverse proxy (e.g. Render) the app uses the forwarded protocol and generates `https://` asset URLs. Without this, the page can load over HTTPS but request CSS/JS over HTTP (mixed content), and the frontend may appear blank.

---

Contents
--------

[](#contents)

- [After install](#after-install)
- [What it does](#what-it-does)
- [Typical dev journey](#typical-dev-journey)
- [Local development](#local-development)
- [Contributing (install from local path)](#contributing-install-from-local-path)
- [Support](#support)

---

After install
-------------

[](#after-install)

1. **Octane in production**
    `scale:install` adds or moves **`laravel/octane` into `require`** in your `composer.json` so the Docker image (which runs `composer install --no-dev`) gets Octane. The package is in `require-dev` so production’s `composer install --no-dev` won’t install it; the published files run in production.
2. **Database**
    The Docker image includes **MySQL**, **PostgreSQL**, and **SQLite** drivers by default (`pdo_mysql`, `pdo_pgsql`, `pdo_sqlite`). Set `DB_CONNECTION` and your DB\_\* env vars (e.g. on Render) and it works. **See the “Database” section under *Deploying on a Cloud Platform* above** for essential requirements: your database must be reachable from your containers (correct ports open) and the DB user must have access to the database.
3. **Stateless setup**
    In Render (and `.env.example`), set:

    - **Session**: Remove all existing `SESSION_*` variables, then set only `SESSION_DOMAIN=.example.com` (replace with your root domain, e.g. `.yourdomain.com`), `SESSION_DRIVER=database`, and `SESSION_LIFETIME=120`. Leave other session settings as Laravel defaults so sessions work properly in this stateless environment (see **docker/README.md** for details).
    - **Logging**: Set **`LOG_STACK=single,stderr`** so the platform can capture logs from stderr.
    - **Cache**: `CACHE_STORE=database` (or `redis`).
    - **Files**: `FILESYSTEM_DISK=s3` and AWS\_\* (or other external disk).
    - **Queue**: `QUEUE_CONNECTION=database` (or `redis`) for the worker service.
4. **Docker**

    - Web: build with `DEPLOYMENT_TYPE=web`, expose port **8000**.
    - Worker: same image with `DEPLOYMENT_TYPE=worker`, or build with `--build-arg DEPLOYMENT_TYPE=worker` for a smaller image.
5. **Render**

    - Web: Docker service, build from repo, start command = container default (entrypoint), port 8000.
    - Worker: second Docker service, same image, `DEPLOYMENT_TYPE=worker`, no port.

### Why separate web and worker-scheduler services?

[](#why-separate-web-and-worker-scheduler-services)

You need **both** a web service and a **worker-scheduler** service (Background Worker on Render):

- **Scheduler**: Running the scheduler (`schedule:work`) on a single dedicated worker avoids race conditions. If every web container ran the scheduler, multiple instances could trigger the same task at once (e.g. duplicate emails or cleanup jobs).
- **Queue and HTTP**: Running `queue:work` and the scheduler on the worker keeps background work off the web processes. That way web containers stay focused on handling requests instead of being slowed or blocked by queued jobs and cron.

See **docker/README.md** (published into your app) for the full stateless checklist, build commands, **PHP version** (how the image picks PHP 8.2–8.5 and how to pin a version), **database** (the image includes MySQL, PostgreSQL, and SQLite drivers by default—no Dockerfile edits needed), and **backend-only apps** (how to remove the Node/frontend stage if your app is API-only).

What it does
------------

[](#what-it-does)

`scale:install` publishes into your app:

- **docker/** — Dockerfile (PHP + Node frontend stage; MySQL, PostgreSQL, SQLite drivers; PostCSS/Tailwind support), entrypoint, `supervisord-web.conf` (Octane), `supervisord-worker.conf` (queue + scheduler), php.ini
- **.dockerignore** — keeps build context small
- **app/Providers/ForceHttpsServiceProvider.php** — forces `https://` for URLs in non-local environments (so production works behind a reverse proxy without Mixed Content)
- **app/Http/Middleware/ForceHttpsMiddleware.php** — runs at the start of the web stack so asset/Vite URLs use `https://` (avoids blank or unstyled pages from Mixed Content)
- **docker/README.md** — stateless checklist (session/cache in DB or Redis, files on S3), PHP version, database (all three drivers by default), backend-only variant

**Requirements:** PHP ^8.2 (Laravel 13 needs PHP ^8.3 in your app), Laravel ^11.0|^12.0|^13.0, laravel/octane ^2.17 (FrankenPHP). Run `composer update` in your app to pull compatible versions.

Typical dev journey
-------------------

[](#typical-dev-journey)

1. **Local setup** – Create your Laravel app, develop as usual (Blade, Inertia, API, etc.).
2. **Install once** – When ready to deploy: `composer require provydon/laravel-scale --dev --with-all-dependencies` and `php artisan scale:install`.
3. **Commit** – Commit `docker/`, `.dockerignore`, `app/Providers/ForceHttpsServiceProvider.php`, `app/Http/Middleware/ForceHttpsMiddleware.php`, `bootstrap/providers.php`, `bootstrap/app.php`, `config/octane.php`, and the `.gitignore` changes. Push to GitHub/GitLab.
4. **Stateless config** – In `.env.example` (and your platform’s env), remove all other `SESSION_*` and set only `SESSION_DOMAIN=.example.com` (use your root domain), `SESSION_DRIVER=database`, `SESSION_LIFETIME=120`; set **`LOG_STACK=single,stderr`**; set `CACHE_STORE=database`, `QUEUE_CONNECTION=database`. Use S3 for uploads. Add Redis extension to Dockerfile if using Redis. See **docker/README.md** for the full session/deployment checklist.
5. **Platform** – Create a Web Service (Docker) and a Background Worker on Render (or similar). Point both at your repo. Set **Dockerfile Path** to `docker/Dockerfile` for each. Add a database (e.g. PostgreSQL on Render) and set env vars, deploy.
6. **Iterate** – Push code; platform rebuilds from the repo. No `scale:install` in CI—everything is already in the repo.

**Local testing (optional):** Run `docker build -f docker/Dockerfile --build-arg DEPLOYMENT_TYPE=web -t app:latest .` and `docker run -p 8000:8000 -e APP_KEY=base64:xxx -e DB_*="..." app:latest` to test the image locally.

---

Local development
-----------------

[](#local-development)

Use Laravel Octane as usual, e.g.:

```
php artisan octane:start --server=frankenphp
```

Or your existing `composer dev` / `npm run dev` setup; the package only adds Docker and publishables for deployment.

Contributing (install from local path)
--------------------------------------

[](#contributing-install-from-local-path)

If you’re developing this package or want to try it from a local clone, in your Laravel app’s `composer.json` add:

```
"repositories": [
  { "type": "path", "url": "/path/to/laravel-scale" }
],
"require-dev": {
  "provydon/laravel-scale": "@dev"
}
```

Then run `composer update provydon/laravel-scale --with-all-dependencies` and `php artisan scale:install`.

**When you upgrade the package** (e.g. `composer update provydon/laravel-scale --with-all-dependencies`), run `php artisan scale:install` again to publish the latest Docker files and fixes, then commit any changed files.

This will:

1. Publish `docker/` (Dockerfile, Dockerfile.backend, entrypoint, supervisor configs, php.ini). Then **ask** whether your app has a frontend (Vite, Blade with assets, etc.); if no (or you pass `--no-frontend`), the main `docker/Dockerfile` is set to the backend-only version (no Node stage). Both variants are in `docker/`; you can switch later by overwriting `Dockerfile` with `Dockerfile.backend` or re-running the install.
2. Publish `.dockerignore`.
3. Run `octane:install --server=frankenphp` (unless you pass `--no-octane`).
4. Update `.gitignore` so `docker/` and `config/octane.php` are committed (unless you pass `--no-gitignore`). Update `.env.example` so only the three stateless session vars are set (unless you pass `--no-env-example`).
5. If you use **Laravel Wayfinder**: remove the Wayfinder plugin from `vite.config.*` (so `npm run build` in Docker doesn't run PHP) and add `!resources/js/routes/`, `!resources/js/actions/`, and `!resources/js/wayfinder/` to `.gitignore` so generated files are committed. Run `php artisan wayfinder:generate` locally and commit the output before deploying.

**Choosing full vs backend-only Dockerfile**

The install publishes two Dockerfiles: **`docker/Dockerfile`** (full: Node + PHP, builds Vite/assets) and **`docker/Dockerfile.backend`** (PHP only, no Node). It then sets which one is the *main* `docker/Dockerfile`:

- **Interactive:** You are asked *"Does this app have a frontend in the same repo (React, Vue, Svelte, Vite, etc.)?"* — **Yes** (default) keeps the full Dockerfile; **No** overwrites `docker/Dockerfile` with the backend-only version. Press Enter for the default (full stack).
- **`--no-frontend`:** Skips the question and uses the backend-only Dockerfile (for API-only apps).
- **Non-interactive (e.g. CI):** No question is asked; the full Dockerfile is kept.

You can switch later by overwriting `docker/Dockerfile` with `docker/Dockerfile.backend`, or re-run `scale:install` and answer the question to restore the full one.

Options:

- `--no-octane` – Only publish files; don't run Octane install.
- `--no-dockerignore` – Don't overwrite `.dockerignore`.
- `--no-gitignore` – Don't update `.gitignore` (ensures `docker/` and `config/octane.php` are committed).
- `--no-env-example` – Don't update `.env.example` (keeps existing session vars; install normally strips other `SESSION_*` and sets only `SESSION_DOMAIN`, `SESSION_DRIVER`, `SESSION_LIFETIME`).
- `--no-wayfinder` – Skip Wayfinder Vite and .gitignore adjustments.
- `--no-frontend` – Use the backend-only Dockerfile (no Node/Vite); for API-only apps. Without this, `scale:install` asks whether your app has a frontend and sets `docker/Dockerfile` accordingly (full or backend-only).

Support
-------

[](#support)

Enjoying Laravel Scale? [Buy me a coffee](https://buymeacoffee.com/provydon) — it helps keep this project maintained.

License
-------

[](#license)

MIT.

###  Health Score

47

—

FairBetter than 94% of packages

Maintenance88

Actively maintained with recent releases

Popularity25

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

17

Last Release

60d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c596d5c1b7f297daa7dc1398beae75ea8f55ba06a89c2ef371cf3116df471673?d=identicon)[provydon](/maintainers/provydon)

---

Top Contributors

[![provydon](https://avatars.githubusercontent.com/u/31216637?v=4)](https://github.com/provydon "provydon (68 commits)")

---

Tags

laraveldockerscaleoctanefrankenphprenderproduction

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/provydon-laravel-scale/health.svg)

```
[![Health](https://phpackages.com/badges/provydon-laravel-scale/health.svg)](https://phpackages.com/packages/provydon-laravel-scale)
```

###  Alternatives

[laravel/octane

Supercharge your Laravel application's performance.

4.0k21.5M159](/packages/laravel-octane)[downtoworld/laravel-devops

Laravel Cloudflare-Tunnels Ready Production Docker-Compose

161.1k](/packages/downtoworld-laravel-devops)[neo/little-sail

Smaller Alpine based image for Laravel Sail runtimes

389.0k](/packages/neo-little-sail)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
