Deployment

Guides

Deployment

SERIXA apps are plain PHP: a front controller in public/ that renders HTML and returns it. Anything that can run PHP 8.2+ and serve a folder can host one. This page covers local serving, a real web server, and the CSS story for production.

The one rule: serve from public/

Every generated app puts index.php, app.css, and assets/runtime/serixa.js under public/. Your web server's document root must point at that folder — not the app root — or the runtime script and stylesheet will 404.

blog/
  public/        ← document root goes here
    index.php
    app.css
    assets/runtime/serixa.js
  pages/
  layouts/
  vendor/        ← keep this OUTSIDE the document root

Local development: php -S

php -S localhost:8000 -t public

-t public sets the document root. This is what composer install sets up as the start script in generated apps:

composer start

Good for development; PHP's built-in server is single-threaded and not meant for production traffic.

Apache

Point the vhost's DocumentRoot at public/, and route unmatched requests to index.php (SERIXA apps read $_GET['page']-style routing, so you generally don't need a catch-all rewrite unless your app uses path-based URLs):

<VirtualHost *:80>
    ServerName blog.example.com
    DocumentRoot /var/www/blog/public

    <Directory /var/www/blog/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

If you want clean paths (/articles instead of /?page=articles), add a public/.htaccess that rewrites everything to index.php and read $_SERVER['PATH_INFO'] (or similar) in your own routing code — SERIXA has no opinion here, since it ships no router.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

nginx

server {
    listen 80;
    server_name blog.example.com;
    root /var/www/blog/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.(?!well-known) {
        deny all;
    }
}

Adjust the fastcgi_pass socket/port to match your PHP-FPM setup.

The runtime JS path

ClientRuntime::register() writes a <script defer src="..."> tag pointing at whatever URL you pass it — by convention /assets/runtime/serixa.js:

ClientRuntime::register($document->assets(), '/assets/runtime/serixa.js');

That URL has to resolve to an actual file. serixa new copies the shipped script into public/assets/runtime/serixa.js automatically. If you move, rename, or proxy your app, make sure that file still exists at the URL you registered — a mismatch is a common cause of a 404 for the runtime script with no visible errors (interactive widgets like Modal or Dropdown will render but simply not respond to clicks). See troubleshooting.md.

To point at a different path (e.g. serving assets from a CDN or a versioned folder), just register a different URL and copy the file there yourself:

ClientRuntime::register($document->assets(), 'https://cdn.example.com/serixa/0.11.0/serixa.js');

The canonical source file is always Serixa\Runtime\ClientRuntime::scriptPath() (physically assets/runtime/serixa.js in the framework package).

CSS: official offline build

SERIXA apps register the compiled stylesheet — never a Tailwind CDN:

use Serixa\Assets\StyleSheet;
use Serixa\Runtime\ClientRuntime;

StyleSheet::register($document->assets()); // /assets/serixa.css
ClientRuntime::register($document->assets());

Build assets before deploy (offline):

vendor/bin/serixa build

This merges framework/assets/css/serixa.css with resources/css/app.css into public/assets/serixa.css, copies the runtime JS, and writes public/assets/manifest.json.

See assets.md for the full pipeline. Maintainers regenerating the base stylesheet use tools/css (Node once); shipped apps stay offline.

For production, deploy public/assets/serixa.css and public/assets/runtime/serixa.js with your app. Do not register cdn.tailwindcss.com.

Composer path repositories in production

If your app's composer.json uses a local path repository (common right after serixa new, while developing next to a framework clone), switch to a real version constraint before deploying:

{
    "require": {
        "serixa/framework": "^0.11"
    }
}

and remove the repositories block. Path repositories with "symlink": true are a local development convenience; they don't make sense once the app is deployed independently. See Path-repository recursion for the related monorepo warning.

Checklist before you deploy

  • [ ] Document root points at public/, not the app root.
  • [ ] public/assets/serixa.css exists (from serixa build) and is registered via StyleSheet::register()
  • [ ] No Tailwind Play CDN script tags in layouts
  • [ ] public/assets/runtime/serixa.js exists and matches the URL passed to ClientRuntime::register()
  • [ ] composer.json uses a version constraint for serixa/framework, not a local path repository.
  • [ ] composer install --no-dev --optimize-autoloader run on the deployment target.