Deploying ActionCable on Heroku with Sidekiq

you’re gonna need a bigger redis

Published July 11, 2016 #howto #rails #heroku #sidekiq

ActionCable is WebSockets on rails. This lets you create realtime, interactive systems, where you can push data from one client to another client without reloading or polling. But how do we deploy it on heroku?

ActionCable is composed to two main parts: a javascript client library, and a backend pub/sub system built upon Redis. We’re also going to use ActiveJob to offload the publishing tasks from the main user thread, so we’ll also be setting up sidekiq.

Let’s go through the steps of how to deploy things on heroku.

First, the app

I’m going to use an app that dhh made as a walk through. What it does is

I’ve transcribed it here to see what was added. The code is available to clone the code from github if you don’t have an app that you are working on already.

Now lets talk about deployment

The app we generated was done using basic rails 5 commands, and it’s out of the box stuff. (Unlike most of the walkthroughs on this site which were done with seed. It’s setup to use puma (good), which will handle the WebSockets. It’s also setup with ActiveJob and ActionCable, both on which are in async local mode and won’t use redis in development. ActionCable will also need to know where it’s websocket is going to connect to. Lets walk through everything that needs to get done.


First we need to add some things to the Gemfile, specifically redis, postgres, and the sidekiq gems. Add the following to your Gemfile

gem 'redis', '~> 3.0'
gem 'pg', group: :production
gem 'sqlite3', group: :development
gem 'sidekiq'
gem 'sinatra', git: '', :require => nil

Make sure you remove the sqlite3 on the top of the file, we only want it in development. We are installing sinatra out of the master branch since the current release doesn’t play well with Rails 5. (If it tries to install Sinatra 1.0 you’ll get rake exception errors.) If you don’t want the web interface to sidekiq you can omit.

Setup sidekiq

First we need to create a Procfile that defines our dynos

web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -c 2

I’m running sidekiq with a concurrency of 2 to limit the number of redis connections. You’ll probably want that a lot higher in real life if you are using Sidekiq for other things..

Now create config/initializers/active_job.rb and tell ActiveJob to use sidekiq

Rails.application.config.active_job.queue_adapter = :sidekiq

And let’s add the sidekiq web interace to your config/routes.rb:

  require 'sidekiq/web'
  mount Sidekiq::Web => '/sidekiq'

If this was a real app, you’d limit who can get to that engine. If you installed devise, the way to do that is:

  authenticate :admin_user do
    mount Sidekiq::Web => '/sidekiq'

A brief digression to test locally

It’s always nice to change as little as we can, so when everything blows up in our face we can limit our investigations to a small change set. So lets first migrate our development environment to communicate over redis, so the ActiveJobs are run in a seperate process, and that ActionCable will be able to communicate from that process, back to the main web server, back to the client.

Edit config/cable.yml to set the development environment use to use redis.

  adapter: redis
  url: redis://localhost:6379/1

If you don’t have redis installed, use homebrew to do that now:

$ brew install redis
$ redis-server

If you don’t have foreman installed, do that now:

$ gem install foreman

Now lets run that app!

$ rails db:migrate
$ foreman start

Using foreman will start both the web interace as well as the sidekiq worker.

Now you should be able to go to http://localhost:3000 in multiple windows and talk with yourself. I really like talking with myself, and I assume that you do as well.

And http://localhost:3000/admin should load up the sidekiq admin console. Validate that everything is working, since now we are going to spin up everything on heroku!

Heroku and Redis

Create a new app:

$ heroku create

Then we create a redis instance

$ heroku addons:create heroku-redis:hobby-dev

We now need to tell ActionCable where it’s redis server is. Lets find out the answer and put in into the production section of config/cable.yml.

$  heroku config | grep REDIS
REDIS_URL:                redis://h:p75fl9as.........


  adapter: redis
  url: redis://h:p75fl9as.........

Set the right ActionCable websocket address

We need to tell rails where it’s expected to receive the websocket connection from. heroku info will show us our external application. If you deploy on a custom url, you’ll need to add that as well.

$ heroku info
=== aqueous-thicket-49913
Addons:        heroku-postgresql:hobby-dev
Dynos:         web: 1, worker: 1
Git URL:
Region:        us
Repo Size:     34 KB
Slug Size:     29 MB
Stack:         cedar-14
Web URL:

Lets tell rails about it. Edit config/environments/production.rb. Obviously update the urls for your application.

  config.action_cable.url = 'wss://'
  config.action_cable.allowed_request_origins = [ '']

Note that wss is WebSockets over over SSL. You should use that, for a number of reasons including but not limited to security. However, if you don’t use SSL, use ws instead.

Now we need to tell the javascript where to connect. Lets open up app/views/layouts/application.html.erb and add this into the <HEAD>:

 <%= action_cable_meta_tag %>

Commit and push

First we add the code to repo

$ git add .
$ git commit -a -m "Added heroku configuration"

Now we push to heroku itself:

$ git push heroku master

If that goes well, lets create the database tables and spin up the worker process

$ heroku run rake db:migrate
$ heroku ps:scale worker=1

And now, lets run the app and look at the logs:

$ heroku open
$ heroku open
$ heroku log --tail

We ran open twice, so you could see what was there in two windows. Does it work? Do 3 windows work?

Do you like talking to yourself as much as I do?

But does it scale

Right now you have 1 web dyno, running puma with 5 threads, and 1 worker dyno running sidekiq with 2 concurrent worker. This should be at least 5 redis connections, up to 2 more depending upon how many jobs have gone through sidekiq. Lets look at the redis info:

$ heroku redis:cli> info

# Clients

So we see that we have 7 active connections now. As you get more people listening to redis, those will go up. Adding more dynos will push the number of active connections up, so you need to be careful in sizing both your postgres install as well as your redis install.

Image credit Lee Roberts

Read next

See also

Using seed to explore APIs

overview of what we’re working on and how to explore apis

I’ve been working to update seed, which is HappyFunCorp’s app generator to make it easy to kick off MVPs. Check out the website for more information. One of the things that I’ve started to do is to seperate out the dependancies more, and being tutorials on how to use each of the different features. After I link to that stuff, let’s walk through a way to combine different techniques we’ve discussed together.

Read more

Setting up Devise with Twitter and Facebook and other Omniauth schemes without email addresses

Connect connect connect

Adding social login to your sites really makes it easier to get users onboard. Devise is great to help get an authentication system up and running, but there are a few tricky things to get right. The first challenge is that you don’t always get the user’s email address when the first connect. The second challenge is that we want to request the minimum permissions first so that the user is more likely to sign up, and gradually ask more as the time arises.

Read more

Dateslice: Writing rails extensions

adding date group_by to ActiveRecord

Ruby on Rails is a very modular framework since the merging with Merb in 2008. The opinionated conventions are implemented under using techniques that let you jump in and build your own components, picking and choosing different parts that let you build Rails apps in the same straightforward way you would if using the official modules. Let’s go through the dateslices gem which I wrote to extend active record so that we could better interact with the group by sql command when dealing with dates.

Read more