Is Phoenix deployment really that hard?
How much does the Erlang's great but unexplored hot code reloading complicate the deployment as we know it?
I repeatedly hear worries of (mostly Rails) web developers about migrating to Phoenix due to the completely new deployment patterns. Although they're usually aware of many possible benefits of true zero-downtime deployments offered by OTP releases, they don't necessarily want to invest significant time and effort into mastering them right away. Well, let me explain why exactly you shouldn't be worried. And when or why you actually should.
Update: As pointed out by Paul Schoenfelder, the exrm's author, the tool should be considered as deprecated and made obsolete by his next development, distillery. I've updated the article to list the tool alongside exrm and edeliver, as well as with a link to article about it.
One of things that maybe isn't emphasized enough in Phoenix books and tutorials is that you're perfectly capable of running your deployments the old way, that is by building the app directly on the webserver and then restarting it. The Phoenix HTTP server (cowboy) can work stand-alone or behind a proxy like Nginx, which among others allows to serve many apps with one VPS.
And what if I told you that it's not just as easy as before, but it improves upon Rails in how such deployment actually works? Here's how:
- Your new version will be built when the old one is still serving responses and then it'll start very fast (~ 1s), minimizing the period when the app doesn't respond or "warm up"
- You'll be happy to discover that cowboy, the default Phoenix HTTP server used both in development and in production, is insanely efficient (enough to route traffic on Heroku)
Now, let's overview some basic options for deploying this way.
Option A: Heroku
Yes, you'll deploy your app to Heroku in no time and the process will have the same basic benefits and limitations as the one for Rails. Benefits include ultra-fast provisioning, the infrastructure with wide array of attachable services and the support. Main limitation (besides rather steep pricing) is the inability to have zero-downtime deployments (without the server restart). But that's not what we're after anyway when considering legacy deployment.
Option B: VPS + Cloudless Box
If you'd rather go with your own server, but you don't want to go for OTP releases yet and/or you want to host multiple projects on it, I recommend to provision and deploy using my very own cloudless-box cookbook for Chef. It covers setting up webserver essentials and allows to host single or multiple web apps written with Phoenix, Rails, Node, Meteor or Middleman.
This option is good for those developers who want to be in control of their web apps, feel comfortable with the CentOS shell (which always comes handy regardless of automation tools like Chef), don't want to pay for Heroku, but also don't want to install everything from scratch on their shiny new VPS. On the other hand, although I've put a lot of effort into making Cloudless Box's guides approachable for everyone, Heroku will still be easier to start with.
Side note: This very website is managed and deployed with Cloudless Box, too.
Option C: VPS + other tool (Capistrano, Mina...)
I never liked Capistrano. That's one of the reasons why I've turned to Chef for deployment. But I know many developers are used to it. You'll most certainly be able to deploy the Phoenix app with Capistrano, Mina or whatever tool you prefer. That's because there's nothing really special about such deployment. Here's what it roughly consists of:
- Checking out the repo.
- Installing Elixir dependencies.
- Compiling the app.
- Installing NPM dependencies.
- Compiling the assets.
- Digesting the assets.
- Restarting the app server.
Steps 1 and 7 are common to all legacy deployments. Step 2 is an equivalent of
bundle install in Rails. Steps 4-6 are equivalents of
rake assets:precompile in Rails. Only step 3 is something new from the Rails perspective. Other than that, it's all home sweet home.
Releases & hot reloading
OK, you can go with legacy restart policy but in the end, you do want to aim for the OTP releases. After all, it's one of those Erlang/Elixir/Phoenix features that's just wonderfully tailored for web apps. In particular, to those projects that really need high availability or long, uninterrupted websocket sessions. This makes Phoenix a perfect framework for chats or games, among others.
This form of deployment certainly becomes easier and easier to apply these days, partially thanks to the development of excellent tools like exrm, distillery and edeliver. For those of you who want to understand it all better, I can't recommend enough the following Fast Continuous Deployment of an Elixir Gameserver video, a showcase of edeliver by its authors:
Here's a bunch of other resources about Phoenix deployment in practice:
- Deploying Elixir applications with Edeliver
- How We Deploy Elixir Apps
- Elixir/Phoenix deployments using Distillery
- Deploying Phoenix Applications with Exrm
- Exrm Releases
To summarize the above video, OTP releases introduce a flow with many steps that are specific to this ecosystem. The flow roughly consists of the following unusual steps:
- Storing previous version(s) of the app.
- Compiling the new version of app with matching architecture.
- Generating the upgrade package based on old and new version.
- Placing the upgrade in proper place in the project structure.
- Running the upgrade (making the hot code reload happen).
- Presenting a task list for managing releases.
- Interfacing with the live system.
That's why you won't get away with just slight adjustment in existing toolchain. You'll need to learn and use new tools, like aforementioned exrm or edeliver as they take into account all those particular needs of the OTP release cycle. In case of edeliver, you'll also have the benefit of architecture with a separate build server covered, so it's ready to be applied in larger projects that can't afford to put the release building burden on the production server.
One of the concerns that I still have is: what exactly should the Phoenix developer be aware of release-wise when implementing actual features and writing the MVC code? For instance:
- what can we expect to happen when particular websocket connection is still open long after the release and the new version of the model (or anything else) gets used there?
- I can imagine that running database migrations, which sometimes gets really tricky with legacy deployment strategies too, may have its own set of problems with OTP releases
These are the questions that are still unanswered in my head, but I hope to change that soon, either by my own experience and testing or by reading and learning from others. Once I do, I'll surely update this section. This is one of those questions that I need to answer in order to really understand the risks of picking Phoenix for the job.
It's easy to get scared by the new release system when first arriving in the Elixir universe. Trust me, I've been there. But as I've shown you, you can quick-start with what you already know and upgrade your methodology and infrastructure later.
Deployment of OTP releases, as amazing as it is, may be hard to approach at first so it's great news that the legacy strategies still apply, as described above.