Deploying an Elixir + Phoenix + Elm with Nanobox

Beto Trevisan
3 min readNov 20, 2017

--

I have recently found out about Nanobox.io and saw it as an excellent alternative to Heroku when deploying an Elixir, Phoenix and Elm application.

The most important advantage of using Nanobox instead of Heroku (which I’m a huge fan) is that it allows for each Elixir node to talk to each other directly. Another advantage is that you can deploy to Cloud computing engine other than AWS such as Azure, Google, DigitalOcean among others using your own billing account.

So assuming Phoenix and Elixir are already installed, let’s start by creating a brand new Phoenix (version 1.3) app:

mix phx.new nanoapp

To save time, skip the “Fetch and install dependencies” for now.

$ cd nanoapp
$ mix deps.get

Setting up Nanobox

Considering you already followed the procedure in https://docs.nanobox.io/install/, the first step is to create a boxfile.yml

# boxfile.yml at the root of your projectrun.config:
# elixir runtime
engine: elixir
engine.config:
runtime: elixir-1.5
erlang_runtime: erlang-20
# we need nodejs in development
# ensure inotify exists for hot-code reloading
dev_packages:
— nodejs
— inotify-tools
# cache node_modules
cache_dirs:
— assets/node_modules
— assets/elm/elm-stuff
# add node_module bins to the $PATH
extra_path_dirs:
— assets/node_modules/.bin
# enable the filesystem watcher
fs_watch: true
extra_steps:
— cd assets && npm install
— cd assets/elm && ../node_modules/.bin/elm-package install — yes
deploy.config:
extra_steps:
— mix phx.digest
before_live:web.main:
— mix ecto.create — quiet
— mix ecto.migrate
# add postgres as a data component
data.db:
image: nanobox/postgresql
web.main:
start: node-start mix phx.server
# Timex and TZData need to write into its priv/ folder
writable_dirs:
— priv

Before we start nanobox we need to add a few lines to assets/package.json . We could install it using bash, but I’ll update the file directly so the instalation only occurs in the VM. For that we’ll add the following lines:

# assets/package.json
{
"repository": {},
"license": "MIT",
"scripts": {
"deploy": "brunch build --production",
"watch": "brunch watch --stdin"
},
"dependencies": {
# Add this line
"elm": "^0.18.0",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html"
},
"devDependencies": {
"babel-brunch": "6.1.1",
"brunch": "2.10.9",
"clean-css-brunch": "2.10.0",
"elm-brunch": "^0.10.0",
"uglify-js-brunch": "2.10.0"
}
}

We are ready to take Nanobox for a spin: nanobox run.
Nanobox will start at the app/ folder which is sync to your projects root folder.

Installing the rest of Elm

Elm is already installed in assets, but let’s make a simple `hello world`.

# in the assets/elm folder:
elm-package install elm-lang/html — yes

And the Main.elm file should look like this:

# assets/elm/Main.elmimport Html exposing(text)main =
text “Hello World”

And now, and most importantly we need to edit brunch-config.json to add the elmBrunch configuration:

# brunch-config.json# Add Elm to the watched folders
watched: [

‘elm’
],
# Remove notifications to avoid Elm compilation messages errors in Docker.
notifications: false,
# Configure the plug-in.
plugins: {
elmBrunch: {
elmFolder: ‘elm’,
mainModules: [ ‘Main.elm’ ],
outputFolder: ‘../js’,
outputFile: ‘main.js’,
executablePath: ‘../node_modules/.bin’,
},

# babel: …

This will read the Elm code at assets/elm and output the compiled main.js to assets/js.

Tying all together

We need to make Phoenix talk to Nanobox database, so in config/dev.exs we should change the database configuration:

# Configure your database
config :nanoapp, Nanoapp.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: “gonano”,
pool_size: 10

You will want to change it in the prod.ex as well so deployment works.

Now we need to integrate Phoenix and Elm starting with adding the following lines to the assets/js/app.js:

# in assets/js/app.jsimport Elm from ‘./main’;const elmDiv = document.querySelector(‘#elm_target’);if (elmDiv) {
Elm.Main.embed(elmDiv);
}

And we need to replace the layout at li/nanoapp_web/template/layout/app.html.eex and leave the body tag as:

<body>
<div class=”container”>
<p class=”alert alert-info” role=”alert”><%= get_flash(@conn, :info) %></p>
<p class=”alert alert-danger” role=”alert”><%= get_flash(@conn, :error) %></p>
<div id=”elm_target”></div></div> <! — /container →
<script src=”https://code.highcharts.com/highcharts.js"></script>
<script src=”<%= static_path(@conn, “/js/app.js”) %>”></script>
</body>

Now, we can exit the VM and make a dry-run: nanobox deploy dry-run.

That is it. Go to IP address Nanobox provided and you should see your Elm app backed by Elixir, Phoenix and Nanobox.

To put your app into the wild follow the deployment workflow.

:D

--

--