As an intermediate level developer who came from an IT background, I've always enjoyed being able to whip up an application in Rails and then quickly deploy to my own server. There are tons of great tutorials on how to publish a Rails app to production on a single server. But when I was first reading through these tutorials, I was always left with a single question. How do I know I have everything setup to handle growth?
Ultimately, that question left me sifting through the documentation for Phusion Passenger for days of my life, only to discover vague optimization advice. The optimization formulas were good, but not great. After taking my first project from a single server with only 3,000 visitors per day and little to no database usage, up to a load-balanced stack of 3 application servers, a separate database server that servers over 8,000 visitors per day that has multiple environments shared across all the servers and now has business-critical applications running, I decided it was time to share some tips and tricks.
First things first, how powerful does each individual server need to be?
Well, like everything in development, it depends. Woof, I hate that vagueness. So here are a few things to help understand server sizing.
Notice I make some assumptions here. First, you're using Phusion Passenger or a similarly non-multithreaded application server (I'm looking at you Unicorn users). If you're using Puma, Nginx Unit, or a similarly multithreaded application server then you may see better performance with smaller safety margins for high demand.
How much memory will you need?
I hated trying to figure this out. It's very frustrating when you are first getting started with your own server because you don't really know. I also found out through plenty of trial and (painful) error that certain services in Rails spin up an entire copy of the application (I'm looking at you ActionCable).
So here's some sizing information I found extremely helpful.
A single Rails app, doing basic CRUD functionality usually takes up 100-150mb of memory, per instance. I've seen that go up to 500mb when under heavy loads (like website scrapers making hundreds or thousands of requests per second).
ActionCable spins up an entire application instance of Rails that is in addition to your main app. Using ActionCable? That means a single app with ActionCable will take up 200mb-300mb of memory to run a "single" instance of your app. Passenger by default runs 3 instances of your app. So that's an easy 600mb-900mb of memory of minimal usage.
A typical Rails instance can handle hundreds or even a few thousand requests per second by itself. I'm throwing this tidbit of information out there because after seeing the numbers above, you may be like me and have a panic of what if I run out of memory because I get a traffic spike? Don't stress about it too much.
Forumula for determining total server memory needed.
Now that you know the rough values, take this handy-dandy formula and apply it to each stage of your application that you want on your server.
TotalApplicationInstances * 225 = TheoreticalApplicationMemoryNeeded
In the above formula, the TotalApplicationInstances
should be the number of Rails services that will be running including each ActionCable instance.
For example:
A typical Passenger app will have 3 primary instances, plus 3 additional services for ActionCable. So our formula would now be:
6 * 225 = 1350
That means we should assume Rails needs 1,350mb of memory to function when under a heavy load evenly across all instances on each individual server. That's around 1,000 requests per minute with basic CRUD and view rendering with an average performance-optimized app.
I typically like to have 2x the Rails app's memory available for all of the system processes. So that means I would typically aim for a 4gb Digital Ocean droplet that has 2 virtual CPUs for about $20/month. Not bad!
Now if I'm making a cluster for an actual production site, I'd have this spread across 3 servers, bringing my monthly cost to around $60 for my servers.
In fact, that's exactly how I've set up my latest project. 1 Digital Ocean managed load balancer, that round-robin distributes traffic to my web servers ($15/month, that's a no-brainer). I have 3 application servers that sit right at 30% or less CPU usage and under 40% memory usage during peak hours of the day (that's with around one hundred active users on our site at a time), and then we have a single managed Postgres database that all of the application servers connect to. The database is radically oversized, running at like $60/month.
So for around $150/month, my application serves over 180,000 website visitors, and my servers include both production and staging environments on a single cluster (separated of course). Not bad! It isn't my business, but I can tell you it employs 10 full-time employees with about another dozen near-fulltime contractors.
For anyone trying to wrap their head around infrastructure costs and scaling, I hope this really helps bring to light just how powerful a small server cluster can be! Don't fear your infrastructure, embrace it :)
Top comments (0)