Boost Laravel Performance: Running Octane with FrankenPHP in Production ( Zero downtime)

Boost Laravel Performance: Running Octane with FrankenPHP in Production ( Zero downtime)

Posted By

kamlesh paul

on

Dec 9, 2024

Are you looking to make your Laravel application blazing fast? With Laravel Octane and FrankenPHP, you can significantly speed up your app by running everything in memory, avoiding the overhead of bootstrapping the app repeatedly.

Laravel Octane makes your application stateful, meaning your app doesn’t need to rebuild everything for every request. Combined with FrankenPHP, a PHP-based web server built on Caddy, you can eliminate reverse proxies and serve PHP applications with unparalleled performance.

Table of contents

Why FrankenPHP?

FrankenPHP is a custom-built Caddy server designed for PHP applications. It lets you run your Laravel application efficiently without a reverse proxy, giving you direct control over performance.

Step 1: Install Laravel Octane and FrankenPHP

  • Start by installing Laravel Octane via Composer:
composer require laravel/octane
  • Next, install Octane with the FrankenPHP server option:
php artisan octane:install --server=frankenphp

This will download and configure the necessary files for FrankenPHP.

Step 2: Test Locally

  • To test the setup on your local environment, run:
php artisan octane:start

This starts the server on localhost. But in production, you’ll need to run it with your domain.

Step 3: Run in Production

  • For production environments, run the following command to start the server with your domain:
php artisan octane:start --host=example.com
  • However, this will start without HTTPS. To enable HTTPS and run on port 443, use:
php artisan octane:start --host=example.com --port=443 --https --admin-port=8000

This will run the server with your domain name and HTTPS, eliminating the need for a reverse proxy, making your app faster.

Step 4: Customize FrankenPHP with a Caddyfile

For optimal performance, you can configure FrankenPHP using a custom Caddyfile. Place your Caddyfile in the specified path, which should match the --caddyfile flag used in the Supervisor configuration.

Here’s Caddyfile with necessary headers and configurations:

Caddyfile
{
	{$CADDY_GLOBAL_OPTIONS}
 
	admin localhost:{$CADDY_SERVER_ADMIN_PORT}
 
	frankenphp {
		worker "{$APP_PUBLIC_PATH}/frankenphp-worker.php" {$CADDY_SERVER_WORKER_COUNT}
	}
}
 
{$CADDY_SERVER_SERVER_NAME} {
	log {
		level {$CADDY_SERVER_LOG_LEVEL}
 
		# Redact the authorization query parameter that can be set by Mercure...
		format filter {
			wrap {$CADDY_SERVER_LOGGER}
			fields {
				uri query {
					replace authorization REDACTED
				}
			}
		}
	}
 
	route {
		root * "{$APP_PUBLIC_PATH}"
		encode zstd br gzip
 
		# Mercure configuration is injected here...
		{$CADDY_SERVER_EXTRA_DIRECTIVES}
 
		@static {
			file
			path *.js *.css *.jpg *.jpeg *.gif *.png *.ico *.cur *.gz *.svg *.svgz *.mp4 *.mp3 *.ogg *.ogv *.webm *.htc *.woff2 *.woff
		}
 
		@staticshort {
			file
			path *.json *.xml *.rss
		}
 
		header {
			# keep referrer data off of HTTP connections
			Referrer-Policy "no-referrer-when-downgrade"
 
			# enable HSTS
			Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
 
			# Enable cross-site filter (XSS) and tell the browser to block detected attacks
			X-Xss-Protection "1; mode=block"
 
			# disable clients from sniffing the media type
			X-Content-Type-Options "nosniff"
 
			# clickjacking protection
			X-Frame-Options "SAMEORIGIN"
 
			# Upgrade insecure requests
			Content-Security-Policy "upgrade-insecure-requests"
 
			# Caching directives
			# 1 year, similar to h5bp nginx config
			@static Cache-Control "public, immutable, stale-while-revalidate, max-age=31536000"
 
			# 1 hour max, gets validated with the origin server
			@staticshort Cache-Control "no-cache, max-age=3600"
		}
 
		# Restrict access to dot files and certain file extensions
		@rejected `path('*.bak', '*.conf', '*.dist', '*.fla', '*.ini', '*.inc', '*.inci', '*.log', '*.orig', '*.psd', '*.sh', '*.sql', '*.swo', '*.swp', '*.swop', '*/.*') && !path('*/.well-known/')`
		error @rejected 401
 
		php_server {
			index frankenphp-worker.php
			# Required for the public/storage/ directory...
			resolve_root_symlink
		}
	}
}

This configuration ensures that your Laravel application is secure, fast, and efficient, with caching headers and security policies.

Ref link https://caddy.community/t/tweaking-frakenphp-caddyfile-for-laravel-octane/23926

Step 5: Keep Octane Running with Supervisor

In production, you’ll need to ensure Laravel Octane keeps running even after closing the terminal. This is where Supervisor comes in.

  • Install Supervisor:
sudo apt-get install supervisor
  • Create a new configuration file for Laravel Octane:
sudo nano /etc/supervisor/conf.d/laravel-octane.conf

Add the following configuration to keep Octane running:

laravel-octane.conf
[program:laravel-octane]
command=php /home/laravel/artisan octane:start
    --host=example.com
    --port=443
    --https
    --admin-port=2019
    --workers=4  # Adjust based on your CPU count
    --max-requests=1000
    --caddyfile=/home/laravel/Caddyfile
 
stdout_logfile=/var/log/supervisor/laravel-octane.log
environment=HOME="/home/laravel"
 
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=your-actual-username  # Ensure this is a valid username
redirect_stderr=true
stopwaitsecs=3600

To optimize performance, check your server’s CPU count and adjust the --workers parameter accordingly. A common practice is to set the number of workers to the number of CPU cores available, allowing your application to handle multiple requests simultaneously without overloading the server.

  • Next, reload the Supervisor configurations:
sudo supervisorctl reread
sudo supervisorctl update
 
sudo supervisorctl start laravel-octane

Step 6: Extend PHP Extensions

If you need additional PHP extensions that are not included by default, you can create the FrankenPHP binary from the FrankenPHP documentation. Follow the instructions provided to compile your custom binary, which you can place in the root of your project and use to enable any extra extensions your application may require.

Step 7: Create a Deployment Script

To streamline your deployment process, you can create a script that ensures everything is built for production and restarts Laravel Octane every time you make changes to your live server.

deploy.sh
#!/bin/bash
set -e
 
echo "Deployment started ..."
 
# Pull the latest version of the app
git pull origin main
 
# Install composer dependencies
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
 
# Clear the old cache
php artisan clear-compiled
 
# Recreate cache
php artisan optimize
 
# Run database migrations
php artisan migrate --force
 
 
php artisan octane:reload
 
echo "Deployment finished!"
  • To run this script:

Make it executable and then execute it:

chmod +x deploy.sh
./deploy.sh

This script ensures that your application is updated with the latest changes, optimized for production, and that Laravel Octane is restarted.

Conclusion

By leveraging Laravel Octane with FrankenPHP, you’re taking full advantage of memory-resident processes and eliminating unnecessary reverse proxies. This results in a highly optimized and scalable application.

Enjoy your blazing-fast Laravel app.

Share this article

132 views