Deploy a simple Phoenix 1.3 api with Distillery 2.0 and basic_auth to Gigalixir

not_robby
8 min readSep 4, 2018

--

This write-up aims to explore one Elixir/Phoenix deployment option with Gigalixir. The goal is to help people check out Elixir/Phoenix and have something fun to experiment with quickly. By the end of this write-up you will have a simple Phoenix 1.3 JSON api deployed on Gigalixir ready to try out.

…skip ahead to the making stuff..here

Elixir/Phoenix deployment overview…..from really really far away, like space.

The nature of OTP is to use releases but Elixir/Phoenix were designed with strength of tooling and ease of use in mind. You can cut a build and deploy with Mix on say Heroku pretty easily. However this involves deploying your source code, hardware to be setup, loss of OTP features mainly hot code swapping some also consider this an addition attack vector.

The other option is to cut a build with Distillery which generates a release or tarball file that includes ERTS(erlang runtime system). This makes your build portable and ready for Docker containers or AWS. This allows you to be able to perform hot code swaps and other OTP features but once you take Mix out of it, running migrations becomes an issue. As well as splitting compile time and runtime ENV_VARS things are rapidly evolving in the deployment landscape.

Recommended reading if you want to know more, but not required to follow along.

Elixir Deployment Tools

Benefits of Distillery?

Distillery 2.0 release

Announcing Distillery 2.0 — blog post from bitwalker

*Distillery 2.0 introduces config_providers but still supports REPLACE_OS_VARS which is the method we will be using to handle ENV_VARS.

Assumptions

This write-up is intended to be a quick up and running guide, it makes the assumption you have poked around the Elixir/Phoenix stack before and have some basic programming experience. For more information please visit the docs for Gigalixir and Distillery.

Tools we are going to use

What is Gigalixir?

“Gigalixir is a fully-featured, production-stable platform-as-a-service built just for Elixir that saves you money and unlocks the full power of Elixir and Phoenix without forcing you to build production infrastructure or deal with maintenance and operations.”

For more information, see https://gigalixir.com.

I would also recommend reading — How does Gigalixir compare to Heroku?

What is Distillery?

“Distillery is a tool for packaging Elixir applications for deployment using OTP releases. In a nutshell, Distillery produces an artifact, a tarball, which contains your application and everything needed to run it. This artifact also contains scripts which allow you to run the application in three different modes (console, foreground, and daemonized), as well as a variety of utility commands, such as remote_console which provides an easy way to connect an IEx session to your running application. Releases are more than just a way to package your application though, and are a core part of Erlang’s design, which we inherit in Elixir.”

For more information, see https://hexdocs.pm/distillery/home.html.

What we are going to do

Let’s make an api that represents a lunchbox that holds food. The lunchbox will be our context that handles our food items. The food will have a name:string and have a status:string to keep it simple. We will add in basic http auth with testing and deploy.

  1. make a basic JSON API with Phoenix 1.3
  2. add a schema and migration
  3. add basic_auth
  4. fix tests for auth
  5. run and test locally with postman
  6. sign up for Gigalixir free account
  7. login and create Gigalixir app
  8. create Gigalixir database
  9. prep app for Gigalixir
  10. build and release
  11. deploy
  12. run migrations
  13. test deployed api with postman

Let’s get going

make a basic JSON API with Phoenix 1.3

  • run $ mix phx.new --no-brunch --no-html lunchbox_api to create a new project.
  • then
$ cd lunchbox_api/
$ mix deps.get
$ mix ecto.create
$ mix phx.server
  • your expectation is to see your server running like this
  • don’t forget to run $ git init on your new project and make an initial commit.

add a schema and migration

  • run $ mix phx.gen.json Lunchbox Food foods name:string status:string
  • open up router.ex and add resources “/foods”, FoodController, except: [:new, :edit] to your api route
https://gist.github.com/ieatkimchi/3712e68d486e0a4cf57635892efb0188
  • run $ mix phx.routes to make sure your path helpers are there
  • run $ mix ecto.migrate to run the new foods migration.
  • run $ mix phx.server to see if your api is running

Your expectation is to visit localhost:4000/api/v1/foods in your browser and see the output of {“data”:[]}

