AnyCable on Rails

AnyCable can be used as a drop-in replacement for Action Cable in Rails applications. It supports most Action Cable features (see Compatibility for more) and can be used with any Action Cable client. Moreover, AnyCable brings additional power-ups for your real-time features, such as streams history support and API extensions.

See also the demo of migrating from Action Cable to AnyCable.

Requirements

  • Ruby >= 2.7
  • Rails >= 6.0

See also requirements for broadcast adapters (You can start with HTTP to avoid additional dependencies).

Installation

Add AnyCable Rails gem to your Gemfile:

# If you plan to use gRPC
gem "anycable-rails", "~> 1.5"

# If you plan to use HTTP RPC or no RPC at all
gem "anycable-rails-core", "~> 1.5"

Read more about different RPC modes here.

Then, run the interactive configuration wizard via Rails generators:

bin/rails g anycable:setup

The command above asks you a few questions to configure AnyCable for your application. Want more control? Check out the manual setup section below.

Configuration

AnyCable Rails uses Anyway Config for configuration. Thus, you can store configuration parameters whenever you want: YAML files, credentials, environment variables, whatever.

We recommend keeping non-sensitive and stable parameters in config/anycable.yml, e.g., broadcast adapter, default JWT TTL, etc.

For secrets (secret, broadcast_key, etc.), we recommend using Rails credentials.

The most important configuration settings are:

  • secret: a common secret used to secure AnyCable features (signed streams, JWT, etc.). Make sure the value is the same for your Rails application and AnyCable server.

  • broadcast_adapter: defines how to deliver broadcast messages from the Rails application to AnyCable server (so it can transmit them to connected clients). See broadcasting docs for available options and their configuration.

See AnyCable Ruby configuration for more information.

Forgery protection

AnyCable respects Action Cable configuration regarding forgery protection if and only if ORIGIN header is proxied by AnyCable server, i.e.:

anycable-go --headers=cookie,origin --port=8080

However, we recommend performing the origin check at the AnyCable server side (via the --allowed_origins option). See AnyCable configuration.

Embedded gRPC server

It is possible to run AnyCable gRPC server within another Ruby process (Rails server or tests runner). We recommend using this option in development and test environments only or in single-process production environments.

To automatically start a gRPC server every time you run rails s, add embedded: true to your configuration. For example:

# config/anycable.yml
development:
  embedded: true

NOTE: Make sure you have Rails.application.load_server in your config.ru. The feature is available since Rails 6.1.

Manual setup

Development

First, activate AnyCable in your Rails application by specifying it as an adapter for Action Cable:

# config/cable.yml
development:
  adapter: any_cable
  # ...

Then, create config/anycable.yml with basic AnyCable configuration:

# config/anycable.yml
development:
  broadcast_adapter: http
  websocket_url: ws://localhost:8080/cable

Install AnyCable server and add the following commands to your Procfile.dev file*:

web: bin/rails s
# ...
ws: anycable-go
# When using gRPC
rpc: bundle exec anycable

Now, run your application via your process manager (or bin/dev, if any). You are AnyCable-ready!

* If you don't have a process manager yet, we recommend using Overmind. Foreman works, too.

IMPORTANT: Despite AnyCable providing multiple RPC modes, we recommend having similar development and production setups. Thus, if you use gRPC in production, use it in development, too.

Production

The quickest way to get AnyCable server for production usage is to use our managed (and free) solution: plus.anycable.io

Whenever you're ready to push your AnyCable-backed Rails application to production (or staging), make sure your application is configured the right way:

  • Configure Action Cable adapter for production:

    # config/cable.yml
    production:
      adapter: any_cable
      # ...
  • Provide AnyCable WebSocket URL via the ANYCABLE_WEBSOCKET_URL environment variable.

    Alternatively, you can use Rails credentials or YAML configuration.

    IMPORTANT: The URL configuration is used by the #action_cable_meta_tag helper. Make sure you have it in your HTML layout.

  • Make sure you configured secrets obtained from your AnyCable server (secret, broadcast_key, etc.)

  • When using gRPC server, make sure you have a corresponding new process added to your deployment.

Check out our deployment guides to learn more about your deployment methods and AnyCable.

Server installation

For your convenience, we provide a binstub (bin/anycable-go) which automatically downloads an AnyCable server binary (and caches it) and launches it. Run the following command to add it to your project:

$ bundle exec rails g anycable:bin

...

You can also install AnyCable server yourself using one of the multiple ways.

Testing with AnyCable

If you'd like to run AnyCable gRPC server in tests (for example, in system tests), we recommend to start it manually only when necessary (i.e., when dependent tests are executed) and use the embedded mode.

You can also run AnyCable server automatically when starting a gRPC server.

That's how we do it with RSpec:

# spec/support/anycable_setup.rb
RSpec.configure do |config|
  cli = nil

  config.before(:suite) do
    examples = RSpec.world.filtered_examples.values.flatten
    has_no_system_tests = examples.none? { |example| example.metadata[:type] == :system }

    # Only start RPC server if system tests are included into the run
    next if has_no_system_tests

    require "anycable/cli"

    $stdout.puts "\n⚡️  Starting AnyCable RPC server...\n"
    AnyCable::CLI.embed!(%w[--server-command=bin/anycable-go])
  end
end

To use :test Action Cable adapter along with AnyCable, you can extend it in the configuration:

# config/environments/test.rb
Rails.application.configure do
  config.after_initialize do
    # Don't forget to configure URL in your anycable.yml or via ANYCABLE_WEBSOCKET_URL
    config.action_cable.url = ActionCable.server.config.url = AnyCable.config.websocket_url

    # Make test adapter AnyCable-compatible
    AnyCable::Rails.extend_adapter!(ActionCable.server.pubsub)
  end

  # ...
end

Gradually migrating from Action Cable

It's possible to run AnyCable along with Action Cable, so you can still serve legacy connections (or perform gradual roll-out, A/B testing, etc.). A common use-case is switching from Action Cable to AnyCable while updating the WebSocket URL (e.g., when you have no control over a load balancer or ingress, so you can't just switch /cable traffic to a different service).

To achieve a smooth migration, you need to accomplish the following steps:

  • Continue using your current pub/sub adapter for Action Cable (say, redis) but extend it with AnyCable broadcasting capabilities by adding the following code:

    # config/initializers/anycable.rb
    AnyCable::Rails.extend_adapter!(ActionCable.server.pubsub) unless AnyCable::Rails.enabled?
  • You can also add AnyCable JWT support to Action Cable. See authentication docs.

That's it! Now you can serve Action Cable clients via both ws://<rails>/cable and ws://<anycable>/cable, and they should be able to communicate with each other.

NOTE: If you use graphql-anycable, things become more complicated. You will need to schemas with different subscriptions providers and a similar dual adapter to support both cables.