¿Qué es Octane?
Laravel Octane es un paquete que mejora drásticamente el rendimiento de las aplicaciones Laravel usando servidores de aplicaciones de alto rendimiento.
En un PHP-FPM normal, la aplicación se arranca y se termina en cada solicitud. Con Octane, la aplicación se arranca una única vez y se mantiene en memoria, procesando las siguientes solicitudes con gran rapidez.
La mayor diferencia respecto a PHP-FPM es que el proceso de arranque de la aplicación (registro de service providers, etc.) no se repite en cada solicitud. Al arrancar una sola vez, las solicitudes se procesan a altísima velocidad.
Servidores compatibles
Octane admite los siguientes tres servidores.
| Servidor | Lenguaje | Instalación | Tareas concurrentes | Notas |
|---|
| FrankenPHP | Go | Automática (binario) | — | Recomendado. Soporta HTTP/3 y Brotli |
| RoadRunner | Go | Automática (binario) | — | Sencillo y fácil de configurar |
| Swoole | Extensión de PHP | Manual con PECL | ✅ | Ofrece tareas concurrentes y la caché de Octane |
En Laravel Cloud se recomienda operar Octane con FrankenPHP y ofrece soporte totalmente gestionado.
Instalación y configuración
Instalación del paquete
composer require laravel/octane
Configuración inicial de Octane
php artisan octane:install
Al ejecutarlo se muestra un prompt para elegir el servidor. Al seleccionarlo se genera config/octane.php.
FrankenPHP
Al elegir FrankenPHP, Octane descarga automáticamente el binario. No hacen falta pasos adicionales.
RoadRunner
Al elegir RoadRunner, Octane descarga automáticamente el binario.
Swoole
Swoole se instala como extensión de PHP con PECL.
Para usar Open Swoole, ejecuta:
Cómo arrancar
Arrancar el servidor
Por defecto arranca en el puerto 8000. Accede en http://localhost:8000.
Indicar el servidor
php artisan octane:start --server=frankenphp
php artisan octane:start --server=roadrunner
php artisan octane:start --server=swoole
Indicar el número de workers
php artisan octane:start --workers=4
Para especificar también los task workers en Swoole:
php artisan octane:start --workers=4 --task-workers=6
Observación de cambios en archivos
Durante el desarrollo, los cambios en los archivos no se reflejan sin reiniciar el servidor. Con --watch se reinicia automáticamente.
php artisan octane:start --watch
Para usar --watch necesitas Node.js y Chokidar.npm install --save-dev chokidar
Otros comandos de gestión
# Recargar workers (ejecutar tras el despliegue)
php artisan octane:reload
# Parar el servidor
php artisan octane:stop
# Comprobar el estado del servidor
php artisan octane:status
Consideraciones sobre inyección de dependencias
En Octane la aplicación se mantiene en memoria, por lo que register y boot de los service providers solo se ejecutan una vez, al arrancar el servidor. La misma instancia de la aplicación se reutiliza entre solicitudes, así que hay que tener cuidado al inyectar el contenedor o la solicitud en el constructor de un objeto.
Inyección del contenedor
Si inyectas el contenedor directamente en el constructor de un singleton, se reutiliza un contenedor antiguo.
// ❌ Problemático: se reutiliza un contenedor antiguo
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app);
});
// ✅ Seguro: obtén siempre el contenedor más reciente con un closure
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance());
});
Los helpers globales app() y Container::getInstance() siempre devuelven el contenedor más reciente, así que son seguros.
Inyección de la solicitud
// ❌ Problemático: se reutiliza una solicitud antigua
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app['request']);
});
// ✅ Seguro: obtén siempre la solicitud más reciente con un closure
$this->app->singleton(Service::class, function (Application $app) {
return new Service(fn () => $app['request']);
});
// ✅ Más recomendado: pasa solo el valor necesario al método
$service->method($request->input('name'));
Tipar Illuminate\Http\Request en los métodos del controlador o en closures de ruta es seguro. El helper global request() también devuelve siempre la solicitud actual.
Inyección del repositorio de configuración
// ❌ Problemático: si los valores cambian, se usa un repositorio antiguo
$this->app->singleton(Service::class, function (Application $app) {
return new Service($app->make('config'));
});
// ✅ Seguro
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance()->make('config'));
});
El helper global config() siempre devuelve el repositorio de configuración más reciente, así que es seguro.
Mitigación de fugas de memoria
Como Octane mantiene la aplicación en memoria, acumular datos en propiedades estáticas provoca fugas.
// ❌ Ejemplo de fuga: $data crece con cada solicitud
class Service
{
public static array $data = [];
}
public function index(Request $request): array
{
Service::$data[] = Str::random(10);
return [];
}
Reciclar workers con max-requests
Al reiniciar automáticamente el worker tras un cierto número de solicitudes se atenúan las fugas de memoria. El valor por defecto es 500 solicitudes.
php artisan octane:start --max-requests=250
Configuración del tiempo máximo de ejecución
Puedes definir el tiempo máximo de una solicitud en config/octane.php. Por defecto son 30 segundos.
'max_execution_time' => 30,
Si cambias max_execution_time, reinicia el servidor Octane.
Tareas concurrentes (solo Swoole)
Con Swoole puedes ejecutar varias operaciones en paralelo con Octane::concurrently().
use App\Models\User;
use App\Models\Server;
use Laravel\Octane\Facades\Octane;
[$users, $servers] = Octane::concurrently([
fn () => User::all(),
fn () => Server::all(),
]);
Las tareas concurrentes se ejecutan como “task workers” de Swoole en procesos separados. Indica el número con --task-workers.
php artisan octane:start --workers=4 --task-workers=6
A concurrently se le pueden pasar como máximo 1024 tareas (límite de Swoole).
Tick e Interval (solo Swoole)
En Swoole puedes registrar tareas que se ejecutan a intervalos regulares. Se registran en el método boot de un service provider.
use Laravel\Octane\Facades\Octane;
// Se ejecuta cada 10 segundos
Octane::tick('delayed-ticker', fn () => ray('Ticking...'))
->seconds(10);
// También se ejecuta inmediatamente al arrancar el servidor
Octane::tick('immediate-ticker', fn () => ray('Ticking...'))
->seconds(10)
->immediate();
Caché de Octane (solo Swoole)
Caché en memoria ultrarrápida basada en las tablas de Swoole. Soporta hasta 2 millones de lecturas/escrituras por segundo.
use Illuminate\Support\Facades\Cache;
Cache::store('octane')->put('framework', 'Laravel', 30);
Caché con intervalo
Caché que se actualiza automáticamente cada cierto tiempo.
use Illuminate\Support\Str;
Cache::store('octane')->interval('random', function () {
return Str::random(10);
}, seconds: 5);
Los datos de la caché de Octane se pierden al reiniciar el servidor.
Tablas de Swoole (solo Swoole)
Puedes definir tablas en memoria accesibles desde todos los workers. Se configuran en tables de config/octane.php.
'tables' => [
'example:1000' => [
'name' => 'string:1000',
'votes' => 'int',
],
],
Accede a la tabla con Octane::table().
use Laravel\Octane\Facades\Octane;
Octane::table('example')->set('uuid', [
'name' => 'Nuno Maduro',
'votes' => 1000,
]);
$row = Octane::table('example')->get('uuid');
Los únicos tipos de columna soportados por las tablas de Swoole son string, int y float. Los datos se pierden al reiniciar el servidor.
Operación en producción
Configuración Nginx + Octane
En producción es habitual ejecutar Octane detrás de Nginx. Nginx sirve los archivos estáticos y termina SSL, mientras que las solicitudes dinámicas se pasan por proxy a Octane.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com;
root /home/forge/example.com/public;
location /index.php {
try_files /not_exists @octane;
}
location / {
try_files $uri $uri/ @octane;
}
location @octane {
set $suffix "";
if ($uri = /index.php) {
set $suffix ?$query_string;
}
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Scheme $scheme;
proxy_set_header SERVER_PORT $server_port;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:8000$suffix;
}
}
Ejecución permanente con Supervisor
En producción, usa Supervisor para mantener Octane siempre activo.
[program:octane]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/example.com/artisan octane:start --server=frankenphp --host=127.0.0.1 --port=8000
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/storage/logs/octane.log
stopwaitsecs=3600
Habilitar HTTPS
Para generar enlaces HTTPS a través de Octane, configura en .env:
Integración con Laravel Cloud
Laravel Cloud ofrece soporte totalmente gestionado para Octane con FrankenPHP. No necesitas configurar Nginx ni Supervisor: puedes activarlo en solo dos pasos.
Instalación del paquete
Instala Octane. Ejecutar octane:install es opcional.composer require laravel/octane
Activar Octane en Laravel Cloud
En la configuración del cluster de cómputo del entorno, activa “Use Octane as runtime” y guarda/despliega. Laravel Cloud construirá y arrancará automáticamente la aplicación con FrankenPHP + Octane.
Consulta la documentación de Laravel Cloud para más detalles.
Recargar workers tras el despliegue
Tras un despliegue, recarga los workers para cargar el nuevo código en memoria.
php artisan octane:reload