add basic_auth

  • add basic auth to your deps in mix.exs
  • run $ mix deps.get
  • add basic_auth plug to the top of food_controller.ex
  • add config for basic_auth to config/test.exs
  • you can also use .env instead of hardcoding with something like…
  • username: System.get_env(“BASIC_AUTH_USERNAME”)
  • password: System.get_env(“BASIC_AUTH_PASSWORD”)

fix tests for auth

  • now we have basic_auth locking down our food_controller.ex but if you run $ mix test you will see adding auth has broken some tests. Let’s fix those!
  • next we need to setup auth for tests and take care of adding auth to the conn, add the following code to your food_controller_test.exs
  • if you run $ mix test you will notice some tests are still failing, we now need to utilize our recycle conn func to get auth headers back on the conn for more reading see. Phoenix Docs for ConnTest
  • your final food_test_controller.exs file should look like this and all tests should be passing now.
  • note adding auth to the conn only makes some tests pass, to make all of them pass we utilize the conn func recycle/1

run and test locally with postman

  • time to check everything out in dev
  • add config for basic_auth to config/dev.exs
  • you can also use .env instead of hardcoding with something like…
  • username: System.get_env(“BASIC_AUTH_USERNAME”)
  • password: System.get_env(“BASIC_AUTH_PASSWORD”)
  • fire up your local server $mix phx.server and postman
  • send a get request to http://localhost:4000/api/v1/foods ,don’t forget to set auth headers!
  • your expectation is to see a response of {“data”:[]} just like before
  • now let’s test storing a food, make sure to set the body in postman to `raw` and change `text` to `JSON (application/json)`
{
"food": {
"name": "cheese",
"status": "really old"

}
}
  • your expectation is to see your new food item returned in the response {“data”:{“status”:”really old”,”name”:”cheese”,”id”:1}}
  • success!! we are almost there, whew!

sign up for Gigalixir free account

login and create Gigalixir app

  • check pip version pip -V if you don’t have pip install with$sudo easy_install pip
  • open up terminal and install the Gigalixir CLI $sudo pip install gigalixir — ignore-installed six
  • login to Gigalixir with $ gigalixir login
  • run $ gigalixir account to verify your new login
  • create a new app $ APP_NAME=$(gigalixir create) and verify with $gigalixir apps
  • check the remote $ gigalixir remote -v

create Gigalixir database

  • run $ gigalixir config and you will notice it is empty
  • create a new database, $ gigalixir pg:create — free
  • verify with $ gigalixir pg
  • now run $ gigalixir config you will see your new db url has already been added

prep app for Gigalixir

  • add Distillery to your deps in mix.exs, {:distillery, “~> 2.0”}
  • run $mix deps.get
  • delete the fileconfig/prod.secrets.exs
  • from config/prod.exs remove the line import_config “prod.secret.exs”
  • adjust your config/prod.exs to look like this
  • don’t forget to add the config for basic_auth in prod
  • create a file named .buildpacks in your api root and add the following
https://github.com/gigalixir/gigalixir-buildpack-clean-cache.git
https://github.com/HashNuke/heroku-buildpack-elixir
https://github.com/gigalixir/gigalixir-buildpack-distillery.git

build and release

  • if you have not been committing with git make sure you do that now.
  • run $ mix release.init * we are skipping $ mix phx.digest since we don’t have static assets but you will need to in order to deploy a full app.
  • commit changes and run $ mix test to make sure nothing is broken
  • run $ MIX_ENV=prod mix release — env=prod to make a release
  • your expectation is to see this in the output

deploy

  • have you made a git commit lately?
  • run $ git push gigalixir master

run migrations

  • Run $ gigalixir add_ssh_key “$(cat ~/.ssh/id_rsa.pub)” to add SSH keys
  • run $ gigalixir ps:migrate

test deployed api with postman

  • now we need to add some VARS for basic auth to Gigalixir
  • run $ gigalixir config:set BASIC_AUTH_USERNAME=”someUserName”
  • and $ gigalixir config:set BASIC_AUTH_PASSWORD=”superSecretPassword”
  • run $ gigalixir config again and now you should see your VARS have been added
  • add auth to postman and try again
  • success! now let’s make some food

Summary

…and that is it! now you have a working api deployed to Gigalixir with some simple auth to try out. You can even visit your api on the web and see it will ask for auth to see your new cheese items. I hope this helps someone out there get up and running with Elixir/Phoenix and Gigalixir. Let me know what you think/ how it goes!

--

--