All web traffic must be routed through index.php — it loads lf-boot/app.php (constants, autoloader, hooks) and calls Url::dispatch(). Everything else in the project root should be unreachable directly.
.htaccess ships with the rewrite rule already in place:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteRule ^ index.php [QSA,L]
</IfModule>
Point your VirtualHost’s document root at the project root (not a public/ subdirectory — Laika serves index.php directly from the root).
Do not hand-write this. nginx.conf in the project root is generated, and
php laika nginx:server prints a complete server block that includes it:
php laika nginx:make # (re)generate nginx.conf
php laika nginx:server --domain=example.com --php=8.3 # full server block
nginx.conf holds the deny rules and the front-controller rewrite:
location / {
if ($http_authorization != "") {
set $auth_header $http_authorization;
}
rewrite ^ /index.php last;
}
Every request is rewritten to index.php, including ones that map to a real file, so
the framework decides what may be served rather than nginx handing files out before PHP
runs. The cost is that static assets go through PHP instead of nginx’s static path.
The server block must define
location = /index.php. That is what terminates the rewrite above — without it nginx loops and returns 500. It also means no PHP file other than the front controller can ever be executed directly.php laika nginx:serveremits it for you; keep theincludeofnginx.confabove it.
composer install --no-dev --prefer-dist --optimize-autoloader — production install, no dev dependenciesphp laika secret:fix — ensure lf-storage/keys/app.key exists (already runs on composer install via post-autoload-dump, but confirm)DEBUG to false in lf-inc/const.php before going live — it’s true by default for local developmentMEMORY_LIMIT / CLI_MEMORY_LIMIT in lf-inc/const.php for your workloadlf-config/database.php (and redis.php/memcached.php/mail.php if used) at production credentials — never commit real credentialsphp laika app:migrate — create/update tables (your own App\Schema classes plus any installed package schemas, e.g. laika-auth’s auth_tokens, laika-queue’s job tables)php laika app:sync — ensure uploads/ exists and .htaccess is in placesecure/samesite appropriately for HTTPS in session and auth configlf-cache/, lf-logs/, lf-storage/, and uploads/ are writable by the web server user, and not publicly reachable over HTTPlaika and worker exist in the project root — they are regenerated by the post-autoload-dump script on every composer install, see InstallationThe worker executable (see Queue) is a long-running process, not a cron job — keep it alive with a process supervisor:
; /etc/supervisor/conf.d/laika-queue-worker.conf
[program:laika-queue-worker]
command=php /path/to/project/worker default
directory=/path/to/project
autostart=true
autorestart=true
numprocs=2
stopsignal=TERM
For more than one queue, add a separate program block (or numprocs group) per queue name. The worker self-restarts on memory pressure — supervisor/systemd just needs to bring it back up when it exits.
.git/, docs/ (optional — harmless but unnecessary on the server)lf-storage/keys/app.key should be generated on the server, not copied from your dev machine, unless you specifically need the same key across environmentslf-cache/ — regenerate it on deploy, don’t ship stale compiled Twig templatesThe framework and each laikait/* package run PHP 8.1–8.5 compatibility checks and lint on every push via GitHub Actions (.github/workflows/test.yml), and build/publish a release artifact on every v*.*.* tag (.github/workflows/release.yml). See each package’s own workflow files for the exact matrix.