In my previous post, we set up a basic chatting application using Action Cable. This post will server as the walk-through for deploying that app to Heroku.
You may be familiar with the fact that Rails and Heroku and Websockets don't necessarily get along. A programming pun:
if Rails && Websockets && Heroku
use middleware
else
have_a_headache
end
Accordingly, we'll be using a neat piece of middleware written by one of the Rails core-contributors in order to get our Action Cable chatting app up and running on Heroku.
Our middleware will be responsible for holding open a socket connection through which Action Cable can stream and broadcast data.
Creating a Heroku App
Start by creating a Heroku app with heroku create
in your terminal in the root directory of this project.
Action Cable Middleware
This piece of middleware comes courtesy of Jorge Bejar, a contributor to Rails. Let's set it up.
Define your middleware here:
# middleware/action_cable_chat.rb
class ChatActionCable
def initialize(app, options={})
@app = app
end
def call(env)
if Faye::WebSocket.websocket?(env)
ActionCable.server.call(env)
else
@app.call(env)
end
end
end
Mount your middleware in production.rb
config.middleware.use ChatActionCable
config.web_socket_server_url = "wss://pacific-chamber-3660.herokuapp.com/"
How does this work? Your middleware will intercept all incoming requests to the app. It will introspect on that request to determine if it is a websocket request. If so, it will call on the Action Cable server.
We mount this middleware in production.rb
and we set the websocket server URL to a secure websocket server (wss
) running on a separate process within our main Puma server.
Now, we need to edit the labs.js
file, which establishes the connection to Action Cable on the client side. Change labs.js
to labs.js.erb
and edit it in the following manner:
this.App = {};
App.cable = Cable.createConsumer("<%= Rails.application.config.web_socket_server_url %>");
Now that we have our middleware up and running, we need to configure Redis for Heroku.
Configuring Redis
First, provision the Redis addon to your Heroku app.
In your terminal, in the top level of this project:
heroku addons:create redistogo
It should return to you your Redis URL. Something like:
redis://redistogo:18998e348ac30ca0002879ce9d85daf0bb0@tarpon.redistogo.com:10272
Now we need to edit config/redis/cable.yml
development: &development
:url: redis://localhost:6379
:host: localhost
:port: 6379
:timeout: 1
:inline: true
test: *development
production: &production
:url: redis://redistogo:10e348ac30ca0002879ce9d85daf0bb0@tarpon.redistogo.com:10272
:host: tarpon.redistogo.com
:port: 10272
:password: 10e348ac30ca0002879ce9d85daf0bb0
:inline: true
:timeout: 1
Your Redis host can be found between the @
and the :
of your Redis URL and your Redis port is the collection of digits following the :
. To get your password, you can use this trick:
- Drop into the Heroku Rails console with
heroku run rails c
- Execute:
uri = URI.parse(<your redis URL>)
uri.password
Last but not least, we need to edit our Procfile.
Procfile
Remove the Redis and Action Cable lines from your Procfile. Instead, it should handle only connecting to a multi-threaded Puma server.
web: bundle exec puma -p $PORT
And that's it! Push up to Heroku and you should be good to go!
For further reference, you can checkout the repo for this project here. Make sure you are looking at the "heroko-version" branch.
Update
This post shows ActionCable being run in a separate server. However! With Rails 5, the default will be to run ActionCable inside the Rails app process under a sub-URI. To see an example of a deployment using this recommendation, check out this excellent Go Rails post on deploying Action Cable with Passenger. And thanks to @honglilai for pointing out some of these changes.