benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Always use Releases

Correct if me I’m wrong, as best I can tell there aren’t any reasons to use mix run --no-halt in production vs releases. The marginal value always outweighs the marginal cost.

Marginal cost:

I’ve seen a lot of folks think that using releases means you MUST have a separate build vs application server/image/whatever. Heroku is a good example. You have a buildpack that contains Elixir and you compile your app with mix. What is the marginal cost of using releases on heroku?

Just one extra command! mix release, and then you run ./bin/myapp foreground instead of mix run --no-halt

Marginal value:

The marginal cost is low, what value do we get for that cost? There are some handy defaults surrounding remote console capability and heart (although frankly I’m a bit unclear about in what scenarios it’s going to act).

The PRIMARY value though is that releases will eagerly load all of your application and dependency code, whereas mix does so lazily.

For projects with many dependencies, there can be a massive latency spike when you first get requests / jobs that occur after starting your app with mix while it loads all your code. This can cause timeouts and error cascades. Sure the supervisor trees generally restart but there are situations where you exceed supervisor restarts per second limits and you end up having enormous portions of your app restart.

Conclusion.

Marginal cost / benefit: (handy defaults + EAGER CODE LOADING) / (one extra command)

Am I missing something? This seems like a hands down win for always using releases.

Most Liked

bitwalker

bitwalker

Leader

In my opinion, nobody should be building releases on their production host - they shouldn’t even have build tools installed on that host. You build your releases on your build host, and push those to production and then pull the trigger on rolling it out when you’re ready. This can be entirely automated, automated with some manual intervention, or done entirely manually.

I’ve set up and used different approaches depending on what I’m working on - with Docker/Kubernetes, there were dedicated hosts in the cluster which handled builds, and transfers of those images to the production hosts were super fast because they were colocated. Similarly, with a more traditional setup, we used Jenkins to build releases, and scp them to staging/production hosts and unpack them. My current project, we’re actually shipping virtual machines, so it’s an entirely different situation, but we’re still using releases (which are ultimately installed on the host via rpms).

The release tarballs are certainly a bit fat if you are including ERTS, but if your build and prod hosts are the same, then you don’t need to do that, and can omit ERTS, which is by far the bulk of the baggage the tarball carries.

17
Post #9
sasajuric

sasajuric

Author of Elixir In Action

I agree. I believe that OTP releases for systems (aka services that need to run continuously), and escripts for CLI tools are the way to go. Maybe there are some special cases, but can’t think of any.

I wouldn’t say that eager loading is the primary value, although it depends on the use case. I suspect that many systems are not that highly loaded for that to make a difference.

I do think that the defaults you mention, which allows remote logging and start the system as the node (so you can remote observe it) are more important.

But above all, I like the self-contained, isolated nature of the release. You won’t pickup some unexpected module from somewhere else (e.g. mix app is not included in OTP release, so less chances of invoking Mix.env at runtime), and you can easily run multiple different systems (services) with different versions of Elixir/Erlang.

I also agree that the cost is marginal. But even if it takes longer to properly setup your own build and deployment, it’s a one-off cost. Pay the price once, reap the benefits many times :slight_smile:

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Releases have no issue with environment variables. REPLACE_OS_VARS=true, and then in your config just foo: "${FOO}". We use identical images in staging and production, and do all config changes by specifying environment variables in our docker containers.

bitwalker

bitwalker

Leader

@thousandsofthem Validation/transformation of the raw config prior to use is best left for init functions in the relevant parts of your application, you have to do validation at these points anyway, I see no reason why one would prefer config.exs for that - I only ever use config.exs to define default values, everything else is handled internally in the application. In my apps, I’ve usually extracted common config logic into a Config module which I then use in init callbacks of my application. You can also use start phases (or just the start callback) to pull config from external systems (for example Vault or something), and do what you need to with it prior to starting your application’s supervisor tree. There are other options as well, such as my conform library, which allows you to do complex validation/transforms of config pulled from either an ini-style config file or from environment variables, and this transformed config is then used by the release.

I often wish that José had gone a different route with config.exs - we wouldn’t be having this conversation, because everyone would just do configuration the way I described above, which is how it’s been done in Erlang for years, and this would be a non-issue, but instead people get used to doing everything in config.exs, and then find that it’s a pain to transition away from it when they want to start using releases - well, yeah, it sucks you have to do that, but it’s not that configuring releases is somehow terrible, because it’s really not, it’s that you have to refactor your code to handle configuration differently. And yes, that’s a valid complaint, but placing the blame on releases is not.

LostKobrakai

LostKobrakai

I’ve not used releases by now, but from what I’ve read about it in slack or otherwise online it seems to involve a lot of quirks and setup to get it to work properly. Installing elixir and using mix can be done by copy pasting a few (well documented) lines of code in your ssh session and the app is running. Comparing that to potentially fiddling around with building the release, dealing with any issues coming up and having to find solutions to those issues it doesn’t sound so marginal. Especially thinking of someone just coming into the language.

I still feel like releases are the better option long term and if releases are working for someone there’s probably no reason at all for mix run, but for me – coming from the upload-file-and-done php world – it feels nice to also have the option to skip all the ceremony. Even if it might be just for the quick and simple success feeling for novices in elixir.

Where Next?

Popular in Guides/Tuts Top

cheerfulstoic
I spent quite a bit of time trying to find a good configuration to build / test my Elixir app in CircleCI and then push an image to Docke...
New
zenw0lf
Hello all! For those wanting to try your hands at Elixir / Phoenix, I wrote a comprehensive tutorial on doing a simple JSON API with sai...
New
9mm
So I’m really loving elixir. BY FAR the most excruciating piece of learning a functional language for me is having to “transform” all my ...
New
hlx
Typed my very first blog post ever, hope it will help some people https://henricus.xyz/roll-your-own-email-password-authentication-with...
New
jtormey
Hello! Having written a lot of LiveView code, I’ve made some VS Code snippets to speed up writing callbacks for LiveViews and LiveCompon...
New
jshprentz
Geoffrey Lessel’s 2019 book, Phoenix in Action, was written for Phoenix 1.4. I found that the book’s code examples did not match the cur...
New
OvermindDL1
Ran across this recently, it's a set of cheatsheet inforgraphic things of the OTP behaviours on the BEAM:
New
lukertty
Install web-mode and mmm-mode first and put this in your config file: (require 'mmm-mode) (require 'web-mode) (setq mmm-global-mode 'may...
New
anuragg
We just published a guide to automatic clustering in Elixir 1.9, with Mix releases and libcluster. The cluster automatically discovers n...
New
kevinlang
Hey all, With Phoenix 1.6 just around the corner, I figured I’d make a tutorial on how to add Bulma to a new Phoenix 1.6 project. By lev...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New

We're in Beta

About us Mission Statement