Deploying Phoenix app with mix releases
When we start to materialize our app from an idea into code, in the first days, weeks, and months, we create, develop and test our app locally, on our machine.
Inevitably, our development will result in a semi-ready or fully developed application, and regardless of whether we build our business application, for a public or private project we will have to package, and deploy Phoenix app somewhere, either we want to release it locally or on some host.
Today we will answer how to neatly and easily package our Phoenix app into a self-contained release.
We will set up mix release config and then go through other steps needed for the deployment process to succeed.
Resulting in ready-to-be-dropped into production machine package.
Let's start with a little introduction.
How we used to generate apps (most of the time)
Let's start with a little introduction. Before Elixir version 1.9+, the most reliable way to make a standalone executable from the codebase, dependencies, Erlang VM, was a library called Distillery.
Distillery your mix project and produced Erlang/OTP release.
With Elixir 1.9 release, most functionality of Distillery was merged into Mix itself, and the library was deprecated. If you used Distillery for your releases before, the transition to mix releases shouldn't be dramatic, although, there are some minor differences, be aware of that.
Welcome mix release
Now, we will explore how to set up and use mix release on the dummy phoenix app, be more than welcome to tag along.
The first step is to use mix command to create a basic project for us:
mix phx.new app
With our app created, you could right now build your release running mix release, output being:
* assembling app-0.1.0 on MIX_ENV=dev
* using config/runtime.exs to configure the release at runtime
Release created at _build/dev/rel/app
# To start your system
Once the release is running:
# To connect to it remotely
# To stop it gracefully (you may also send SIGINT/SIGTERM)
To list all commands:
This command compiled release and saved it under the path: _build/[ENV]/rel/[app name] (ENV depends on which environment you invoked mix release, and app name is just the name of your app, so in our example dev and app)
There reside all files needed to start our app, often we are only interested in
_build/dev/rel/app/bin/app, because it holds all commands needed to start, connect to, stop our application, and even invoke functions using eval (for list of all commands refer to output above)
If you open
_build/dev/rel/app/bin/ folder you can see that by default there is also a .bat file to control our app in windows environment. Of course, you could change that behavior, but how?
Configuring mix releases
To configure how mix release behaves we must go into mix.exs file and then we will focus on the project section.
def project do
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
Here we can add a releases key and in this key will reside all of the configuration for mix release. To be more specific, under your release name, your config will be staying.
You can have many different releases if you want to.
For example, let's add a basic config for production that removes executables for windows and keeps some information about BEAM files, we will call this release prod.
strip_beams: [keep: ["Docs", "Dbgi"]]
Cool, now to use this config for our release we simply call mix release with the name of desired configuration: prod.
mix release prod
There are of course many other options, if you are interested, please refer to:
A few important notes before we can proceed with deployment
We will release locally but the steps I will show you could be easily packed into some build script for the auto-deployment process on a remote machine.
There is one important matter I need to mention, when you want to deploy a release, it requires the same operating system distribution and version as the one that the release was built on.
One more thing before we start, in Elixir 1.11, config/runtime.exs file was introduced.
This configs file's purpose is to provide application configuration at runtime(meaning after compilation of our project), as opposed to compile time config files.
A few other important aspects of this file is that it's running in all
environments and that config/runtime.exs is meant to run in an environment without Mix, so functions you would use to set application variables(such as Mix.env() or Mix.target() )are not available. Instead, they were added to the Config module.
The first step is to take care of environment variables.
For our case, we only need to set DATABASE_URL (I believe it is self-explanatory) and SECRET_KEY_BASE (if you don't know, this secret is used by Endpoint configuration, it's a base to generate secrets for encrypting and signing data)
You can generate a secret using:
Now, either you can manually type:
into console or paste it into a .env file in the main directory of our project then type source .env
We should not forget to prepare dependencies for our project, we need to fetch them(only those that are needed for production)
and then compile them.
mix deps.get --only prod
MIX_ENV=prod mix compile
And also compile the app's assets
MIX_ENV=prod mix assets.deploy
Finally, you could now run mix release and start release on the local machine or ship the package to the target machine and run it there, but a few more things could help you in your releases.
mix phx.gen.release command generates three files,two files that helps us in running migration inside the release:
lib/app_name/release.ex - this module contains tasks to run migrations inside a release
rel/overlays/bin/migrate - and this one is for invoking our migrations using the Release module
third file: rel/overlays/bin/server is for running phoenix server inside a release with an environment variable: PHX_SERVER=true.
If you wonder what is a overlays folder, it is a folder where its content will be copied into every release, so we don't have to manually copy some files that we want in every release, for example our scripts to run the web server and migrations as we saw above or
Dockerfile so we can containerize our project, speaking of docker.
phx.gen.release has the option to make for us
Dockerfile and dockerignore.
Dockerfile contains all setup needed to produce the release and containerize it so we can have seamless deployment in docker container.
No complex configuration, no hours of wondering how to set up our project, just one small config and we are good to deploy elixir app and manage our package with Mix Releases. Happy deploying.