Search code examples
ruby-on-railsrubynginxpassengerpuma

How can a Rails app run with Puma alone by default - without a web server


A web application that generates dynamic content requires two components:

  1. Web Servers - primarily communicate with clients, by handling HTTP requests and responses while serving content.
  2. Application Servers - on the other hand, generally sit behind a web server. If the web server can not generate the requested content via static files, it then reaches the application server to generate the dynamic content.

Software Examples

Web servers and application servers work together as components in typical web applications.

Running Rails in Production

When running a Rails app in production using passenger, some options include:

  • Running Passenger as a stand alone solution
  • Running Passenger as an app server and Apache/Nginx as the web server

Running Rails in Development

When running a Rails app in development, it is configured to use Puma by default - see Ruby Docs. Puma is an application server. How is it that by default, in Rails, Puma can run the entire web application on its own? There's no mention of a web server like Nginx or Apache in the application stack.

I don't understand how this is possible. Can someone please explain this? Puma has always been an application server, not a web server...

Thanks in advance.


Solution

  • The distinction between a "web server" and an "application server" is rather muddy and has a lot of implicit (and mostly historical) baggage.

    Generally, a web server is understood as a software which communicates via HTTP (or HTTPS) to clients and send static files as a response to requests.

    An application server on the other hand often does not communicate directly with clients (but instead with intermediate server systems in front of it, such as loadbalancers, proxy servers or well, web servers) and its main function is to respond to requests with dynamically generated content. Application servers sometimes communicate with those intermediate servers using protocols other than HTTP, such as FCGI or AJP.

    Often, we will see classic webservers (such as nginx, Apache, lighttpd) used together with an application server such as Puma, Unicorn, Thin, or Passenger. The reason for that is that those webservers are more efficient in serving static files than the application servers which are more geared towards helping the application generate dynamic responses. Also, web servers might be better suited than application servers for buffering requests and responses from clients without using a lot of resources.

    With that being said, in the last couple of decades, it became increasingly common to just use HTTP everywhere rather than to use e.g. FCGI internally. Thus, application servers are generally able to speak to HTTP clients on their own without strictly requiring an additional web server. Often, these application servers can also serve static files directly and thus take on most features of a webserver too.

    However, as written above, most webservers are magnitudes faster and more scalable when serving static files. Also, some application servers such as Unicorn are not intended to be exposed to clients directly since Unicorn does not buffer requests and responses efficiently. Instead, they rely on a frontend server such as nginx for that.

    Thus, as a conclusion: most Ruby application servers can be used without a webserver directly. With e.g. Puma this will work quite okay. To more efficiently serve static assets, or to loadbalance or protect your application, you can also introduce a webserver / proxy in front of your application server such as nginx or Apache.