loading...
Cover image for Migrating Rails Apps to VPS - worth it?

Migrating Rails Apps to VPS - worth it?

msarit profile image Arit Amana Updated on ・3 min read

UPDATE: I decided to leave my apps on Heroku, thanks to a convo I had with @zspencer . I've reproduced our conversation below - hope it helps someone!


ARIT: Hi Zee. You recently provided a beautifully-detailed answer to my devTo question about migrating Rails from Heroku to VPS. I built these Rails apps while in coding bootcamp; their storage is linked to AWS S3 buckets and my AWS free year is coming to a close. I'd like to migrate the apps so they can use local storage on the server. Although they are client-facing, they dont use up too many resources, so I figure hosting them all on the same VPS would save me a bit of money.
I followed Oliver's migration tutorial on GoRails.com today; I got through it okay, but upon loading the server's IP, I'm getting a error:

Error ID: 35090cd9
Details: Web application could not be started by the Phusion Passenger application server.

Don't know where to go from here, and my head hurts.

ZEE: Did you run the numbers to figure out how much S3 would cost if you left the apps on Heroku? I store a few gigabytes of data in S3 and it's pennies a month.
My guess is that you'll want to try and find the logs for nginx and/or phusion passenger to figure out why the application is refusing to start.
Further, there is a risk that if you rely on your server to provide storage capacity for your application, you no longer have the uptime and redundancy that S3 provides; s3 is a really good tool for storing files very very inexpensively and making sure that you can access it later.
If you do wind up storing the data on your web server, and then the web-server starts running out of space, you'll need to upgrade the storage capacity of the web-server, which will involve taking the server down for a period of time, duplicating the disk image, and re-inflating it on a larger virtual disk.
Also it's totally OK to ask these questions on devTo :) Someone else is probably in a very similar boat and would benefit from you exposing your uncertainty!

ARIT: You've convinced me Zee :) I'll stay with S3. I'll update my devTo post.

ZEE: I mean, I don't know if that's the right call, but I am supremely lazy and I'm happy to exchange dollars for time, especially if it's pennies on the hour or day.

ARIT: Lol. No, you've really convinced me - especially your argument about scaling. And you're right - S3 isn't that expensive for low storage use and queries. Plus, the process of migrating just 1 of my apps was so headache-inducing! 😫

ZEE: Wait until you have customers on the app and it goes down at 3am on a Tuesday and you thought adding a "call me when it goes down" alert was a good idea and you wake up to a phone screeching and you have to re-figure out what the hell is going on while groggy.

ARIT: πŸ˜†


Old Post Follows Below:

Hello DevTo Fam! I need some help.
I'd like to migrate my Rails apps from Heroku to a VPS on Vultr. There are several guides on how to do this for one Rails app. However, I would like to host all 5 of my apps on the same VPS, utilizing a name-based hosting approach to resolve all their domains. This is where I'm not finding much online that is helpful. One guide makes use of the Passenger server, but I understand that Puma is considered better. For a web server, I'm partial to Nginx.
I would really appreciate any help in mapping out how to accomplish this. Thank you so much in advance! πŸ™πŸΎ

Posted on by:

msarit profile

Arit Amana

@msarit

Software Engineer. Former Public Health Analyst. Coding Bootcamp Grad. Mentor to aspiring and early-career female devs.

Discussion

pic
Editor guide
 

Hey Arit!

I always get nervous when migrating off of a platform such as Heroku and onto ones own Virtual Machines. I help clients do it all the time, but before we proceed I make sure I really understand why they're doing it. Heroku does an amazing job of encapsulating a lot of the costs of hosting an app, so much so that it becomes easy to take what it does for granted. Hosting an app with any significant traffic on a self-maanged virtual machine will likely result in someone needing to respond to an issue in off-hours; which means ~12x more bad-days per year.

You will also now need a database provider with a backup and recovery strategy; so that you don't lose all that user data if the virtual machine the database is running on falls over. Oh, and if your traffic spikes and you need a bigger machine right now? Well, hope-to-god your investment in infrastructure automation wasn't neglected so you can spin up a replacement web or database server within a few minutes; like you can on Heroku.

