Developing with Elixir
Elixir is seriously awesome. It’s kinda funny, with the dozens of languages that our dev team has used for various projects over the last decade, there have been two languages that seem to have garnished the most affinity: Erlang and Ruby.
We love Erlang because of the runtime efficiency, but we also love Ruby because of the developer productivity. So naturally, when we caught wind that Jose Valim and the folks at Plataformatec were creating a new language that brings developer productivity to the Erlang VM, we were intrigued.
I say intrigued… but, that is a serious understatement! At the time we were running more than a dozen Erlang services in production and between them, a combined 60 million requests a day. Erlang was (and still is) a serious part of our operation. So I took the very next project and banged it out in Elixir. Even though it wasn’t a great fit for Elixir, it was awesome, and I was hooked.
Fast-forward about four years and now our team has a completely new focus: Nanobox - A portable, micro platform for developing and deploying apps. Our mission is to help developers be more productive, and as a result, help organizations succeed. As Elixir is a language designed for developer productivity without compromising runtime performance, it’s a natural fit. So much so, we’ve spent a significant amount of time developing a native Elixir development environment and deployment engine.
Elixir Deployment Options
So you have an Elixir app and you want to deploy it. You’ll need to decide first, how you want to run your app once it’s in production. There are two ways to do this with Elixir: with mix, or to create standalone releases.
Mix is the build tool that ships with elixir. It provides the framework for application configuration, dependency management, running tests, compiling your application, and generally anything else that you or a dependency might wire it to do.
By default, this is how Elixir apps are built and run. Because most Elixir dependencies, like Ecto for example, depend on mix for configuration and for running tasks, it becomes really hard to imagine not having mix in production.
Though it is possible, wiring up your release to handle Ecto migrations and other routines becomes something you have to think about and configure. Unless you really need releases, which I’ll discuss next, I’d suggest sticking with mix.
A release is essentially your compiled application, the Elixir and Erlang runtime systems, and a collection of scripts to manage your app, all packaged together. With releases, there are two major advantages:
- It is a distributable runtime that can run anywhere, and
- You can generate subsequent releases and your running application can be upgraded without having to be restarted.
The downside, however, is that once you’ve generated a release, you’ve left the context of the Elixir environment, and thus the mix build tool is not available to your app. In fact, once you’ve generated a release, there is really nothing to distinguish your app at that point from any other Erlang release (yes, I said Erlang).
Elixir Local Development
As with any deployment workflow, I need to first get my app running in a local development environment. The app I’ll be using is available on Github if you’d like to explore: https://github.com/tylerflint/nanobox-phoenix-example. Nanobox doesn’t natively support Elixir releases yet, so I’m going to run the app with mix in production.
Nanobox uses Docker to provision an isolated Elixir development environment on my local machine. Once I’m ready to deploy, it’ll make deploying my Pheonix app to a live server really simple.
Create a boxfile.yml
boxfile.yml defines my environment configuration, including my Elixir runtime and supporting data services. I’ll create a
boxfile.yml with the following and place it in the root of my project.
run.config: # elixir runtime engine: elixir # we need nodejs in development # ensure inotify exists for hot-code reloading dev_packages: - nodejs - inotify-tools # cache node_modules cache_dirs: - node_modules # add node_module bins to the $PATH extra_path_dirs: - node_modules/.bin # enable the filesystem watcher fs_watch: true deploy.config: # generate the static assets digest extra_steps: - mix phoenix.digest # just before the new process comes online, # let's migrate the database before_live: web.main: - mix ecto.create --quiet - mix ecto.migrate # add postgres as a data component data.db: image: nanobox/postgresql:9.4 web.main: start: node-start mix phoenix.server
boxfile.yml will give me an Elixir runtime with a Postgres database in both local and production environments. It will also provide a webserver when deployed to live servers.
Modify Postgres Configuration
Because my app needs to be portable between environments, I’m going to use environment variables to populate my Postgres connection. Nanobox will auto-generate these environment variables in each environment.
# Configure your database config :phoenix_crud, PhoenixCrud.Repo, adapter: Ecto.Adapters.Postgres, username: System.get_env("DATA_DB_USER"), password: System.get_env("DATA_DB_PASS"), hostname: System.get_env("DATA_DB_HOST"), database: "phoenix_crud_dev", pool_size: 10
Setup Your Local Environment
To setup my local environment, just as a matter of convenience, I’ll add a dns alias for my local app so I can access easily from a browser. I’ll then use the
nanobox run command to spin up my app locally then drop into a console inside my Elixir environment. Once there I’ll seed my database.
# add a convenient way to access the app nanobox dns add local phoenix.dev # start my local dev env and drop into a console nanobox run # create and seed my database mix do ecto.create, ecto.migrate
Run the App Locally
With my database seeded, I’m ready to fire up Pheonix. From inside the Nanobox console:
I can then access my running Phoenix app at http://phoenix.dev. My local codebase is mounted into the dev container, so any changes I make will be reflected in my running application.
I’m going to go ahead and deploy my Elixir/Phoenix app with Nanobox. Before I push it up to live servers, I want to test the deploy process locally.
Preview a Production Deploy
Nanobox allows you to stage a production deploy on your local machine through its “dry-run“ functionality. I’ll first add a dns alias to my dry-run app, just to make it easy to access from the browser after it’s running.
# add a convenient way to access the app nanobox dns add dry-run phoenix.st # do a dry-run deploy nanobox deploy dry-run
This will spin up a local app just as if it were deploying to production. Once complete, I can visit and test the app at http://phoenix.st.
Deploy Elixir to Production Servers
After testing everything and a dry-run environment, I’m ready to deploy my app to live servers. I’m going to create a new app with Nanobox, add my new app as a remote on my project, then deploy.
# add my new app as a remote nanobox remote add app-name # deploy to my new app nanobox deploy
Nanobox will provision a live server using my cloud provider account, deploy my local codebase to the server, create containers for each of my app’s components (web and database), and start Phoenix.
Get Started With Nanobox
Nanobox is free for personal use and open source projects. You can learn about our paid plans by visiting our pricing page.
To get started with Nanobox, sign up for an account, set up Nanobox following one of our language or framework guides, and watch your life as a software creator and publisher become simpler and easier.
If you have other questions or need some help getting started email us at email@example.com, call us at 801-396-7422, or message us through any of our social media channels.