
Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability.
Cowboy is a small, fast and modular HTTP server written in Erlang.
Heroku is a cloud application platform which allows you to deploy and scale your own application at pleasure.
In this post, we will write an Erlang/Cowboy Web Application from scratch, deploying it on Heroku. Since Heroku offers a free tier for deploying web apps, this will cost you nothing. Also, the entire process should take no more than half an hour.
Requirements
- You have git installed on your machine.
- You have Erlang installed on your machine. In this example I will be using R15B01.
- You are familiar with the basic concepts of Erlang and OTP
- You have a free account on Heroku.
- You have the Heroku start-kit for your OS installed.
Summary
- Create the skeleton of an Erlang app using Rebar
- Create a Heroku application
- Use Cowboy to create a simple web app
- Compile and run your application locally
- Configure your Heroku app
- Deploy your Erlang application on Heroku
- Profile your deployed Erlang application
Create the skeleton of an Erlang app using Rebar
Rebar is the de-facto standard build-tool for Erlang projects.
Fetch rebar from Github and bootstrap it:
$ git clone https://github.com/rebar/rebar.git
$ cd rebar
$ ./bootstrap
$ cd ..
Initialize a new git repository and use rebar to create the skeleton for a new Erlang app. I decided to call my application erlblog. Call your application differently, replacing every occurrence of erlblog with your favourite application name in the instructions below. Please note that this is not optional, since two applications cannot have the same name on Heroku and you don’t dare to clash with my own application.
$ git init erlblog
$ cd erlblog
$ cp ../rebar/rebar .
$ ./rebar create-app appid=erlblog
Commit what you have done in git:
$ git add rebar src
$ git commit -m "Add rebar skeleton"
Create a Heroku application
Login into Heroku using the heroku command from the terminal.
$ heroku login
Use your Heroku email and password to login.
Now create a new Heroku app, using the Erlang buildpack from @archaelus:
$ heroku create erlblog --stack cedar --buildpack https://github.com/archaelus/heroku-buildpack-erlang
Use Cowboy to create a simple web app
Add the Cowboy web server as a rebar dependency:
$ cat rebar.config
{deps, [
{cowboy, "0.8.4", {git, "https://github.com/extend/cowboy.git", {tag, "0.8.4"}}}
]}.
Add cowboy to the list of applications in your .app.src file. Also, set the http_port environment variable to 8080 (see next paragraphs).
$ cat src/erlblog.app.src
{application, erlblog,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib,
cowboy
]},
{mod, { erlblog_app, []}},
{env, [{http_port, 8080}]}
]}.
Modify the start/2 function from the erlblog_app module so that Cowboy starts a pool of acceptors when the erlblog application is started. Configure the Cowboy dispatcher with a single dispatching rule, routing all requests to ’/’ to the erlblog_handler (see below).
Heroku assigns random ports to your application and uses the OS environment variable $PORT to inform you about the port on which your web server should listen to. Therefore, in the following code we read that environment variable, defaulting to port 8080 in case the environment variable is not specified. This is useful, for example, if you want to try your web server locally before deploying it on Heroku.
$ cat src/erlblog_app.erl
-module(erlblog_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
-define(C_ACCEPTORS, 100).
%% ===================================================================
%% Application callbacks
%% ===================================================================
start(_StartType, _StartArgs) ->
Routes = routes(),
Dispatch = cowboy_router:compile(Routes),
Port = port(),
TransOpts = [{port, Port}],
ProtoOpts = [{env, [{dispatch, Dispatch}]}],
{ok, _} = cowboy:start_http(http, ?C_ACCEPTORS, TransOpts, ProtoOpts),
erlblog_sup:start_link().
stop(_State) ->
ok.
%% ===================================================================
%% Internal functions
%% ===================================================================
routes() ->
[
{'_', [
{"/", erlblog_handler, []}
]}
].
port() ->
case os:getenv("PORT") of
false ->
{ok, Port} = application:get_env(http_port),
Port;
Other ->
list_to_integer(Other)
end.
Let’s now implement a basic HTTP Cowboy handler, which simply replies with a 200 status code and a notorious welcoming message to any incoming request:
$ cat src/erlblog_handler.erl
-module(erlblog_handler).
-export([init/3]).
-export([handle/2]).
-export([terminate/3]).
init(_Transport, Req, []) ->
{ok, Req, undefined}.
handle(Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], <<"Hello world!">>, Req),
{ok, Req2, State}.
terminate(_Reason, _Req, _State) ->
ok.
Finally, let’s create an interface module which will be responsible for starting your erlblog application together with all its dependencies.
$ cat src/erlblog.erl
-module(erlblog).
-export([start/0]).
start() ->
ok = application:start(crypto),
ok = application:start(ranch),
ok = application:start(cowboy),
ok = application:start(erlblog).
Compile and run your application locally
Compile the erlblog application using rebar:
$ ./rebar get-deps compile
Start the application and verify that everything works as expected:
$ erl -pa ebin deps/*/ebin -s erlblog
From the Erlang shell, type:
1> application:which_applications().
The erlblog application should be included in the output.
Finally, point your browser to:
http://localhost:8080
And verify that the string “Hello World!” is there.
You can use Ctrl-G q to exit the Erlang shell.
If everything works as expected, commit everything to git:
$ git add rebar.config src
git ci -m "Include Cowboy skeleton"
Configure your Heroku app
You need to tell Heroku that you’re going to deploy an Erlang Application. To do so, you need to create a Procfile file, containing your start-up script:
$ cat Procfile
web: erl -pa ebin deps/*/ebin -noshell -noinput -s erlblog
Commit your changes to git:
$ git add Procfile
$ git ci -m "Add Procfile"
You also want to specify that your application requires Erlang R15B01:
$ cat .preferred_otp_version
OTP_R15B01
Commit your changes to git:
$ git add .preferred_otp_version
$ git ci -m "Specify R15B01 as Erlang version"
Deploy your Erlang application on Heroku
That’s the beautiful part:
$ git push heroku master
You should now be able to access the erlblog application at:
http://erlblog.herokuapp.com
If something does not work as expected, you might want to verify the logs for your Heroku app:
$ heroku logs
Profile your deployed Erlang application
Let’s now verify how many requests our erlblog application can handle. Please note that to run the steps below, you need ab and gnuplot installed on your machine.
Using ApacheBench, perform 5000 HTTP requests against your new web server, using 20 concurrent requests. Store the output in the gnuplot.dat file.
$ ab -n 5000 -c 20 -g gnuplot.dat http://erlblog.herokuapp.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking erlblog.herokuapp.com (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests
Server Software: Cowboy
Server Hostname: erlblog.herokuapp.com
Server Port: 80
Document Path: /
Document Length: 12 bytes
Concurrency Level: 20
Time taken for tests: 71.473 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 615000 bytes
HTML transferred: 60000 bytes
Requests per second: 69.96 [#/sec] (mean)
Time per request: 285.891 [ms] (mean)
Time per request: 14.295 [ms] (mean, across all concurrent requests)
Transfer rate: 8.40 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 121 130 46.4 127 1317
Processing: 126 154 60.0 140 1176
Waiting: 125 153 60.0 139 1162
Total: 248 284 75.7 268 1495
Percentage of the requests served within a certain time (ms)
50% 268
66% 275
75% 282
80% 286
90% 307
95% 343
98% 441
99% 617
100% 1495 (longest request)
Using our Heroku free tier (1 single dyno worker), our Cowboy Web Server managed to complete all 5000 requests, allowing ~70 requests per second. 90% of the requests have been served in about 300 ms. Of course, such a good result has been possible only because we surely have been hitting some kind of cache in the Heroku servers. Still, not bad for a free hosting solution for a simple Erlang applications.
We can visualize the above results using Gnuplot:
$ gnuplot
gnuplot> plot "gnuplot.dat" using 9 smooth sbezier with lines title "Cowboy Heroku Benchmarking"

The complete source code for the erlblog application is available here.