rogach

rogach

How to: Tweak mix release to work with hot code reloading

I’ve spent some time understanding how to do hot code reloading with releases built using mix release, and here I’d like to detail the steps needed, in hopes that it will help someone.

So, in order to make hot-reloadable release you need:

  1. Write an appup file (the Erlang’s Appup cookbook is a great resource for that: Appup Cookbook — Erlang System Documentation v29.0.2).
  2. Tweak the release generated by mix release, by copying the .rel & .appup files into expected places and generating the relup file. This can be done by hand, but I’ve wrote a simple function that can do that right inside the mix release task:
def project do
  [
    releases: [
      app_name: [
        include_executables_for: [:unix],
        steps: [:assemble, &release_fixup_step/1, :tar],
      ],
    ],
    ...
  ]
end

defp release_fixup_step(release) do
  releases_dir = Path.join(release.path, "releases")
  rel_file = Path.join([releases_dir, release.version, "#{release.name}.rel"])
  :ok = :release_handler.create_RELEASES(releases_dir, rel_file, [])

  # Yes, we have three copies of a same file in the same place.
  # This is necessary to appease the release_handler.
  File.cp!(rel_file, Path.join([releases_dir, release.version, "#{release.name}-#{release.version}.rel"]))
  File.cp!(rel_file, Path.join([releases_dir, "#{release.name}-#{release.version}.rel"]))

  # Copy appup file into the correct location and generate the relup.
  appup_file = Path.join("appups", "#{release.version}.appup")
  if File.exists?(Path.join("appups", "#{release.version}.appup")) do
    File.cp!(
      appup_file,
      Path.join([release.path, "lib", "#{release.name}-#{release.version}", "ebin", "#{release.name}.appup"])
    )

    {:ok, appup} = :file.consult(appup_file)
    [{appup_release_version, [{version_up_from, _}], [{version_down_to, _}]}] = appup

    if appup_release_version != to_charlist(release.version) do
      raise "Unexpected version in appup: #{appup_release_version} (expected #{release.version})"
    end

    if version_up_from != version_down_to do
      raise "Unexpected versions in appup instructions: #{version_up_from} != #{version_down_to}"
    end

    :systools.make_relup(
      ~c"#{release.name}-#{release.version}",
      [~c"#{release.name}-#{version_up_from}"],
      [~c"#{release.name}-#{version_down_to}"],
      path: [to_charlist(Path.join(release.path, "releases/*")), to_charlist(Path.join(release.path, "lib/*/ebin"))],
      outdir: to_charlist(release.version_path)
    )
  end

  release
end

Now, after running mix release you will get a release archive in _build/<env>/app_name-<version>.tar.gz.

  1. Copy this archive into the releases/ directory of your running release.
  2. Connect to the Iex shell of the running release.
  3. Carefully run the commands:
:release_handler.unpack_release(~c"app_name-<version>")
:release_handler.install_release(~c"<version>")
:release_handler.make_permanent(~c"<version>")

Where Next?

Popular in Guides/Tuts Top

tfwright
I thought I’d share a small project I’m working on to gain some familiarty with LiveView in a Phoenix app. Github Repo Deployment It’s...
New
fuelen
Hi all! Just want to share a small code snippet which allows writing CASE expressions using macro which is similar to cond. Here is an ...
New
OvermindDL1
Ran across this recently, it’s a set of cheatsheet inforgraphic things of the OTP behaviours on the BEAM: https://github.com/Telichkin/o...
New
New
dkuhlman
For those of you who might be interested in using ZeroMQ in Elixir, I converted the Erlang examples in the ZeroMQ “zguide” to Elixir. I ...
New
hauks96
Hello everyone, I created a deployment tutorial for Phoenix applications with Kubernetes (microk8s) a few months back with the goal of s...
New
nelsonic
Complete beginners Todo List Tutorial in Phoenix 1.5.3 (latest and greatest): https://github.com/dwyl/phoenix-todo-list-tutorial It’s a...
New
berts-4865
Here is a quick guide to uploading a file from the browser to DO spaces. It is crude, but will hopefully save sometime time and frustrat...
New
water
I’ll post this here, It might help someone in the future. Feedback is greatly appreciated. I use it with direnv on NixOS, It should wor...
New
nietaki
Just a quick heads up: There seems to be a bug in Erlang/OTP 21.3, which can cause some errors when making http requests. If you’re using...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 43487 311
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31013 112
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43757 214
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
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
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement