<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam McCrea</title>
    <description>The latest articles on DEV Community by Adam McCrea (@adamlogic).</description>
    <link>https://dev.to/adamlogic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F113283%2F7cef63ea-79cb-42d5-912d-fae91a32f01b.jpeg</url>
      <title>DEV Community: Adam McCrea</title>
      <link>https://dev.to/adamlogic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamlogic"/>
    <language>en</language>
    <item>
      <title>Power Tools for Analyzing your Heroku logs</title>
      <dc:creator>Adam McCrea</dc:creator>
      <pubDate>Tue, 17 Aug 2021 20:14:49 +0000</pubDate>
      <link>https://dev.to/adamlogic/power-tools-for-analyzing-your-heroku-logs-6cb</link>
      <guid>https://dev.to/adamlogic/power-tools-for-analyzing-your-heroku-logs-6cb</guid>
      <description>&lt;p&gt;Your log data is a treasure-trove of information about your application, but it can be overwhelming. This post will dig into several strategies for extracting metrics and other helpful information from your logs. We’ll start with the basics of the &lt;code&gt;heroku logs&lt;/code&gt; command, then we’ll dig into the real fun using a tool called &lt;a href="https://github.com/rcoh/angle-grinder"&gt;Angle Grinder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q2IAuJXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://railsautoscale.com/assets/static/agrind-by-time.6c33384.b68ae288b428c2a8fc3119b2652f993c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q2IAuJXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://railsautoscale.com/assets/static/agrind-by-time.6c33384.b68ae288b428c2a8fc3119b2652f993c.gif" alt="Angle Grinder real-time aggregation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to view your Heroku logs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-logs"&gt;&lt;code&gt;heroku logs&lt;/code&gt;&lt;/a&gt; on its own just prints the most recent logs from your app and then exits. Generally that's not very useful. I almost always want the &lt;code&gt;-t&lt;/code&gt; (or &lt;code&gt;--tail&lt;/code&gt;) option to continually tail my logs. Additionally I usually want it scoped to a specific dyno process, so I’ll include &lt;code&gt;-d router&lt;/code&gt;, &lt;code&gt;-d web&lt;/code&gt;, or &lt;code&gt;-d worker&lt;/code&gt; so I’m only seeing relevant logs.&lt;/p&gt;

&lt;p&gt;Here's how I would tail my router logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router

2021-07-28T16:23:07.870849+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.8&amp;amp;pid=4" host=api.railsautoscale.com request_id=0ce66277-877c-4d4f-91c4-2c1075089b41 fwd="3.84.54.241,172.70.34.122" dyno=web.7 connect=1ms service=156ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.774247+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.3&amp;amp;pid=81" host=api.railsautoscale.com request_id=fe46b69d-8938-4d41-a566-4c837050f6da fwd="3.85.98.203,172.69.62.61" dyno=web.14 connect=1ms service=14ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.627308+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=11" host=api.railsautoscale.com request_id=f5b69be4-8283-48f6-b683-30c051b4f51d fwd="34.232.107.232,172.70.42.94" dyno=web.11 connect=0ms service=327ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.740752+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.4&amp;amp;pid=4" host=api.railsautoscale.com request_id=89d2da0d-e1d8-484d-99f2-bf26921ba9a5 fwd="3.249.54.29,162.158.158.123" dyno=web.11 connect=0ms service=354ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.881220+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=24539" host=api.railsautoscale.com request_id=37171239-4524-46cf-9dce-7cdda8bd4ace fwd="3.95.158.194,172.70.34.173" dyno=web.20 connect=2ms service=34ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.743590+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=50" host=api.railsautoscale.com request_id=bb063a51-8d83-4284-b26a-db31c3f4b485 fwd="54.204.194.19,172.70.34.122" dyno=web.8 connect=1ms service=19ms status=204 bytes=358 protocol=https
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scoping it further
&lt;/h2&gt;

&lt;p&gt;Even with the &lt;code&gt;-d&lt;/code&gt; filter, it’s usually way too much data to be useful, so I’ll want to filter it even more. This is where chaining with &lt;code&gt;grep&lt;/code&gt; is useful.&lt;/p&gt;

&lt;p&gt;Here I'll tail my worker logs, but only show the entries that include "StatWorker":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d worker | grep StatWorker

2021-07-28T16:28:11.718082+00:00 app[worker.18]: pid=4 tid=1fzk class=StatWorker jid=bae74463b8a3c977aaaac6b4 args=3061,2021-07-28T16:27:50Z measure#sidekiq.queued=1069 tag#worker=StatWorker tag#queue=default INFO: start
2021-07-28T16:28:11.763375+00:00 app[worker.18]: pid=4 tid=1fy4 class=StatWorker jid=2c4de245191802b57344bae8 args=2344,2021-07-28T16:27:50Z measure#sidekiq.queued=933 tag#worker=StatWorker tag#queue=default elapsed=0.181 INFO: done
2021-07-28T16:28:11.782176+00:00 app[worker.18]: pid=4 tid=1fy4 class=StatWorker jid=c704e40d09cf93b628d023f3 args=3000,2021-07-28T16:27:50Z measure#sidekiq.queued=1129 tag#worker=StatWorker tag#queue=default INFO: start
2021-07-28T16:28:11.809741+00:00 app[worker.18]: pid=4 tid=1fzk class=StatWorker jid=bae74463b8a3c977aaaac6b4 args=3061,2021-07-28T16:27:50Z measure#sidekiq.queued=1069 tag#worker=StatWorker tag#queue=default elapsed=0.092 INFO: done
2021-07-28T16:28:11.820178+00:00 app[worker.18]: pid=4 tid=1fzk class=StatWorker jid=7af1852ff673a9f747d8f4ad args=1920,2021-07-28T16:27:50Z measure#sidekiq.queued=1171 tag#worker=StatWorker tag#queue=default INFO: start
2021-07-28T16:28:11.719832+00:00 app[worker.20]: pid=4 tid=13c4g class=StatWorker jid=c531a39bf63886c8ce386c13 args=3826,2021-07-28T16:27:50Z measure#sidekiq.queued=922 tag#worker=StatWorker tag#queue=default elapsed=0.149 INFO: done
2021-07-28T16:28:11.721947+00:00 app[worker.20]: pid=4 tid=13c4g class=StatWorker jid=4e2884278d99fb616eb452b1 args=1546,2021-07-28T16:27:50Z measure#sidekiq.queued=1073 tag#worker=StatWorker tag#queue=default INFO: start
2021-07-28T16:28:11.761234+00:00 app[worker.20]: pid=4 tid=8rk60 class=StatWorker jid=9f54c9b8418801e4274f0fe7 args=2191,2021-07-28T16:27:50Z measure#sidekiq.queued=928 tag#worker=StatWorker tag#queue=default elapsed=0.184 INFO: done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses a &lt;a href="https://www.geeksforgeeks.org/piping-in-unix-or-linux/"&gt;"pipe"&lt;/a&gt; to send the output of &lt;code&gt;heroku logs&lt;/code&gt; as input to the &lt;a href="https://en.wikipedia.org/wiki/Grep"&gt;&lt;code&gt;grep&lt;/code&gt; utility&lt;/a&gt;. Grep searches plain text data using regular expressions. We're not using any regexp syntax in this example, but we certainly could.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chaining &lt;code&gt;heroku logs -t&lt;/code&gt; with &lt;code&gt;grep&lt;/code&gt; is how I’m most often interacting with my Heroku logs, but sometimes I need more power.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting specific values
&lt;/h2&gt;

&lt;p&gt;It's time to meet &lt;a href="https://github.com/rcoh/angle-grinder"&gt;Angle Grinder&lt;/a&gt;, a real-time log analysis power tool. It can parse many formats, extract values, and even perform aggregations. Let’s just dive right in with an example.&lt;/p&gt;

&lt;p&gt;Heroku’s router logs are formatted the same for every Heroku app, so let’s use those. Here’s the same router log output we saw above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router

2021-07-28T16:23:07.870849+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.8&amp;amp;pid=4" host=api.railsautoscale.com request_id=0ce66277-877c-4d4f-91c4-2c1075089b41 fwd="3.84.54.241,172.70.34.122" dyno=web.7 connect=1ms service=156ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.774247+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.3&amp;amp;pid=81" host=api.railsautoscale.com request_id=fe46b69d-8938-4d41-a566-4c837050f6da fwd="3.85.98.203,172.69.62.61" dyno=web.14 connect=1ms service=14ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.627308+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=11" host=api.railsautoscale.com request_id=f5b69be4-8283-48f6-b683-30c051b4f51d fwd="34.232.107.232,172.70.42.94" dyno=web.11 connect=0ms service=327ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.740752+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.4&amp;amp;pid=4" host=api.railsautoscale.com request_id=89d2da0d-e1d8-484d-99f2-bf26921ba9a5 fwd="3.249.54.29,162.158.158.123" dyno=web.11 connect=0ms service=354ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.881220+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=24539" host=api.railsautoscale.com request_id=37171239-4524-46cf-9dce-7cdda8bd4ace fwd="3.95.158.194,172.70.34.173" dyno=web.20 connect=2ms service=34ms status=204 bytes=358 protocol=https
2021-07-28T16:23:07.743590+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=50" host=api.railsautoscale.com request_id=bb063a51-8d83-4284-b26a-db31c3f4b485 fwd="54.204.194.19,172.70.34.122" dyno=web.8 connect=1ms service=19ms status=204 bytes=358 protocol=https
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll notice that the metadata is all formatted as &lt;code&gt;[key]=[value]&lt;/code&gt;. This format is called &lt;a href="https://brandur.org/logfmt"&gt;&lt;code&gt;logfmt&lt;/code&gt;&lt;/a&gt;. It's embraced heavily by Heroku, and it’s one of the many formats that Angle Grinder understands.&lt;/p&gt;

&lt;p&gt;Go follow the &lt;a href="https://github.com/rcoh/angle-grinder#installation"&gt;Angle Grinder installation instructions&lt;/a&gt; then come back to follow along.&lt;/p&gt;

&lt;p&gt;Let's use Angle Grinder's logfmt parsing for Heroku router logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind '* | logfmt'

[2021-07-28T16:50:11.654197+00:00=None][at=info][bytes=358][connect=1ms][dyno=web.2][fwd=54.154.157.222,162.158.159.109][heroku[router]:=None][host=api.railsautoscale.com][method=POST][path=/api/REDACTED/v2/reports?dyno=web.3&amp;amp;pid=79][protocol=https][request_id=d4ecf840-666b-430f-81c3-082b4e58c876][service=29ms][status=204]
[2021-07-28T16:50:11.450697+00:00=None][at=info][bytes=358][connect=4ms][dyno=web.18][fwd=34.233.123.109,172.69.62.81][heroku[router]:=None][host=api.railsautoscale.com][method=POST][path=/api/REDACTED/v2/reports?dyno=web.5&amp;amp;pid=242][protocol=https][request_id=1599b9f0-17ec-48ae-8341-5d917bcbd598][service=19ms][status=204]
[2021-07-28T16:50:11.644107+00:00=None][at=info][bytes=358][connect=1ms][dyno=web.16][fwd=3.84.186.26,172.70.38.19][heroku[router]:=None][host=api.railsautoscale.com][method=POST][path=/api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=9][protocol=https][request_id=2bef6858-458f-47f9-9e97-8c35c4d9ebef][service=12ms][status=204]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, that's really not any better than before. We can see that the key-value pairs were parsed, but it's still just a wall of text.&lt;/p&gt;

&lt;p&gt;Let's try again, this time using the &lt;code&gt;--format&lt;/code&gt; option to only display the "connect" and "service" metrics from the router logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind '* | logfmt' --format '{connect} {service}'

2ms 9ms
1ms 160ms
1ms 15ms
0ms 6ms
4ms 192ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're getting somewhere! Let's expand the formatting string to display even more data, along with some defined widths so we have control over the alignment. We'll also split the command into multiple lines to make it more readable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '* | logfmt' \
  --format '{dyno:&amp;lt;6} {connect:6} {service:7} {status:4} {method:5}  {path}'

web.3     0ms    52ms  204  POST  /api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=37
web.23    1ms    17ms  204  POST  /api/REDACTED/v2/reports?dyno=web.2&amp;amp;pid=4
web.4     1ms    40ms  204  POST  /api/REDACTED/v2/reports?dyno=web.3&amp;amp;pid=186
web.1     1ms    17ms  204  POST  /api/REDACTED/v2/reports?dyno=web.1&amp;amp;pid=86
web.13    1ms    10ms  204  POST  /api/REDACTED/v2/reports?dyno=web.21&amp;amp;pid=65
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beautiful! &lt;strong&gt;We've broken the wall of text into a nicely-aligned, human-readable stream of output.&lt;/strong&gt; You can use this to watch your web traffic in real-time. From this we could quickly see if a particular dyno is struggling, or if a particular path is being requested more frequently than we expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Angle Grinder work?
&lt;/h2&gt;

&lt;p&gt;Okay, let's peel apart that last example.&lt;/p&gt;

&lt;p&gt;First we tail Heroku logs using the Heroku CLI, just like before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we pipe the output to &lt;code&gt;agrind&lt;/code&gt; (just like we were piping to &lt;code&gt;grep&lt;/code&gt; before). &lt;code&gt;agrind&lt;/code&gt; takes a single argument—called the "query"—which tells Angle Grinder how to process the incoming text. Queries can get complex as we'll soon see, but this example keeps it simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind '* | logfmt'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the query is &lt;code&gt;* | logfmt&lt;/code&gt;, and we enclose it in single quotes to separate it from the rest of the command. It might look a bit odd seeing the "pipe" character within the query string, so take care to not confuse it with a command line pipe.&lt;/p&gt;

&lt;p&gt;The Angle Grinder query syntax is a composition of an initial filter with a series of operators. Here's how this query breaks down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; – This is the initial filter, and it tells Angle Grinder to operate on every input line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logfmt&lt;/code&gt; – This is an operator, and it extracts key/value fields using the logfmt syntax.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first operator is generally a parser. Next we'll see how subsequent operators would then operate on the extracted fields.&lt;/p&gt;




&lt;p&gt;✉️ Want notified when we write more articles like this one? Sign up for the &lt;a href="https://masteringheroku.substack.com/"&gt;Mastering Heroku&lt;/a&gt; mailing list! Less than one email per month, always relevant, and no sales pitches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Aggregating your log data in real-time
&lt;/h2&gt;

&lt;p&gt;With a steady volume of requests, what we've seen so far can still be an overwhelming amount of information. Depending on what we're looking for, we can use &lt;a href="https://github.com/rcoh/angle-grinder#aggregate-operators"&gt;Angle Grinder's aggregate operators&lt;/a&gt; to &lt;strong&gt;summarize log data in real-time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, here's a snapshot of 50th and 95th percentile service times by dyno:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '* | logfmt | p50(service), p95(service), count by dyno | sort by p50 desc'

dyno         p50        p95        _count
------------------------------------------------------
web.2        57         369        622
web.3        40         287        622
web.1        36         114        598
web.7        18         306        612
web.5        15         93         641
web.6        13         41         631
web.4        9          25         576
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our Angle Grinder query has gotten a bit more complex, with several operations chained together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; – Operate on every input line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logfmt&lt;/code&gt; – Extract fields using the logfmt parser.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p50(service), p95(service), count by dyno&lt;/code&gt; – Calculate three aggregates grouped by the "dyno" field. This assumes a "dyno" field was extracted via &lt;code&gt;logfmt&lt;/code&gt;, which it was. The output of this operation is three fields: "p50", "p95", and "_count".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort by p50 desc&lt;/code&gt; – Sort the aggregate results by the "p50" field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could change the order of these fields, rename them using "as", or query multiple fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '* | logfmt | count as count, p95(connect) as connect, p95(service) as service by dyno | sort by p50 desc'

dyno          count        connect        service
--------------------------------------------------------------
web.3         618          1              84
web.1         693          1              93
web.6         623          2              62
web.4         624          1              20
web.5         674          2              561
web.2         673          2              573
web.7         621          4              191
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query is getting hard to read, so I'd split it to many lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '*
    | logfmt
    | count as count,
      p95(connect) as connect,
      p95(service) as service
      by dyno
    | sort by p50 desc'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parsing more than just logfmt
&lt;/h2&gt;

&lt;p&gt;One query I find very handy is grouping by the second. This let’s me see a &lt;strong&gt;live second-by-second summary of how my app is performing&lt;/strong&gt;. I'll use this when there's a production bottleneck so I can see exactly when the issue is resolved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '*
    | parse "T*." as time
    | logfmt
    | count as count,
      p95(connect) as connect,
      p95(service) as service
      by time
    | sort by time'

time            count        connect        service
----------------------------------------------------------------
19:20:36        354          2              1452
19:20:37        492          2              825
19:20:38        519          1              740
19:20:39        513          3              270
19:20:40        435          2              74
19:20:41        451          4              76
19:20:42        468          4              82
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query introduces a new parser called &lt;code&gt;parse&lt;/code&gt;. For this aggregation, I needed the time string truncated to the second, and that’s not available in the logfmt data. &lt;code&gt;parse&lt;/code&gt; gives us the power of arbitrary string matching to pull out values from anywhere in the log line.&lt;/p&gt;

&lt;p&gt;Here an example raw router log for reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2021-07-28T16:23:07.870849+00:00 heroku[router]: at=info method=POST path="/api/REDACTED/v2/reports?dyno=web.8&amp;amp;pid=4" host=api.railsautoscale.com request_id=0ce66277-877c-4d4f-91c4-2c1075089b41 fwd="3.84.54.241,172.70.34.122" dyno=web.7 connect=1ms service=156ms status=204 bytes=358 protocol=https
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the timestamp precedes the logfmt data, and it is &lt;em&gt;not&lt;/em&gt; formatted as a logfmt key/value pair.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parse "T*." as time&lt;/code&gt; extracts a single field, "time", using the pattern &lt;code&gt;T*.&lt;/code&gt;. The asterisk (&lt;code&gt;*&lt;/code&gt;) is a wildcard, and anything it consumes is extracted as a field. So a single asterisk means a single field is extracted. The text between &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt; is &lt;code&gt;16:23:07&lt;/code&gt;, so that becomes the value for "time".&lt;/p&gt;

&lt;p&gt;From there, it's all what we've seen before: &lt;code&gt;logfmt&lt;/code&gt; to parse the logfmt data, then aggregate operations, then sorting the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional aggregates
&lt;/h2&gt;

&lt;p&gt;One last example to show how we can use conditional logic in our queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku logs -t -d router | agrind \
  '*
    | parse "T*." as time
    | logfmt
    | count(status == 200) as successful,
      count(status != 200) as failure,
      p95(service) as service,
      p95(connect) as connect
      by time
    | sort by time desc'

time            successful        failure        service        connect
------------------------------------------------------------------------------------
19:41:58        189               244            1803           1
19:41:57        182               120            2076           1
19:41:56        191               163            2479           1
19:41:55        174               122            2300           1
19:41:54        176               97             2196           1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This example is taken from a different app, and it's not doing so well. Over half of the requests are failing.&lt;/em&gt; 😱&lt;/p&gt;

&lt;p&gt;Still, it's pretty cool that we can use conditionals in our &lt;code&gt;count&lt;/code&gt; aggregate using extracted logfmt fields like "status".&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;Hopefully by now you've installed Angle Grinder, and you're having some fun slicing and dicing your own log data.&lt;/p&gt;

&lt;p&gt;Our Heroku log stream can be completely overwhelming, but tools like &lt;code&gt;heroku logs&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and especially &lt;code&gt;agrind&lt;/code&gt; give us everything we need to get useful and actionable information from the firehose.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>devops</category>
      <category>heroku</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Use Heroku For Free</title>
      <dc:creator>Adam McCrea</dc:creator>
      <pubDate>Fri, 21 May 2021 16:12:59 +0000</pubDate>
      <link>https://dev.to/adamlogic/how-to-use-heroku-for-free-52h4</link>
      <guid>https://dev.to/adamlogic/how-to-use-heroku-for-free-52h4</guid>
      <description>&lt;p&gt;Heroku is the easiest way to deploy and host a server-rendered web app, and it doesn't have to cost an arm and a leg. In fact, &lt;strong&gt;for some apps you can do it for free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This guide covers everything you need to know about using Heroku for free, including free dynos, the free Postgres database plan, and free add-ons.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Heroku work?
&lt;/h2&gt;

&lt;p&gt;Heroku runs your app on "dynos". This is just Heroku's word for "container" or "server". For your app to run and serve requests, you need at least one web dyno running. Adding more web dynos will increase your capacity to handle a higher volume of requests.&lt;/p&gt;

&lt;p&gt;Heroku offers &lt;a href="https://www.heroku.com/pricing#containers" rel="noopener noreferrer"&gt;six dyno types&lt;/a&gt;, each increasing in price and performance characteristics. At the lowest tier are the free dynos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-14.46.09%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-14.46.09%402x.png" alt="Heroku dyno types &amp;amp; pricing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running a free dyno literally costs you nothing, and it will always cost you nothing.  No matter how many hours they run or what you have remaining in your free dyno quota (more on this below), &lt;strong&gt;you will never be charged for running free dynos.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Free dyno limitations
&lt;/h2&gt;

&lt;p&gt;Naturally, "free" comes with some limitations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Free web dynos will idle (sleep) after a period of inactivity. The next request will wake it up, but users will notice a delay while the app is started.&lt;/li&gt;
&lt;li&gt;  You have a limited number of "free dyno hours" each month. When you've exhausted your quota, your app shuts down.&lt;/li&gt;
&lt;li&gt;  Free dynos are &lt;a href="https://devcenter.heroku.com/articles/heroku-teams#pricing-and-limits" rel="noopener noreferrer"&gt;not available for Heroku Teams&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Free dynos do not support &lt;a href="https://devcenter.heroku.com/articles/ssl" rel="noopener noreferrer"&gt;Heroku SSL&lt;/a&gt; for custom domains.&lt;/li&gt;
&lt;li&gt;  Heroku metrics are not available for free dynos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those limitations might seem like dealbreakers, but fear not! We have workarounds for many of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent free dynos from sleeping/idling
&lt;/h2&gt;

