Laravel Octane lets you use Roadrunner.
But, instead of using the provided rr binary why not compile your own? Compiling Roadrunner lets you extend your app by writing Go plugins and middleware.
The conventions on how to compile Roadrunner have changed in version 2. It’s now done with a tool called Velox.
Tutorial
In an existing Laravel application, install Octane:
composer require laravel/octane php artisan octane:install
Pick Roadrunner as the application server and install the dependency. This will download rr into the Laravel folder.
Tip: If Roadrunner fails to download make sure you don’t already have another version in your path. If you do, delete that stray version and try again.
At this point the default Roadrunner configuration should work. Try it out to make sure.
php artisan octane:start
Next, we’re ready to replace the downloaded rr binary with our own compiled version. To build it we need Go 1.18+ There are many ways to install Go. Here’s one way for MacOS:
brew install go
I also have this in my .bash_profile file:
export GOPATH="${HOME}/go" export GOROOT="$(brew --prefix golang)/libexec" export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin" export GO111MODULE="on"
After installing Go, install Velox:
go install github.com/roadrunner-server/velox/vx@latest
Next, create a new plugins.toml file in your Laravel folder. This file lists all the Roadrunner plugins to compile. There are many plugins! This example compiles all of them.
[velox] build_args = ['-trimpath', '-ldflags', '-s -X github.com/roadrunner-server/roadrunner/v2/internal/meta.version=v2.10.2 -X github.com/roadrunner-server/roadrunner/v2/internal/meta.buildTime=10:00:00'] [roadrunner] ref = "v2.10.2" [github] [github.token] token = "__REPLACE_ME__" [github.plugins] # ref → master, commit or tag logger = { ref = "master", owner = "roadrunner-server", repository = "logger" } temporal = { ref = "master", owner = "temporalio", repository = "roadrunner-temporal" } metrics = { ref = "master", owner = "roadrunner-server", repository = "metrics" } cache = { ref = "master", owner = "roadrunner-server", repository = "cache" } reload = { ref = "master", owner = "roadrunner-server", repository = "reload" } otel = { ref = "master", owner = "roadrunner-server", repository = "otel" } server = { ref = "master", owner = "roadrunner-server", repository = "server" } service = { ref = "master", owner = "roadrunner-server", repository = "service" } amqp = { ref = "master", owner = "roadrunner-server", repository = "amqp" } beanstalk = { ref = "master", owner = "roadrunner-server", repository = "beanstalk" } boltdb = { ref = "master", owner = "roadrunner-server", repository = "boltdb" } broadcast = { ref = "master", owner = "roadrunner-server", repository = "broadcast" } fileserver = { ref = "master", owner = "roadrunner-server", repository = "fileserver" } grpc = { ref = "master", owner = "roadrunner-server", repository = "grpc" } gzip = { ref = "master", owner = "roadrunner-server", repository = "gzip" } headers = { ref = "master", owner = "roadrunner-server", repository = "headers" } http = { ref = "master", owner = "roadrunner-server", repository = "http" } jobs = { ref = "master", owner = "roadrunner-server", repository = "jobs" } memory = { ref = "master", owner = "roadrunner-server", repository = "memory" } nats = { ref = "master", owner = "roadrunner-server", repository = "nats" } new_relic = { ref = "master", owner = "roadrunner-server", repository = "new_relic" } prometheus = { ref = "master", owner = "roadrunner-server", repository = "prometheus" } redis = { ref = "master", owner = "roadrunner-server", repository = "redis" } sqs = { ref = "master", owner = "roadrunner-server", repository = "sqs" } static = { ref = "master", owner = "roadrunner-server", repository = "static" } status = { ref = "master", owner = "roadrunner-server", repository = "status" } kv = { ref = "master", owner = "roadrunner-server", repository = "kv" } memcached = { ref = "master", owner = "roadrunner-server", repository = "memcached" } tcp = { ref = "master", owner = "roadrunner-server", repository = "tcp" } rpc = { ref = "master", owner = "roadrunner-server", repository = "rpc" } uuid = { ref = "master", owner = "connerbw", repository = "uuid" } [log] level = "debug" mode = "development"
Tip: Make sure to replace the github.token or you will get access denied errors. If your repos are public you don’t need any special permissions.
Near the end of the plugins section pay special attention to this line.
uuid = { ref = "master", owner = "connerbw", repository = "uuid" }
This line represents a custom RPC plugin written in Go. The code comments of the plugin act as a sort of guide if ever you want to write your own. Study it.
https://github.com/connerbw/uuid/blob/master/plugin.go
Next, cd to your Laravel folder and compile your plugins.toml with Velox.
vx build -c plugins.toml -o ~/path/to/your/laravel/app
Tip: Replace ~/path/to/your/laravel/app with your own. Delete the rr binary before compiling to avoid a write permission error.
After it compiles, add this code to routes/web.php to test it out.
Route::get('/uuid', function () { $rpc = Spiral\Goridge\RPC\RPC::create(Spiral\RoadRunner\Environment::fromGlobals()->getRPCAddress()); return $rpc->call('uuid.Generate', 'not-used'); });
Restart Laravel Octane and navigate to the new route.
If it all works then PHP is communicating to the new Go plugin over RPC bus. Good times!
Today I wanted to give a lightning talk at ConFoo about this post.
I spent 45 minutes trying to get this working again and I failed. Something changed. I gave up and didn’t present.
I’ve written twice about Roadrunner and Go plugins. Both blog posts are useless because everything changed. The advice is deprecated. It’s always changing and it’s annoying.
At ConFoo I saw a talk about Open Swoole. After seeing that, maybe I don’t care that much about Go plugins anymore.