How-to

Laravel

Serve a Laravel application from its public/ directory through PHP-FPM, with the Laravel profile’s deny and cache-bypass rules.

Prerequisites

  • ScalWS installed, with include: sites.d/*.yaml in /etc/scalws/scalws.yaml (see Sites).
  • The project deployed outside /home, for example /srv/shop, with composer install done and .env configured. Only /srv/shop/public is served.
  • The project readable by group scalws (sudo chmod -R g+rX /srv/shop with group scalws).
  • PHP-FPM: the distribution’s pool under scalws-controller, or a pool ScalWS manages under scalwsd.service (see the three services).

Let ScalWS confirm the framework first; detect only reads the directory:

scalwsctl detect /srv/shop

What the profile does

profile: laravel gives:

  • a PHP runtime with root <root>/public, front_controller: /index.php, a managed pool of 2 to 16 workers and memory_limit: 256M (used only when the site has no php:);
  • deny rules for /.env, /storage/logs and /vendor (answered 404);
  • cache-bypass rules for the laravel_session and XSRF-TOKEN cookies (used when cache: is set).

Under scalws-controller: an existing PHP-FPM pool

The controller refuses managed pools, so give the pool and the public root yourself; the profile’s rules still apply:

# /etc/scalws/sites.d/shop.example.com.yaml
domain: shop.example.com
root: /srv/shop
profile: laravel
php:
  fastcgi: unix:/run/php/php8.3-fpm.sock
  root: /srv/shop/public
  front_controller: /index.php
https_redirect: true
  • root is the project directory.
  • php.root is the document root: never serve the project root.
  • php.front_controller sends every path that is not a file to public/index.php.

User scalws must be able to connect to the pool’s socket (on Debian and Ubuntu: sudo usermod -aG www-data scalws, then sudo systemctl restart scalws-controller and sudo scalws-controller reload). PHP-FPM’s pool user needs write access to storage/ and bootstrap/cache/.

Under scalwsd.service: a managed pool

The profile supplies the whole runtime:

# /etc/scalws/sites.d/shop.example.com.yaml
domain: shop.example.com
root: /srv/shop
profile: laravel
https_redirect: true

Or written out, to change the pool:

domain: shop.example.com
root: /srv/shop
profile: laravel
php:
  root: /srv/shop/public
  front_controller: /index.php
  fpm:
    workers: {min: 4, max: 16}
    ini: {memory_limit: 256M}
    env: {APP_ENV: production}

The pool clears the environment and passes only php.fpm.env. PHP runs as scalws (or the tenant’s user) inside the unit’s sandbox, where /srv is read-only. Allow Laravel’s writable directories with a drop-in:

# /etc/systemd/system/scalwsd.service.d/writable.conf
[Service]
ReadWritePaths=/srv/shop/storage /srv/shop/bootstrap/cache

Then sudo systemctl daemon-reload && sudo systemctl restart scalwsd.

Apply

sudo scalwsctl check
sudo scalws-controller reload        # under scalws-controller
sudo scalwsctl reload                # under scalwsd.service

Check it works

curl -sI -H 'Host: shop.example.com' http://127.0.0.1/          # 200 from public/index.php
curl -sI -H 'Host: shop.example.com' http://127.0.0.1/.env      # 404
curl -sI -H 'Host: shop.example.com' http://127.0.0.1/up        # Laravel 11 health route
sudo scalwsctl site list

Common problems

  • The project’s files are reachable, for example /composer.json: the PHP root is the project root. Set php.root to public/ (or use the profile without php:).
  • managed php runtimes are not supported with several instances (ADR-0036): under scalws-controller the site needs php.fastcgi.
  • public/.htaccess seems ignored: with htaccess: true, .htaccess files are read below the site’s root (/srv/shop), not below php.root. Laravel’s file only routes to index.php, which front_controller already does, so you do not need it.
  • 500 from Laravel, “Permission denied” in storage/logs: the PHP user cannot write storage/ or bootstrap/cache/.
  • 502: PHP-FPM is not running or its socket does not let scalws connect; see troubleshooting.

See also: applications, profiles.