Integrating Action Cable with Passenger Standalone

Action cable

Passenger Standalone is fully compatible with Action Cable. There are two ways to setup Action Cable with Passenger Standalone:

  1. Running the Action Cable server as part of the normal Rails application (default)
  2. Running the Action Cable server as a separate Passenger Standalone instance

Table of contents

  1. Loading...

Running the Action Cable server as part of the normal Rails application

This is the default setup as recommended by Rails and is also the easiest. It works by mounting ActionCable.server to a certain path in your config/routes.rb. That way, your Action Cable server will be running on the same host and port as your application, but under a sub-URI.

Requirements

  • You need at least Passenger 5.0.25.
  • You need Redis, PostgreSQL, or any other inter-process adapter supported by Action Cable.
  • You need to use Passenger Standalone with the 'nginx' engine (which is the default). This setup does not work with the 'builtin' engine.

Step 1: configuring Passenger

Suppose that your routes.rb contains a call which mounts the Action Cable server at a certain path:

# Serve websocket cable requests in-process
mount ActionCable.server => '/cable'

To make this work in Passenger Standalone, you need to tell Passenger that you are running Action Cable inside your Rails application, and you need to tell Passenger what path it's listening on. In your Passengerfile.json, specify the --unlimited-concurrency-path / "unlimited_concurrency_paths" option:

{
  "unlimited_concurrency_paths": [
    "/cable"
  ]
}

Replace /cable with the actual Action Cable path as is specified in your routes.rb.

Step 2: configuring 'config/cable.yml'

Passenger runs the Action Cable internally as a separate process. (Although the routes.rb comment says that mounting ActionCable.server is meant for serving it in-process, Passenger actually insists on running it as a separate process.) Action Cable defaults to the 'async' adapter, which does not work when multiple processes are involved. So you need to configure Action Cable to use a different adapter, such as Redis or PostgreSQL.

Here is an example that configures config/cable.yml to use Redis:

production:
  adapter: redis
  url: redis://redis.example.com:6379

local: &local
  adapter: redis
  url: redis://localhost:6379

development: *local
test: *local

Don't forget to start Redis.

Running the Action Cable server as a separate Passenger Standalone instance

You can also run the Action Cable server in a separate Passenger Standalone instance. This approach allows you to scale out the Action Cable server separately from the normal Rails application processes, and also allows you to put the Action Cable server on a separate host/port.

Requirements

  • You need at least Passenger 5.0.24.
  • You need Redis, PostgreSQL, or any other inter-process adapter supported by Action Cable.

Preparation: configure Action Cable

Before configuring Passenger, you need to decide on a hostname and port for your Action Cable server. Open config/environments/development.rb or config/environments/production.rb and specify your choice in the config.action_cable.url option.

Furthermore, Passenger will run the Action Cable internally as a separate process. Action Cable defaults to the 'async' adapter, which does not work when multiple processes are involved. So you need to configure Action Cable to use a different adapter, such as Redis or PostgreSQL.

Here is an example that configures config/cable.yml to use Redis:

production:
  adapter: redis
  url: redis://redis.example.com:6379

local: &local
  adapter: redis
  url: redis://localhost:6379

development: *local
test: *local

Don't forget to start Redis.

Step 1: create a Rackup file

This setup requires a Rackup configuration file specifically for Action Cable. It is recommended that you call it cable/config.ru. A basic Rackup configuration for Action Cable looks as follows:

require ::File.expand_path('../../config/environment', __FILE__)
Rails.application.eager_load!

run ActionCable.server

Step 2: create a 'bin/cable' script

Create a bin/cable script, whose purpose is to start a Passenger Standalone instance with the Action Cable server inside it. It should contain:

#!/bin/sh
exec bundle exec passenger start -p YOUR_ACTION_CABLE_PORT_NUMBER_HERE -R cable/config.ru --force-max-concurrent-requests-per-process 0

The above code assumes that you have Passenger in your Gemfile. If you don't have Passenger in your Gemfile, then replace bundle exec with env NOEXEC_EXCLUDE=passenger, like this:

#!/bin/sh
exec env NOEXEC_EXCLUDE=passenger passenger start -p YOUR_ACTION_CABLE_PORT_NUMBER_HERE -R cable/config.ru --force-max-concurrent-requests-per-process 0

The --force-max-concurrent-requests-per-process / "force_max_concurrent_requests_per_process" option tunes Passenger for optimal WebSocket performance.

When done, make the file executable:

$  chmod +x bin/cable

Step 3: start Passenger

Run the bin/cable script:

$ ./bin/cable

Congratulations, you are done!

Restarting the Action Cable server

Main article: Restarting applications

If you want to restart the Action Cable server, then you must use the passenger-config restart-app command. Touching restart.txt does not work well, because restart.txt is checked at the beginning of every new request. But WebSockets are persistent, so restart.txt won't be checked often.

See also