Skip to content

Deploying applications

Deploy a tested application with its committed composer.lock, production configuration and persistent runtime storage. Serve only public/. Use a production web server and supported PHP runtime; PHP's development server is intended for local use.

Install a tested release

Run application tests against the lock file. Check PHP 8.3+ and package-specific extensions on the target. From the deployed project root:

composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
composer check-platform-reqs --no-dev

Use composer update when intentionally changing dependencies in development. Deployment uses install to reproduce the tested lock. Keep CLI in production dependencies when migrations or workers need it. Attach protected configuration and persistent storage before starting PHP.

Production configuration

Set APP_ENV=prod. NAF loads .env.local instead of .env when it exists. Use the exact value prod; an unrecognized value such as production can enable diagnostic error output.

Framework 0.2.8+ preserves process environment values over file values. Use ENV:NAME references when configuration may come from the process environment. See Configuration. Keep source, dependencies, environment files and private storage outside the document root.

Use HTTPS and configure public application URLs. Behind a TLS-terminating proxy, configure both trusted addresses and header handling for sessions.

Web server routing

This Nginx example assumes /var/www/my-app/public and PHP-FPM at /run/php/php-fpm.sock. Replace the host, paths and socket with your server's values. Configure TLS separately.

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

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

    location = /index.php {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    location ~ \.php$ {
        return 404;
    }

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

Static files are served directly; other paths reach the front controller. This includes dynamic assets such as Flow's /_flow/flow.js. The dotfile rule permits /.well-known/ for OAuth/OIDC metadata while denying other hidden paths. Validate and reload Nginx using the server's normal administration procedure. Consult Nginx's request routing and FastCGI parameters when adapting it. Flow has a router for local PHP servers.

Persistent storage and permissions

Give PHP and worker users write access to the runtime directories they use. Keep source and dependencies read-only to them where practical. Preserve runtime data between deployments.

Feature Storage
Logging logs/, or the configured PSR-3 logger
Sessions PHP session.save_path, or database session storage
SQLite Database file and writable parent directory
File queues Queue and deadletter directories in Queues
Scheduler Shared scheduler state when tickers coordinate
MCP storage/mcp/, or the configured token store
File storage Configured local disk roots or remote storage

Local files are not shared automatically across hosts. Choose shared storage or database-backed services for features used by multiple application servers.

Database migrations

Review migrations, take an appropriate backup, and run against the intended database. Requires Database and CLI:

vendor/bin/naf db:migrate up

MySQL/MariaDB DDL can commit implicitly, leaving partial work after a failure. A code rollback does not reverse schema changes. Plan compatibility with the previous application release and recovery using migration transaction behavior.

Background processes

Supervise workers, tickers and WebSocket servers. Restart them after deployments to load new code and configuration. Scheduling needs a ticker and worker. Jobs must tolerate repeated delivery when using a driver with at-least-once delivery.

Verify and recover

Check a public route and unknown route, then exercise a critical flow with test data. Confirm assets load, errors are sanitized, persistence works and queued jobs execute. Review application and server logs, worker status and any configured heartbeat files.

Keep the previous release until verification passes. Restore it through the same deployment mechanism if necessary, accounting for schema and data changes already applied. See Troubleshooting when checks fail.