2020 / 07 / 27
Deploy a Phoenix application to Gigalixir

Short guide on how to deploy a Phoenix app to Gigalixir.

elixir
devops
phoenix

Prerequisites

First, we need a Phoenix app.

The application we’ll be deploying is the one from:
Building a JSON API in Elixir with Phoenix.

Deploying to Gigalixir’s free tier

This is the easiest way to get your app up and running.
It’s basically a zero config approach and it just works.

Create a new account on Gigalixir’s website.
Then let’s switch to the CLI tool for everything else.

Install the Gigalixir CLI tool:

sudo pip install gigalixir

After the installation is done, log in.
This will get your API key saved to ~/.netrc:

gigalixir login

# Email: your@email.goes.here
# Password:
# Would you like us to save your api key to your ~/.netrc file? [Y/n]: Y
# Logged in as your@email.goes.here.

Add your SSH public key so you can do migrations and other operations:

gigalixir account:ssh_keys:add "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2rhfcHmCLw6DkTHBtMjJi+RRHLFkIOojZ78L4hDToRLOZlTNEnxbWYQCECVMd2meemTiVT7Lbd6hjr4dTHo/+HTE01wOxjtZdS2soDylVV2GH4WyMxTmlfFCMsBsWCoHCUV15sTshrZNgHUpM3RdgvyV0awFZ9OIjUXKJi0ReBusWlcvK32mls+ccVEwf8wtslsB0350G25I8Na+9WMwpupUJT+2QM9gqYvch/ptSE6zUOzqe94vlIlXmBlCrbRbtpjS7soGSYKT2jBVWTGBLqW4LQDRJ+s7Q8B3+tilU2C49r2Yeg8Xn4GS6S8y2KsrXZHeSL/GdxQnS2sVGYtMKsQFKAxkAE/J5eXGeTcZwiuCkmAZTm1E9AqwhDbxDNG2uyT/kQJBbcQmAnRLoIVngYdqXcgWtg1iUI+ta4opt6imgtsv99mWFilfIQvisxxBxgyOaciexh3Feeoupn/tLG5wywjN7sEl9SvYABi9Pii15JeVfP1P5bHbdWoiW3APD1WEAyC70O2fanNwgA30PUG2j/zDm14DRMkQ7wpcJxdRGsY/vqYS7o1HwPBTAn8+8uedCDm24KIi2stZvShOTrduIRJzaEduSPlq4pR1nIZSIjdUP9WFJff/mLgbTxOLUtNlboWfJx305dnsVEXFpBKvrx13QcPYywFGoc62spQ== some@user"

Create a new app

cd into your Phoenix project and now let’s create an app and DB for it:

cd my-phoenix-json-api
gigalixir create
gigalixir pg:create --free

Some useful status commands

To see the list of all the apps you have access to:

gigalixir apps
[
  {
    "cloud": "gcp",
    "region": "v2018-us-central1",
    "replicas": 0,
    "size": 0.3,
    "stack": "gigalixir-18",
    "unique_name": "square-brilliant-diplodocus",
    "version": 2
  }
]

Show current app status:

gigalixir ps
{
  "cloud": "gcp",
  "pods": [],
  "region": "v2018-us-central1",
  "replicas_desired": 0,
  "replicas_running": 0,
  "size": 0.3,
  "stack": "gigalixir-18",
  "unique_name": "square-brilliant-diplodocus"
}

List all DBs for current app:

gigalixir pg
[
  {
    "app_name": "square-brilliant-diplodocus",
    "database": "9a75bce2-fbf8-4a1b-80b2-115541025078",
    "host": "postgres-free-tier-1.gigalixir.com",
    "id": "9a75bce2-fbf8-4a1b-80b2-115541025078",
    "limited_at": null,
    "password": "pw-93c493e4-aba0-4f0d-b134-f66e0a73ca91",
    "port": 5432,
    "state": "AVAILABLE",
    "tier": "FREE",
    "url": "postgresql://9a75bce2-fbf8-4a1b-80b2-115541025078-user:pw-93c493e4-aba0-4f0d-b134-f66e0a73ca91@postgres-free-tier-1.gigalixir.com:5432/9a75bce2-fbf8-4a1b-80b2-115541025078",
    "username": "9a75bce2-fbf8-4a1b-80b2-115541025078-user"
  }
]

Delete an app

If you want to get rid of the app and start afresh, you need to delete the DB first.

gigalixir pg:destroy -d 5d62c4a9-37a4-4104-a19e-187b8221e322 # this is the dataabase id

Then delete the app itself:

gigalixir apps:destroy -a green-trusty-coqui # this is the app name

To see all available commands just type gigalixir.

The deploy process