&lt;p&gt;Free dynos will &lt;a href="https://devcenter.heroku.com/articles/free-dyno-hours#dyno-sleeping" rel="noopener noreferrer"&gt;sleep (shut down) after 30 minutes of inactivity&lt;/a&gt;. This is actually a &lt;em&gt;feature&lt;/em&gt; to avoid using up your free dyno hours. &lt;strong&gt;This makes free dynos great for staging/test environments that don't have constant usage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want your free web dynos to run 24/7, you'll need to ensure they receive at least one request every 30 minutes. &lt;strong&gt;The easiest way to prevent your dynos from sleeping is using an uptime monitor that pings your app all day long.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are &lt;em&gt;lots&lt;/em&gt; of uptime monitor services, and there's a good chance you're already using one. If you need a free one (you're reading about free dynos, right?), I've had success with free plans from &lt;a href="https://www.freshworks.com/website-monitoring/" rel="noopener noreferrer"&gt;Freshping&lt;/a&gt; and &lt;a href="https://uptimerobot.com/" rel="noopener noreferrer"&gt;UptimeRobot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that &lt;strong&gt;dyno sleeping&lt;/strong&gt; *&lt;strong&gt;&lt;em&gt;only applies to web dynos&lt;/em&gt;&lt;/strong&gt;*. If you're running a "worker" dyno (asynchronous/background job processing), it will not go to sleep. It's &lt;em&gt;very&lt;/em&gt; easy to consume your free dyno hours quota with worker dynos.&lt;/p&gt;

&lt;h2&gt;
  
  
  How are free dyno hours calculated?
&lt;/h2&gt;

&lt;p&gt;Heroku &lt;a href="https://devcenter.heroku.com/articles/usage-and-billing#cost" rel="noopener noreferrer"&gt;prorates dyno usage to the second&lt;/a&gt;. If you run a free dyno for 30 minutes, that'll use 0.5 hours from your free dyno hours quota. If you run an app with single free dyno 24/7 for 31 days, that'll use 744 (24 x 31) of your free dyno hours.&lt;/p&gt;

&lt;p&gt;*&lt;strong&gt;&lt;em&gt;So how many free dyno hours do you get?&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://devcenter.heroku.com/articles/free-dyno-hours" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Accounts are given a base of 550 free dyno hours each month. In addition to these base hours, accounts which verify with a credit card will receive an additional 450 hours added to the monthly free dyno quota. This means you can receive a total of 1000 free dyno hours per month, if you verify your account with a credit card.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty simple. Add a credit card, and you'll have 1,000 free dyno hours each month. Since a full month is at most 744 hours, this means &lt;strong&gt;you can run a single web dyno 24/7 for free&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Heroku trivia
&lt;/h3&gt;

&lt;p&gt;In 2015-2016 there was a time when free dynos could &lt;a href="https://blog.heroku.com/heroku-free-dynos" rel="noopener noreferrer"&gt;only run 18 hours a day&lt;/a&gt;, but this is &lt;a href="https://devcenter.heroku.com/changelog-items/907" rel="noopener noreferrer"&gt;no longer the case&lt;/a&gt;. Free dynos will run 24 hours a day as long as you have sufficient hours available in your quota.&lt;/p&gt;

&lt;p&gt;If you had a free app running when these new rules &lt;a href="https://blog.heroku.com/announcing_heroku_free_ssl_beta_and_flexible_dyno_hours#flexible-free-dyno-hours" rel="noopener noreferrer"&gt;launched in 2016&lt;/a&gt;, you may have been grandfathered in with more than 1000 free dyno hours available to you. Lucky you!&lt;/p&gt;




&lt;h2&gt;
  
  
  Check your free dyno usage
&lt;/h2&gt;

&lt;p&gt;So how many free dyno hours do *&lt;strong&gt;&lt;em&gt;you&lt;/em&gt;&lt;/strong&gt;* have available? Check out your &lt;a href="https://dashboard.heroku.com/account/billing" rel="noopener noreferrer"&gt;Heroku billing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-15.06.42%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-15.06.42%402x.png" alt="Heroku account billing page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a great way to check up on any forgotten apps that are consuming your free dynos quota!&lt;/p&gt;

&lt;h2&gt;
  
  
  Free vs. Hobby vs. Standard-1x dynos
&lt;/h2&gt;

&lt;p&gt;Heroku's &lt;a href="https://devcenter.heroku.com/articles/dyno-types" rel="noopener noreferrer"&gt;six dyno types&lt;/a&gt; are &lt;em&gt;generally&lt;/em&gt; more powerful as they get more expensive, but that's not the case for cheapest three.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-15.07.56%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-15.07.56%402x.png" alt="Heroku dyno types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free, Hobby, and Standard-1X dynos are exactly the same hardware with the same performance characteristics.&lt;/strong&gt; Hobby dynos only lift a few of the feature limitations of Free dynos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Hobby dynos do not go to sleep.&lt;/li&gt;
&lt;li&gt;  Hobby dynos support Heroku SSL for custom domains.&lt;/li&gt;
&lt;li&gt;  There's no "hours quota" for Hobby dynos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One shared limitation between Free and Hobby dynos is that you cannot scale to more than one dyno per process. If you need to scale up to two web dynos, for example, you must use standard or performance dynos.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSL for free dynos
&lt;/h2&gt;

&lt;p&gt;SSL is no longer optional these days—you must serve your web app via HTTPS. On free dynos, you have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Serve your app from the default Heroku URL. Every Heroku app is available by default at [app-name].herokuapp.com, and all of these URLs include SSL support out of the box.&lt;/li&gt;
&lt;li&gt;  If you need a custom domain, you'll need to use a service like &lt;a href="https://support.cloudflare.com/hc/en-us/articles/200170416-End-to-end-HTTPS-with-Cloudflare-Part-3-SSL-options#h_4e0d1a7c-eb71-4204-9e22-9d3ef9ef7fef" rel="noopener noreferrer"&gt;Cloudflare's Flexible SSL&lt;/a&gt; (also free). Your users will connect via HTTPS to Cloudflare, and Cloudflare will connect via HTTP to your Heroku app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If neither of these options are acceptable to you, you'll need to use one of the paid dyno types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a production app on free dynos
&lt;/h2&gt;

&lt;p&gt;Since you can run a free dyno (which is &lt;em&gt;equivalent to a standard-1x dyno&lt;/em&gt;) for 1,000 hours per month, *&lt;strong&gt;&lt;em&gt;that means you can run your production app 24/7 on Heroku for free, right?&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;

&lt;p&gt;Well, that depends.&lt;/p&gt;

&lt;p&gt;Most production apps need at least a web process and a background worker process for things like sending email and communicating with third-party services. This requires a "worker dyno" in addition to your "web dyno", which exceeds your 1,000 hour allotment if you run them both all month. And free worker dynos do not automatically sleep like free web dynos do, so they'll be running 24/7 unless you manually scale them down.&lt;/p&gt;

&lt;p&gt;This is where you need to decide just how "production-scale" your app needs to be right now. You could perform this work synchronously within your web process instead of backgrounding it, or you could find a way to &lt;a href="https://github.com/brandonhilkert/sucker_punch" rel="noopener noreferrer"&gt;run async jobs within your web process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some background work could also be performed on a periodic basis with &lt;a href="https://devcenter.heroku.com/articles/scheduler" rel="noopener noreferrer"&gt;Heroku Scheduler&lt;/a&gt;, which creates a "one-off dyno" and only uses dyno hours for the time it's running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you really do need a separate worker process (and most full-stack production apps do), then you've probably outgrown free dynos.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When to upgrade your free dynos
&lt;/h2&gt;

&lt;p&gt;If you're already running your app on free dynos, how will you know when you've outgrown them? If your app is a successful product with growing traffic, you will certainly hit a point where a single free dyno is insufficient.&lt;/p&gt;

&lt;p&gt;You can't scale free dynos horizontally—in other words, you can't add more than one free web dyno to your app. If one dyno is not enough for you, you'll need to upgrade to standard dynos.&lt;/p&gt;

&lt;p&gt;The best way to know when you're outgrowing free dynos is by watching your metrics. Since &lt;a href="https://devcenter.heroku.com/articles/metrics" rel="noopener noreferrer"&gt;Heroku's metrics tab&lt;/a&gt; is only available on paid plans, check out &lt;a href="https://elements.heroku.com/addons/librato" rel="noopener noreferrer"&gt;Librato's free add-on&lt;/a&gt; for similar metrics and alerting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.20.18%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.20.18%402x.png" alt="Librato metrics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're seeing high request times and/or HTTP errors, there's a good chance you're outgrowing free dynos.&lt;/p&gt;

&lt;p&gt;It's also possible your app is just slow. An APM tool like &lt;a href="https://elements.heroku.com/addons/scout" rel="noopener noreferrer"&gt;Scout&lt;/a&gt; will help you drill down into specific bottlenecks. A full tour of Scout is beyond the scope of this article, but generally speaking, high request queue time indicates a capacity issue (need more dynos).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.25.38%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.25.38%402x.png" alt="Scout APM screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're seeing lots of time spent in other parts of your stack (Ruby, Postgres, etc.) then adding more dynos will not help. You'll need to investigate your app code to resolve the performance issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;✉️ Want notified when I write more articles like this one? Sign up for my&lt;/em&gt; &lt;a href="https://masteringheroku.substack.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Mastering Heroku&lt;/em&gt;&lt;/a&gt; &lt;em&gt;mailing list! Very infrequent, very relevant, no sales pitches.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Heroku's free Postgres database
&lt;/h2&gt;

&lt;p&gt;Whew! All that and we haven't even talked about your database yet. Much like dynos, Heroku offers multiple tiers of Postgres.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.31.50%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frails-autoscale.ghost.io%2Fcontent%2Fimages%2F2021%2F05%2FCleanShot-2021-05-13-at-16.31.50%402x.png" alt="Heroku Postgres options screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ouch. &lt;strong&gt;Heroku's &lt;a href="https://elements.heroku.com/addons/heroku-postgresql" rel="noopener noreferrer"&gt;free Postgres&lt;/a&gt;is limited to 10,000 records&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even the smallest production apps are likely going to exceed this limitation. Here are your options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Upgrade to a paid Heroku database plan, starting at $9/month for 10,000,000 records.&lt;/li&gt;
&lt;li&gt;  Use a third-party hosted database provider with a more generous (but still quite limited) free plan, like &lt;a href="https://www.elephantsql.com/plans.html" rel="noopener noreferrer"&gt;ElephantSQL&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's really no way around it. Your database will eventually be the most expensive layer of your stack, so be prepared to pay for it early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free Heroku add-ons
&lt;/h2&gt;

&lt;p&gt;One of the best parts of Heroku is the add-on ecosystem, and many add-ons have excellent free plans. Here are some of my favorites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/librato" rel="noopener noreferrer"&gt;Librato&lt;/a&gt; — As mentioned this earlier, Librato gives you visibility into your app metrics above and beyond what Heroku provides (and Heroku provides no metrics for free dynos).&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/scout" rel="noopener noreferrer"&gt;Scout APM&lt;/a&gt; — Monitor and diagnose app performance issues. I install this on every app.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/logentries" rel="noopener noreferrer"&gt;Logentries&lt;/a&gt; — You'll need a way to view your application logs. Logentries isn't the best option, but it has the most generous free plan.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/sentry" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt; — Fantastic error monitoring service with generous free plan.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/rediscloud" rel="noopener noreferrer"&gt;Redis Cloud&lt;/a&gt; – Heroku has their own Redis service, but you'll get more for your money (or for free) with Redis Cloud.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://elements.heroku.com/addons/autoidle" rel="noopener noreferrer"&gt;AutoIdle&lt;/a&gt; — This one isn't a free option, but it's super useful if you've upgraded to paid dynos and you want to save money when your app isn't being used. It essentially replicates the "sleeping" behavior of free dynos. Very nice for staging/QA apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;So with all this in mind, when do Heroku's free offerings make sense for &lt;em&gt;you&lt;/em&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Launching a side project.&lt;/strong&gt; There's a good chance the side project won't get much traction at first, so throw it on free dynos and a free Postgres DB and see what happens! You won't have enough free hours to run worker dynos, but who cares... until you have real users and revenue, you can do everything in your web process.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Running a staging or QA app.&lt;/strong&gt; These are perfect for free dynos because they don't need to run all the time. Your staging data might even fit within a free Postgres DB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond those use cases, you're probably going to have to pay for Heroku. The free options exist to try out the service, play with an idea, and run experiments. Once you're running a "real app", you're hopefully at a point where you can spend a few dollars on hosting.&lt;/p&gt;

&lt;p&gt;Questions or thoughts on this article? Let's chat &lt;a href="https://twitter.com/adamlogic" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>rails</category>
      <category>devops</category>
    </item>
    <item>
      <title>How many Heroku dynos do you need, and which size—An opinionated guide</title>
      <dc:creator>Adam McCrea</dc:creator>
      <pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/adamlogic/how-many-heroku-dynos-do-you-need-and-which-size-an-opinionated-guide-4fec</link>
      <guid>https://dev.to/adamlogic/how-many-heroku-dynos-do-you-need-and-which-size-an-opinionated-guide-4fec</guid>
      <description>&lt;p&gt;Heroku makes it effortless to deploy our web apps when we're just getting started, but anyone who's scaled an app on Heroku knows that there are still &lt;em&gt;lots&lt;/em&gt; of decisions to make. Topping the list are &lt;em&gt;"which dyno type should I use?"&lt;/em&gt; and &lt;em&gt;"how many dynos do I need?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z2Xo9RIK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/decisions.b1f4eea.956125503a85f55fd01d47e02fb96ffc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z2Xo9RIK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/decisions.b1f4eea.956125503a85f55fd01d47e02fb96ffc.jpg" alt="Decision fatigue" title="Photo by Victoriano Izquierdo on Unsplash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're feeling overwhelmed or confused, you're not alone. It's not always clear which dyno type is best and how many we need, but by the end of this article, we'll all be experts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you don't care about the details and just want a recommendation, here it is:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Standard-2x dynos offer the best combination of price and performance for most apps. If you encounter memory quota warnings on 2x dynos, you should make the jump to Performance-L. For either dyno type, autoscaling is the only way to know how many dynos you should be running.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How did I land on that recommendation? Let's dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Heroku dyno types&lt;/li&gt;
&lt;li&gt;How are the dyno types different?&lt;/li&gt;
&lt;li&gt;Free dynos&lt;/li&gt;
&lt;li&gt;Hobby dynos&lt;/li&gt;
&lt;li&gt;Standard-1x dynos&lt;/li&gt;
&lt;li&gt;Heroku routing and "in-dyno concurrency"&lt;/li&gt;
&lt;li&gt;Multiple processes on a standard-1x dyno&lt;/li&gt;
&lt;li&gt;Standard-2x dynos&lt;/li&gt;
&lt;li&gt;Performance-M dynos&lt;/li&gt;
&lt;li&gt;Performance-L dynos&lt;/li&gt;
&lt;li&gt;How many dynos do you need?&lt;/li&gt;
&lt;li&gt;Dyno calculations in the real world&lt;/li&gt;
&lt;li&gt;Automation is the answer&lt;/li&gt;
&lt;li&gt;Putting it all together&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Heroku dyno types
&lt;/h2&gt;

&lt;p&gt;Heroku offers &lt;a href="https://devcenter.heroku.com/articles/dyno-types"&gt;six "Common Runtime" dyno types&lt;/a&gt;. These are often referred to as dyno "sizes" since the more expensive ("larger") dynos typically offer more memory and CPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Of_u2Gm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dyno-types.b5d8c32.a136cdd5fedfb5062491ee745c53bfc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Of_u2Gm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dyno-types.b5d8c32.a136cdd5fedfb5062491ee745c53bfc5.png" alt="Heroku dyno types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Heroku also offers &lt;a href="https://www.heroku.com/dynos/private-spaces"&gt;"Private"&lt;/a&gt; and &lt;a href="https://www.heroku.com/shield"&gt;"Private Shield"&lt;/a&gt; dynos, which offer increasing levels of security compliance. The performance characteristics of these dyno types are almost identical to their Common Runtime counterparts, so we'll focus on the six Common Runtime dyno types.&lt;/p&gt;

&lt;h2&gt;
  
  
  How are the dyno types different?
&lt;/h2&gt;

&lt;p&gt;A few important supplemental notes to the comparison chart above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monthly dyno cost from top to bottom: $0 (Free), $7 (Hobby), $25 (Standard-1x), $50 (Standard-2x), $250 (Perf-M), $500 (Perf-L).&lt;/li&gt;
&lt;li&gt;Free, Hobby, and Standard-1x dynos are identical performance-wise, but Heroku imposes some feature limitations on Free and Hobby. More on these limitations below.&lt;/li&gt;
&lt;li&gt;The two Performance-level dynos run on dedicated hardware. The four Standard dynos run on shared hardware, where we're susceptible to "noisy neighbors" and somewhat ambiguous processing power (Heroku does not explain what the "Compute" metric represents).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that as our high-level view, let's go into each dyno type in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free dynos
&lt;/h2&gt;

&lt;p&gt;Free dynos automatically shut down during periods of inactivity and can only run for a limited number of hours. As I explain in my &lt;a href="https://railsautoscale.com/heroku-free-dynos/"&gt;deep dive into Heroku's free dynos&lt;/a&gt;, these limitations make free dynos a poor fit for production applications. Heroku provides enough free hours to run a single dyno continuously for a month, but if we need a worker dyno for background processing (most apps do), we will not have enough free dyno hours.&lt;/p&gt;

&lt;p&gt;Free dynos are for great for demos, experimentation, and perhaps a staging app. &lt;strong&gt;For production apps, we need to pay.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hobby dynos
&lt;/h2&gt;

&lt;p&gt;Hobby dynos are identical in performance to Standard-1x dynos, and they don't have the limited hours and automatic shutdown constraints of Free dynos. Sounds great, right?&lt;/p&gt;

&lt;p&gt;The catch is a limitation in how we scale our dynos. We can run multiple Hobby dynos if they're different process types (web and worker dynos, for example), but we can't run multiple dynos of the same type. &lt;strong&gt;This means that if we ever need to scale to multiple web dynos, Hobby dynos are not an option.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qs7eHKtM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/hobby-dynos.42db587.457caa5ba51d655af94fb405cee5a541.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qs7eHKtM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/hobby-dynos.42db587.457caa5ba51d655af94fb405cee5a541.png" alt="Hobby dynos limited to a single web or worker dyno"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if a single Hobby dyno is sufficient for our current traffic, running a production app on a single dyno is risky. Remember that Free, Hobby, and Standard dynos run on shared architecture. This means a noisy neighbor can slow our app down. Or maybe an unexpected spike in traffic has saturated our dyno. There are many ways a dyno can enter a "bad state", and running on a single dyno is a single point of failure.&lt;/p&gt;

&lt;p&gt;Later on we'll go deeper on how many dynos to run, but for now let's rule out Hobby dynos since they prevent having any redundancy in our web dynos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard-1x Dynos
&lt;/h2&gt;

&lt;p&gt;Standard-1x dynos are the first of the "professional" dynos—dynos that don't have any feature restrictions upon them. Feature restrictions aside, these dynos are identical to Free and Hobby dynos. I know I've said that multiple times, but it bears repeating.&lt;/p&gt;

&lt;p&gt;I've also mentioned that Standard dynos run on shared hardware, making them susceptible to noisy neighbors—tenants running on the same hardware that are consuming more than their fair share of resources. Noisy neighbors on Standard dynos are a real thing, and they're tough to detect and mitigate.&lt;/p&gt;

&lt;p&gt;That doesn't rule out Standard dynos altogether, though. At a fraction of the price of Performance dynos, we can run many more Standard dynos for the same cost, helping mitigate possible performance issues caused by noisy neighbors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real problem with Standard-1x dynos is memory.&lt;/strong&gt; With a limit of 512 MB, many apps will exceed that quota with even a single process. Running multiple processes per dyno is critical, and we're going to take a little digression to discuss why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Heroku routing and "in-dyno concurrency"
&lt;/h2&gt;

&lt;p&gt;When a user requests a page on our web app, Heroku's router decides which of our web dynos receives the request. Ideally, the router would know how busy each dyno is, and it would give the request to the &lt;em&gt;least busy&lt;/em&gt; dyno.&lt;/p&gt;

&lt;p&gt;But that's not how it works.&lt;/p&gt;

&lt;p&gt;Heroku's router uses a &lt;a href="https://devcenter.heroku.com/articles/http-routing#request-distribution"&gt;random routing algorithm&lt;/a&gt;. It doesn't care about the size of the request, the path of the request, or how busy each web dyno might be. This means our dynos will inevitably receive an unfair share of large or slow requests, at least some of the time. &lt;strong&gt;It also means that if a web dyno can only process a single request at a time, we've introduced a dangerous bottleneck into our system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--edvRj4hl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/random-routing.419d072.368461eb296827a55408b0cf180ab75c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--edvRj4hl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/random-routing.419d072.368461eb296827a55408b0cf180ab75c.jpg" alt="Heroku random routing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we have no "in-dyno concurrency", a single slow request can cause other requests to back up inside the dyno. This is when we start to see &lt;a href="https://railsautoscale.com/docs/questions/#what-is-request-queue-time"&gt;request queue time&lt;/a&gt; increase. It's a combination of not running enough web dynos, and those dynos not having sufficient concurrency.&lt;/p&gt;

&lt;p&gt;No matter how many dynos we run, it's critical that each dyno can process multiple requests concurrently. In Ruby, this means running multiple web processes—usually Puma workers. Running multiple threads doesn't cut it. Ruby threads do provide a bit of concurrency (especially when there's a lot of I/O), but due to the GVL, it's not true concurrency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple processes on a Standard-1x dyno
&lt;/h2&gt;

&lt;p&gt;This an important point when considering a Standard-1x dyno, because most Rails apps will consume far more than 512 MB when running mulitple Puma workers.&lt;/p&gt;

&lt;p&gt;An easy way to test this is to run your app for 24 hours on Standard-2x dynos (which we'll discuss next) with 2 Puma workers. Usually this is accomplished by setting &lt;code&gt;WEB_CONCURRENCY&lt;/code&gt; to "2", and ensuring that you've uncommented the "workers" line in your Puma config. Check your memory usage on your Heroku dashboard, and you should see it start to level off after a few hours (it's normal for it to increase initially).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3B498FRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/heroku-memory-24-hours.ae78f0b.cc9a3be24367975875c40ca1c29af32e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3B498FRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/heroku-memory-24-hours.ae78f0b.cc9a3be24367975875c40ca1c29af32e.png" alt="Heroku memory graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For most of us, this experiment will show 2 processes consuming somewhere between 500-1000 MB. Since running at least 2 processes is a must, and Standard-1x dynos are limited to 512 MB, Standard-1x is rarely a viable option.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pro tip: One way to decrease memory usage for Ruby apps on Heroku is by using the &lt;a href="https://elements.heroku.com/buildpacks/gaffneyc/heroku-buildpack-jemalloc"&gt;Jemalloc buildpack&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Standard-2x dynos
&lt;/h2&gt;

&lt;p&gt;Standard-2x dynos are identical with Standard-1x, with double the memory and CPU at double the price.&lt;/p&gt;

&lt;p&gt;If you're wondering how a single Standard-2x dyno is any different than two Standard-1x dynos, it's all about the memory and concurrency. While most apps are constrained by memory to a single process in a 1x dyno, the 2x dyno opens up the possibility of running multiple processes.&lt;/p&gt;

&lt;p&gt;Compare these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Two 1x Dynos running 1 web process:&lt;/em&gt; Heroku routes requests randomly between the two dynos. Inevitably it will make bad decisions, sending requests to a busy dyno when another is available for work.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;One 2x Dyno running 2 web processes:&lt;/em&gt; The web processes will balance the requests coming into the dyno, ensuring that an available process always gets the next request. Same cost, better concurrency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Standard-2x dynos in-dyno concurrency possible while costing a fraction of Performance dynos.&lt;/strong&gt; That's why I recommend them for most apps.&lt;/p&gt;

&lt;p&gt;But not all apps can squeeze multiple processes onto a 2x dyno. Rails apps that consume 1 GB or more memory with two processes are not uncommon, and for those apps there's Performance dynos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance-M dynos
&lt;/h2&gt;

&lt;p&gt;Perf-M dynos are the first of two "Performance" dyno options. Looking at the cost and performance characteristics relative to other dyno types, we can see there's not a lot of value here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perf-M dynos have &lt;strong&gt;less than one fifth the memory of Performance-L dynos at half the cost&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Perf-M have &lt;strong&gt;only 2.5x the memory of Standard-2x dynos at 5x the cost&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not much more to say about these. They're just a bad deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance-L dynos
&lt;/h2&gt;

&lt;p&gt;Perf-L dynos cost $500/month per dyno—10 times the cost ($50/month) of Standard-2x—but we get what we pay for. With a 14 GB memory quota, we can easily run several app processes in a single dyno.&lt;/p&gt;

&lt;p&gt;Performance dynos run on dedicated architecture, so we don't have to worry about noisy neighbors. With Standard dynos, performance differences can be noticeable between dynos, such as after restarting or deploying. Performance dynos don't have this issue. The performance is very consistent from one dyno to the next.&lt;/p&gt;

&lt;p&gt;Still, the performance variability with Standard dynos can be mitigated by running more of them, so &lt;strong&gt;I only recommend Perf-L dynos for apps that are memory-constrained on 2x dynos.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want notified when I write more articles like this one? Sign up for my &lt;a href="https://masteringheroku.substack.com"&gt;Mastering Heroku&lt;/a&gt; mailing list! Very infrequent, very relevant, no sales pitches.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How many dynos?
&lt;/h2&gt;

&lt;p&gt;Once we've selected a dyno type, the next logical question is "how many of them?" &lt;strong&gt;Let's run a quick calculation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll assume an example app that takes 100ms to process each request. This translates to 10 requests per second without any concurrency. If we're running two processes per dyno, this takes us to 20 requests per second per dyno.&lt;/p&gt;

&lt;p&gt;We can look at Heroku's throughput chart so see how many requests per second the app receives. Let's assume this example app maxes out at 200 requests per second.&lt;/p&gt;

&lt;p&gt;The math is straightforward: If we can handle 20 req/s per dyno, we'll need 10 dynos to handle 200 req/s. Here's the formula:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--beTlzfgi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dynos-needed-calculation.0e08fc2.901cf2614054b626b7e4508848312aec.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--beTlzfgi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dynos-needed-calculation.0e08fc2.901cf2614054b626b7e4508848312aec.jpg" alt="Formula for calculating dynos needed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only problem is that this example app doesn't exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dyno calculations in the real world
&lt;/h2&gt;

&lt;p&gt;If we looked at a real app instead of a hypothetical app, we'd see some stark differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response times are not consistent. Some endpoints are slow, some are fast.&lt;/li&gt;
&lt;li&gt;Requests don't show up evenly. We might get a burst of 1,000 requests in one minute, then just a trickle of traffic for the next 10 minutes.&lt;/li&gt;
&lt;li&gt;Traffic patterns change throughout the day, week, and year.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attempting to use the calculation above in a real-world app is fraught with error. I've been there and felt the pain. We're bound to find ourselves in one of these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We under-provision our dynos, and our app struggles to keep up. Requests are queueing because we don't have enough capacity to serve them, and our users experience slow page requests. &lt;strong&gt;Unsure of how to fix the problem, we crank up the dynos.&lt;/strong&gt; Now we're in the next scenario.&lt;/li&gt;
&lt;li&gt;We over-provision our dynos, and our app is performing just fine. Unfortunately, &lt;strong&gt;we're paying for extra capacity we don't need.&lt;/strong&gt; This can be really frustrating, and it leads to the many claims of Heroku being too expensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how can we determine how many dynos to run, having confidence that our app will stay fast, without paying any more than necessary?&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation is the answer
&lt;/h2&gt;

&lt;p&gt;Instead of trying to manually calculate something that's changing every second, let's make software do it for us. That's exactly what an autoscaler does: it continually calculates how many dynos are needed &lt;em&gt;right now&lt;/em&gt; based on live metrics.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://railsautoscale.com"&gt;built an autoscaler&lt;/a&gt; that does this better than anything out there, but this isn't a sales pitch for Rails Autoscale—it &lt;em&gt;is&lt;/em&gt; a pitch for autoscaling in general. There's just no reason to do it the hard way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every production app should have autoscaling in place.&lt;/strong&gt; Even if your production app receives little traffic and runs fine on a single dyno, that dyno is a single point of failure. Running a single dyno without autoscaling is an invitation for slowdowns at best, a production outage at worst. Think of it as a low-cost safety net.&lt;/p&gt;

&lt;p&gt;Autoscaling on Heroku is easy and cheap. With it we avoid the painful battles of being under-provisioned, and we avoid paying for unnecessary capacity when over-provisioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;We started with six dyno types, and narrowed them down to two: Standard-2x and Performance-L.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ogWsTdU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dyno-types-reduced.e9ea414.93d1ed724f4b16fbd6f99c409d1391bf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ogWsTdU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://railsautoscale.com/assets/static/dyno-types-reduced.e9ea414.93d1ed724f4b16fbd6f99c409d1391bf.jpg" alt="Standard-2x and Performance-L Dynos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend all apps &lt;em&gt;try&lt;/em&gt; Standard-2x dynos first, running two app processes per dyno. If the 1 GB memory quota proves insufficient for your app, then make the jump to Perf-L. (Or better yet, find out why your app is consuming so much memory!)&lt;/p&gt;

&lt;p&gt;Regardless of dyno type, always set up autoscaling on production apps. &lt;strong&gt;The cost of this setup will rival any non-Heroku option, and it'll run smoothly for years with little manual intervention.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>heroku</category>
    </item>
  </channel>
</rss>