This is compounded by the fact that most Virtual machine providers do not guarantee individual machines up-time. The expectation is that you will have multiple machines operating behind a load balancer, so that if an instance falls over, the other instance will stay up.

That said, it would be 100% shitty if I second-guessed your question and moonwalked away; so I'll try and provide the help you asked for and not the help you didn't.

To understand how to go about solving the problem, I'm going to restate the question and unpack the assumptions I'm making.

How do I go about hosting multiple Rails applications on a virtual machine, then directly requests to the correct application based upon which sub-domain the request is coming from?

There are two main problems to solve:

  1. How do I configure, deploy and run multiple rails applications on a single virtual machine?
  2. How do I route traffic from sub-domains to different rails applications on a single server?

I'll start with problem 1 and then go into problem 2.

> How do I configure, deploy and run multiple rails applications on a single virtual machine?

First, a story. Because I'm old and a little high. A long, long time ago I mostly wrote PHP code. The really cool thing about PHP is that the when you write a PHP program, you can literally copy and paste the files from your personal computer into a folder on the server and the program is ran by the server. Just like that! No re-booting, no "dynos deploying." This worked because the PHP language's interpreter is ran inside the process of the Apache web server itself as an extension.

Each PHP file was, in effect, it's own application and each HTTP request would run that particular application from start to finish. This meant only one process, Apache, was listening to port 80 and 443 forΒ HTTP and HTTPS requests and taking responsibility for choosing which program would handle it.

That's what Phusion/Passenger does - It runs as many Ruby/Rails applications as you want within a single(ish) process. Nginx winds up doing the heavy lifting of the http stuff, while Phusion/Passenger runs the Ruby code and makes sure each application is as isolated as it can be.

Puma, however, follows a different process model. Puma expects that it will be running a single application across many processes. Puma then exposes a single Rack-compliant application (In your case, a Rails app) on a single network port to provide access to that application. You would then run Nginx and configure it to direct traffic that matches a particular pattern to that particular port.

The pros and cons of each solution, single-process, many apps or single-app, many processes is not obvious. Which is part of why people complain "Rails doesn't scale" or "Rails doesn't perform." The single-process, many applications solution is perfect for environments where you "scale vertically." Scaling vertically is when you increase the disk, processor and memory performance of a server to better support high traffic. This is perfect for processes that may take a while to start, or if each request has a lot of work to do before it can safely be returned to the client. If you were to have many processes handling requests, you would likely see the server CPU "thrashing" with a bunch of processes fighting to use up all 100% of the processor power and failing because there's really only 2~4 "real" processors on the virtual machine.

Single-app, many process designs such as Puma + Nginx are well suited to situations where individual response time must be incredibly low, and a lot of the work can be delegated until after the response is sent to the HTTP client. Each request may take very little processing power to execute upon, so having a number of processes handling each request is unlikely to result in a lot of thrashing.

To be honest, picking the "right" scaling model becomes one of those really good problems to have! The nice thing about Rails applications is it's possible to use either or, and switch based upon how your application winds up being used. Here's two guides from linode on running Nginx with Rails:

I apologize for mixing and matching linux distributions, but I couldn't find good instructions for different technologies on matching distros :(.

OK, on to question two!

> How do I direct traffic to the appropriate application based upon the sub-domain?

Story time again! Apache and PHP allowed you to direct traffic to the appropriate application using mod-rewrite Someone coming to accounts.your-company.example.com? Host up the PHP files in ~/site/accounts! Done! Nginx also provides a mechanism for this using their block configuration. There are a number of really powerful ways to make routing decisions and I can't walk you through each of them, but the one you'll want to look out for to do sub-domain level direction to different applications is server_name; and the one you want to look out for to point it at which application on which port is proxy_pass

I am super lazy and tend to reference this full example of the nginx configuration options when I'm trying to figure out my nginx configuration.

THere is a third, secret, unasked question which is:

> How do I keep track of all this stuff so I can spin the server back up if it goes down?

The answer to that is there is an entire swathe of tools designed for storing your server configuration and then using that configuration to re-create servers. I personally prefer ansible but I haven't had to do server management in a very long time, so the cool kids may be using something else nowadays. Back in the day I used Capistrano which still looks like it's maintained and is designed for Ruby people.

Good luck! Hopefully this wasn't too rambly and if you have further questions I'll do my best to reply more specifically.

 

Wow Zee! THANK YOU! I owe you a coffee :) Please look for a direct message for me, where I share more about my particular situation. I'm so grateful.

Note: I think you need to follow me to allow me direct-message you :)

 

Hmm, not sure how direct messages work on here, but if you go to my twitter or github you'll be able to find my email, and my twitter DMs are always open.

If you follow one another you can DM.

Open DMs coming at some point.

And yeah, amazing response.

 

You may want to checkout hatchbox if you're not trying to do this configuration on your own. Chris has done an amazing job with it and has a ton of great information on guides on his site GoRails. He was actually just talking about the difference between Puma and Passenger.

Which should I use? Passenger or Puma?

First, let's take a quick rundown of the difference between the two.

Passenger is an NGINX module that manages and runs your Rails apps. It installs alongside NGINX which means you don't have to manage a separate process for it. If NGINX is running, then Passenger is running.

Puma, on the other hand, is a separate process. It's going to run your Rails app, but you'll have to setup some scripts to manage the service manually and make sure that it gets restarted when it crashes, etc.

Passenger has an open source version as well as a enterprise version you can pay for. Puma is completely free and open source.

I'm sure you're thinking: "Great, but that doesn't help me decide!"

Here's a quick pros and cons list of Puma vs Passenger.

Passenger

Pros
  • Super-fast and managed seamlessly with NGINX
  • Well documented and easy to install
  • Easy debugging when app is broken
  • Supports Python and Node apps too
Cons
  • Multi-threading and other features are expensive

And now for Puma...

Puma

Pros
  • Completely free and open source
  • Supports both multi-process and multi-threading
  • Comes with Rails now by default
Cons
  • Manual setup, and process management required
  • Harder to debug when your app is broken

Passenger also wrote up a comparison between the two that goes into more detail. You can find that here.

Here's the thing:

Puma can be really tricky to learn if you're a first-timer deploying Rails to production. You have to learn SystemD, how process management works, and a fair bit about Linux to get it working reliably. It isn't going to hold your hand if you deploy broken code. Your app will just stop responding and you'll have to know where to look for the logs to figure out what's going wrong.

Passenger's pretty good at alerting you when something goes wrong. It'll display an error page and tell you to check the logs immediately. The direct integration with NGINX makes it easier to work with too as you don't have to learn anything about SystemD to get it running.

For beginners, I always recommend Passenger.
Since Passenger is so easy to use and friendly when errors occur, it's what I recommend to most people. If you're new to running Rails in production on your own servers, it's an easy recommendation.

Unless your site has massive traffic, you'll get along well on Passenger's free version. It's what I use on GoRails.com every day and serves up 2M pageviews a year without breaking a sweat.

And just a quick reminder, you can always change this later on. Just pick one and get your app in production. πŸ‘

Either way you go passenger or puma, self-hosted or using hatchbox, definitely report back on your findings!

 

Hi Drew! I decided to stay on Heroku lol. I've updated my blog post to reflect this decision. Thanks for your thorough response - I feel so supported πŸ€—

 

Absolutely! I remember when we moved from AWS to DigitalOcean. It was super cool to be setting up my own servers but also super scary to being configuring from scratch. First one I did took me a whole day, but I can spin a new one up and have an app deployed in under an hour now. My suggestion is no matter if you're doing it barebones or using a service like Heroku take notes!

 

Can't tell you much about running rails apps in parallel, but I'd guess that's probably the easier part anyway. If you're ok with using nginx, you can easily configure 5 server blocks, each with their own server_name (the subdomain) that reverse proxies to the rails instance either through localhost or unix domain sockets (no idea if rails supports those). From there you can use nginx to gzip data, handle https for all of your apps in one place, do some caching, take care of server-push, etc. Overall a good option if you want scalability. If you're feeling adventurous, I'd suggest going with openresty instead of vanilla nginx; the added benefit of lua scripting comes with almost no cost and may allow for some easier customization.

 

Thank you DarkWiiPlayer, I'm grateful :)