Open config/prod.exs and change the host URL to your app name plus .gigalixirapp.com. Like this:

config :my_app, MyAppWeb.Endpoint,
  url: [host: "square-brilliant-diplodocus.gigalixirapp.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json"

Then create an elixir_buildpack.config file in your project’s root directory and add the versions you used for your project:

elixir_version=1.10.4
erlang_version=23.0.3

Commit your changes on git, then to deploy just:

git push gigalixir main

To run any existing migrations:

gigalixir ps:migrate

If you are using an identity file different from id_rsa —say lobotuerto and lobotuerto.pub, you can pass SSH options to the command like this:

gigalixir ps:migrate -o "ssh -i ~/.ssh/lobotuerto"

Take a look at your current app status:

gigalixir ps
{
  "cloud": "gcp",
  "pods": [
    {
      "lastState": {},
      "name": "square-brilliant-diplodocus-765fc6f7c8-zpdbw",
      "sha": "426a17f47d10b751b53fc96f8f23e0298ac9bfdc",
      "status": "Healthy",
      "version": "1"
    }
  ],
  "region": "v2018-us-central1",
  "replicas_desired": 1,
  "replicas_running": 1,
  "size": 0.3,
  "stack": "gigalixir-18",
  "unique_name": "square-brilliant-diplodocus"
}

The "status": "Healthy" part signals that your app is ready to receive requests.

The remote console

Let’s log into the remote console and create a user so we can test our endpoints.

gigalixir ps:remote_console

If using a different identity file, you might tweak your command to:

gigalixir ps:remote_console -o "-i ~/.ssh/lobotuerto"

Notice the subtle change to what’s inside the quotes, this time you need to remove the ssh we were using in the ps:migrate command.

When the remote console opens, enter the following:

MyApp.Account.create_user(%{email: "asd@asd.com", password: "qwerty"})

Then close it with a double CTRL-C.

Running the seeds file

Let’s add the following code to the priv/repo/seeds.exs file:

IO.puts("Adding a couple of users...")

MyApp.Auth.create_user(%{email: "user1@email.com", password: "qwerty"})
MyApp.Auth.create_user(%{email: "user2@email.com", password: "asdfgh"})

Commit the changes then push to the gigalixir remote and run the seeds with:

git push gigalixir master
gigalixir run -- mix run priv/repo/seeds.exs

You can have a look at the server logs with:

gigalixir logs

Now, you can try the curl commands against a live application in production!

curl -H "Content-Type: application/json" -X GET \
http://square-brilliant-diplodocus.gigalixirapp.com/api/users \
-c cookies.txt -b cookies.txt -i

# HTTP/1.1 401 Unauthorized
# Server: nginx/1.17.7
# Content-Type: application/json; charset=utf-8
# Content-Length: 44
# cache-control: max-age=0, private, must-revalidate
# x-request-id: a8f2421075d1bc52248bfe0a088e328e
#
# {"errors":{"detail":"Unauthenticated user"}}


curl -H "Content-Type: application/json" -X POST \
-d '{"email":"asd@asd.com","password":"qwerty"}' \
http://square-brilliant-diplodocus.gigalixirapp.com/api/users/sign_in \
-c cookies.txt -b cookies.txt -i

# HTTP/1.1 200 OK
# Server: nginx/1.17.7
# Content-Type: application/json; charset=utf-8
# Content-Length: 85
# cache-control: max-age=0, private, must-revalidate
# x-request-id: 77d6a6af461823d883e45fb5d2c7a177
#
# {"data":{"user":{"email":"asd@asd.com","id":"5d91bef4-94b2-437a-8e6a-d8db7ea1b376"}}}


curl -H "Content-Type: application/json" -X GET \
http://square-brilliant-diplodocus.gigalixirapp.com/api/users \
-c cookies.txt -b cookies.txt -i

# HTTP/1.1 200 OK
# Server: nginx/1.17.7
# Date: Sun, 26 Jul 2020 02:04:10 GMT
# Content-Type: application/json; charset=utf-8
# Content-Length: 276
# Vary: Accept-Encoding
# cache-control: max-age=0, private, must-revalidate
# x-request-id: 7ec40e0e453d6df5d00f47b15243e99f
# Via: 1.1 google
#
# {"data":[{"email":"asd@asd.com","id":"5d91bef4-94b2-437a-8e6a-d8db7ea1b376","is_active":false},{"email":"user1@email.com","id":"cb6fce2d-a264-4d92-a2ba-a224b15d541f","is_active":false},{"email":"user2@email.com","id":"35e627ed-e7d2-40d8-9597-a09ee7caa0a3","is_active":false}]}

That was fun, wasn’t it? :D