How to build phoenix app using mix release?

Hi,

I’m trying to deploy my phoenix api project using fly.io so I followed the instructions here using mix release.

When I try to deploy my app it says /app/bin/senti: /app/bin/senti: No such file or directory. Is there anything wrong that I made?

➜  senti git:(main) ✗ fly deploy
Deploying winter-wildflower-8614
==> Validating app configuration
--> Validating app configuration done
Services
TCP 80/443 ⇢ 4000
==> Building image with Buildpacks
20: Pulling from heroku/buildpacks
Digest: sha256:e8b7f2e1976bd815dc018f6ea83bccdabcb24b17158781c930124af29cda33d6
Status: Image is up to date for heroku/buildpacks:20
20: Pulling from heroku/pack
Digest: sha256:7bb820682c2a756c91bec06c081403eb30a54afa013ab7a3ddc15b6e76cd0e40
Status: Image is up to date for heroku/pack:20
Downloading from https://cnb-shim.herokuapp.com/v1/hashnuke/elixir
1.88 MB/-1 B
===> DETECTING
hashnuke/elixir 0.1
===> ANALYZING
Previous image with name "registry.fly.io/winter-wildflower-8614:deployment-1621173527" not found
===> RESTORING
===> BUILDING
-----> Will export the following config vars:
       * MIX_ENV=prod
-----> Checking Erlang and Elixir versions
       Will use the following versions:
       * Stack heroku-20
       * Erlang 23.3.4
       * Elixir v1.11.2
-----> Stack changed, will rebuild
-----> Fetching Erlang 23.3.4 from https://repo.hex.pm/builds/otp/ubuntu-20.04/OTP-23.3.4.tar.gz
-----> Installing Erlang 23.3.4 (changed)

-----> Fetching Elixir v1.11.2 for OTP 23 from https://repo.hex.pm/builds/elixir/v1.11.2-otp-23.zip
-----> Installing Elixir v1.11.2 (changed)
-----> Installing Hex
* creating /home/heroku/.mix/archives/hex-0.21.2
-----> Installing rebar
* creating /home/heroku/.mix/rebar
* creating /home/heroku/.mix/rebar3
-----> Fetching app dependencies with mix
Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
  argon2_elixir 2.4.0
  comeonin 5.3.2
  connection 1.1.0
  cors_plug 2.0.3
  cowboy 2.8.0
  cowboy_telemetry 0.3.1
  cowlib 2.9.1
  db_connection 2.4.0
  decimal 2.0.0
  ecto 3.6.1
  ecto_sql 3.6.1
  elixir_make 0.6.2
  gettext 0.18.2
  jason 1.2.2
  mime 1.6.0
  nanoid 2.0.5
  phoenix 1.5.8
  phoenix_ecto 4.2.1
  phoenix_html 2.14.3
  phoenix_live_dashboard 0.4.0
  phoenix_live_view 0.15.5
  phoenix_pubsub 2.0.0
  plug 1.11.1
  plug_cowboy 2.5.0
  plug_crypto 1.2.2
  postgrex 0.15.9
  ranch 1.7.1
  telemetry 0.4.3
  telemetry_metrics 0.6.0
  telemetry_poller 0.5.1
All dependencies are up to date
-----> Copying hex from /home/heroku/.mix/archives/hex-0.21.2
-----> Compiling
==> connection
Compiling 1 file (.ex)
Generated connection app
==> gettext
Compiling 1 file (.erl)
Compiling 20 files (.ex)
Generated gettext app
===> Compiling ranch
===> Failed to restore /workspace/deps/ranch/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

===> Compiling telemetry
===> Failed to restore /workspace/deps/telemetry/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

==> telemetry_metrics
Compiling 7 files (.ex)
Generated telemetry_metrics app
===> Compiling telemetry_poller
===> Failed to restore /workspace/deps/telemetry_poller/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

==> decimal
Compiling 4 files (.ex)
Generated decimal app
==> jason
Compiling 8 files (.ex)
Generated jason app
==> comeonin
Compiling 4 files (.ex)
Generated comeonin app
==> elixir_make
Compiling 1 file (.ex)
Generated elixir_make app
==> argon2_elixir
mkdir -p /workspace/_build/prod/lib/argon2_elixir/priv
cc -g -O3 -pthread -Wall -Wno-format-truncation -I"/app/.platform_tools/erlang/erts-11.2.2/include" -Iargon2/include -Iargon2/src -Ic_src -shared -fPIC -fvisibility=hidden -Wl,-soname,libargon2.so.0 argon2/src/argon2.c argon2/src/core.c argon2/src/blake2/blake2b.c argon2/src/thread.c argon2/src/encoding.c argon2/src/ref.c c_src/argon2_nif.c -o /workspace/_build/prod/lib/argon2_elixir/priv/argon2_nif.so
Compiling 3 files (.ex)
Generated argon2_elixir app
==> db_connection
Compiling 14 files (.ex)
Generated db_connection app
==> ecto
Compiling 56 files (.ex)
Generated ecto app
==> phoenix_pubsub
Compiling 11 files (.ex)
Generated phoenix_pubsub app
===> Compiling cowlib
===> Failed to restore /workspace/deps/cowlib/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

===> Compiling cowboy
===> Failed to restore /workspace/deps/cowboy/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

===> Compiling cowboy_telemetry
===> Failed to restore /workspace/deps/cowboy_telemetry/.rebar3/rebar_compiler_erl/source.dag file. Discarding it.

==> nanoid
Compiling 4 files (.ex)
Generated nanoid app
==> mime
Compiling 2 files (.ex)
Generated mime app
==> postgrex
Compiling 61 files (.ex)
Generated postgrex app
==> ecto_sql
Compiling 26 files (.ex)
Generated ecto_sql app
==> plug_crypto
Compiling 5 files (.ex)
Generated plug_crypto app
==> plug
Compiling 1 file (.erl)
Compiling 41 files (.ex)
warning: System.stacktrace/0 is deprecated, use __STACKTRACE__ instead
  lib/plug/conn/wrapper_error.ex:23

warning: MIME.valid?/1 is deprecated. Use MIME.extensions(type) != [] instead
  lib/plug/mime.ex:32: Plug.MIME.valid?/1

Generated plug app
==> plug_cowboy
Compiling 5 files (.ex)
Generated plug_cowboy app
==> cors_plug
Compiling 1 file (.ex)
Generated cors_plug app
==> phoenix_html
Compiling 8 files (.ex)
Generated phoenix_html app
==> phoenix
Compiling 66 files (.ex)
Generated phoenix app
==> phoenix_live_view
Compiling 22 files (.ex)
Generated phoenix_live_view app
==> phoenix_live_dashboard
Compiling 36 files (.ex)
Generated phoenix_live_dashboard app
==> phoenix_ecto
Compiling 7 files (.ex)
Generated phoenix_ecto app
==> senti
Compiling 37 files (.ex)
Generated senti app
warning: the dependency pow is not present in the build directory
* Cleaning pow
-----> Creating .profile.d with env vars
-----> Writing export for multi-buildpack support
===> EXPORTING
Adding layer 'hashnuke/elixir:profile'
Adding 1/1 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Adding layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
Saving registry.fly.io/winter-wildflower-8614:deployment-1621173527...
*** Images (36be27084d72):
      registry.fly.io/winter-wildflower-8614:deployment-1621173527
Adding cache layer 'hashnuke/elixir:shim'
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/winter-wildflower-8614]
83d85471d9f8: Layer already exists
41c57834b79a: Layer already exists
20e1cf6014bd: Layer already exists
dca58f1aac35: Pushed
911870c321d2: Layer already exists
95668a62a6a6: Pushed
140533a8850e: Pushed
e9677c99d3a6: Pushed
4125fa93a795: Pushed
2f140462f3bc: Pushed
63c99163f472: Pushed
ccdbb80308cc: Pushed
deployment-1621173527: digest: sha256:73294bb250b7e5a5c2c45e5cb445e773e0fe72d9fda8119d92427e5dde0d5627 size: 2823
--> Pushing image done
Image: registry.fly.io/winter-wildflower-8614:deployment-1621173527
Image size: 2.1 GB
==> Creating release
Release v2 created
Release command detected: this new release will not be available until the command succeeds.

You can detach the terminal anytime without stopping the deployment
==> Release command
Command: /app/bin/senti eval Senti.Release.migrate
         Starting instance
         Configuring virtual machine
         Pulling container image
         Unpacking image
         Preparing kernel init
         Configuring firecracker
         Starting virtual machine
         Starting init (commit: cc4f071)...
         Running: `launcher /app/bin/senti eval Senti.Release.migrate` as heroku
         2021/05/16 14:04:40 listening on [fdaa:0:2a2a:a7b:aaa:73ad:8867:2]:22 (DNS: [fdaa::3]:53)
         /app/bin/senti: /app/bin/senti: No such file or directory
         Main child exited normally with code: 127
         Starting clean up.
Error Release command failed, deployment aborted
➜  senti git:(main) ✗

Here’s what I’ve done:

Create file to run DB migration lib/senti/release.ex

defmodule Senti.Release do
  @moduledoc """
  Used for executing DB release tasks when run in production without Mix
  installed.
  """
  @app :senti

  def migrate do
    load_app()

    for repo <- repos() do
      {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
    end
  end

  def rollback(repo, version) do
    load_app()
    {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
  end

  defp repos do
    Application.fetch_env!(@app, :ecto_repos)
  end

  defp load_app do
    Application.load(@app)
  end
end

Update `rel/env.sh.eex`` file

#!/bin/sh

ip=$(grep fly-local-6pn /etc/hosts | cut -f 1)
export RELEASE_DISTRIBUTION=name
export RELEASE_NODE=$FLY_APP_NAME@$ip
export ELIXIR_ERL_OPTIONS="-proto_dist inet6_tcp"

Create runtime config

import Config

if config_env() == :prod do
  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise """
      environment variable SECRET_KEY_BASE is missing.
      You can generate one by calling: mix phx.gen.secret
      """

  app_name =
    System.get_env("FLY_APP_NAME") ||
      raise "FLY_APP_NAME not available"

  config :senti, SentiWeb.Endpoint,
    server: true,
    url: [host: "#{app_name}.fly.dev", port: 80],
    http: [
      port: String.to_integer(System.get_env("PORT") || "4000"),
      # IMPORTANT: support IPv6 addresses
      transport_options: [socket_opts: [:inet6]]
    ],
    secret_key_base: secret_key_base

  database_url =
    System.get_env("DATABASE_URL") ||
      raise """
      environment variable DATABASE_URL is missing.
      For example: ecto://USER:PASS@HOST/DATABASE
      """

  config :senti, Senti.Repo,
    url: database_url,
    # IMPORTANT: Or it won't find the DB server
    socket_options: [:inet6],
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end

Remove the last line of config/prod.exs that says import_config “prod.secret.exs”.

Create elixir_buildpack.config

erlang_version=23.3.4
elixir_version=1.11.2

My apologies if the thread is long I just need to layout all changes I’ve made so its easier to know what’s I did wrong.

Hope someone could point out the problem. Thank you very much.

The Buildpack we’re using is a little wonky, especially with mix release. You’re probably better off using a Dockerfile: Build, Deploy and Run an Elixir Application · Fly

To use that: remove the [build] block from fly.toml and put a Dockerfile in your app directory.

2 Likes

Hi, I did what you’ve suggested but now a new error occured. Please kindly see the logs below

➜  senti git:(main) ✗ fly deploy
Deploying dawn-hill-7230
==> Validating app configuration
--> Validating app configuration done
Services
TCP 80/443 ⇢ 4000
Remote builder fly-builder-cold-resonance-2354 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
Step 1/24 : FROM hexpm/elixir:1.11.2-erlang-23.3.2-alpine-3.13.3 AS build
 ---> 90ddceef09bf
Step 2/24 : RUN apk add --no-cache build-base npm
 ---> Using cache
 ---> a794e29c4f26
Step 3/24 : WORKDIR /app
 ---> Using cache
 ---> 9432c9995e65
Step 4/24 : RUN mix local.hex --force &&     mix local.rebar --force
 ---> Using cache
 ---> 0731795587a8
Step 5/24 : ENV MIX_ENV=prod
 ---> Using cache
 ---> df036c888025
Step 6/24 : ENV SECRET_KEY_BASE=nokey
 ---> Using cache
 ---> 888fe62da22f
Step 7/24 : COPY mix.exs mix.lock ./
 ---> Using cache
 ---> 43c9cc333e75
Step 8/24 : COPY config config
 ---> Using cache
 ---> da95bfa79bab
Step 9/24 : RUN mix deps.get --only prod &&     mix deps.compile
 ---> Using cache
 ---> 02ff5cbef2aa
Step 10/24 : COPY priv priv
 ---> Using cache
 ---> c234241828db
Step 11/24 : COPY lib lib
 ---> Using cache
 ---> fd2c4df3edd6
Step 12/24 : COPY rel rel
 ---> Using cache
 ---> a7e7493249d2
Step 13/24 : RUN mix do compile, release
 ---> Using cache
 ---> 848b3799d31d
Step 14/24 : FROM alpine:3.13.3 AS app
 ---> 302aba9ce190
Step 15/24 : RUN apk add --no-cache openssl ncurses-libs
 ---> Using cache
 ---> 0f4bb72a1b5f
Step 16/24 : WORKDIR /app
 ---> Using cache
 ---> 7eb2274dd1b0
Step 17/24 : RUN chown nobody:nobody /app
 ---> Using cache
 ---> 663677199a63
Step 18/24 : USER nobody:nobody
 ---> Using cache
 ---> ecb8fc7aac67
Step 19/24 : COPY --from=build --chown=nobody:nobody /app/_build/prod/rel/senti ./
 ---> Using cache
 ---> 4c6a6e706934
Step 20/24 : ENV HOME=/app
 ---> Using cache
 ---> 6989f28b5899
Step 21/24 : ENV MIX_ENV=prod
 ---> Using cache
 ---> 5ad6c97ca804
Step 22/24 : ENV SECRET_KEY_BASE=nokey
 ---> Using cache
 ---> 1d21afbae13b
Step 23/24 : ENV PORT=4000
 ---> Using cache
 ---> 3254e654afec
Step 24/24 : CMD ["bin/senti", "start"]
 ---> Running in 69370c0aa970
 ---> c6ef872b7a57
Successfully built c6ef872b7a57
Successfully tagged registry.fly.io/dawn-hill-7230:deployment-1621208918
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/dawn-hill-7230]
f7bd51aaa151: Layer already exists
05d73f65d818: Layer already exists
60c9c90b00d9: Layer already exists
407fa4dc1e5e: Layer already exists
0f7b3ff8b310: Layer already exists
deployment-1621208918: digest: sha256:7293cad44b083965bf0823ce88b35ae444a85d5cd404c38b4263dd4047635295 size: 1362
--> Pushing image done
Image: registry.fly.io/dawn-hill-7230:deployment-1621208918
Image size: 19 MB
==> Creating release
Release v2 created
Release command detected: this new release will not be available until the command succeeds.

You can detach the terminal anytime without stopping the deployment
==> Release command
Command: /app/bin/senti eval Senti.Release.migrate
         Starting instance
         Configuring virtual machine
         Pulling container image
         Unpacking image
         Preparing kernel init
         Configuring firecracker
         Starting virtual machine
         Starting init (commit: cc4f071)...
         Running: `/app/bin/senti eval Senti.Release.migrate` as nobody
         Virtual machine started successfully
         2021/05/16 23:50:01 listening on [fdaa:0:2a2a:a7b:aaa:ff19:e67b:2]:22 (DNS: [fdaa::3]:53)
         23:50:01.806 [error] GenServer #PID<0.134.0> terminating
         ** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
             (elixir 1.11.2) lib/keyword.ex:420: Keyword.fetch!/2
             (postgrex 0.15.9) lib/postgrex/protocol.ex:161: Postgrex.Protocol.connect_endpoints/6
             (db_connection 2.4.0) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
             (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
             (stdlib 3.14.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
         Last message: nil
         23:50:01.806 [error] GenServer #PID<0.135.0> terminating
         ** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
             (elixir 1.11.2) lib/keyword.ex:420: Keyword.fetch!/2
             (postgrex 0.15.9) lib/postgrex/protocol.ex:161: Postgrex.Protocol.connect_endpoints/6
             (db_connection 2.4.0) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
             (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
             (stdlib 3.14.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
         Last message: nil
         23:50:01.832 [error] GenServer #PID<0.136.0> terminating
         ** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
             (elixir 1.11.2) lib/keyword.ex:420: Keyword.fetch!/2
             (postgrex 0.15.9) lib/postgrex/protocol.ex:161: Postgrex.Protocol.connect_endpoints/6
             (db_connection 2.4.0) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
             (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
         Last message: nil
             (stdlib 3.14.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
         23:50:01.833 [error] GenServer #PID<0.137.0> terminating
         ** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
             (elixir 1.11.2) lib/keyword.ex:420: Keyword.fetch!/2
             (postgrex 0.15.9) lib/postgrex/protocol.ex:161: Postgrex.Protocol.connect_endpoints/6
             (db_connection 2.4.0) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
             (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
             (stdlib 3.14.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
         Last message: nil
         23:50:01.833 [error] GenServer #PID<0.138.0> terminating
         ** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
             (elixir 1.11.2) lib/keyword.ex:420: Keyword.fetch!/2
             (postgrex 0.15.9) lib/postgrex/protocol.ex:161: Postgrex.Protocol.connect_endpoints/6
             (db_connection 2.4.0) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
             (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
             (stdlib 3.14.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
         Last message: nil
         ** (EXIT from #PID<0.93.0>) shutdown
         Main child exited normally with code: 1
         Reaped child process with pid: 557 and signal: SIGUSR1, core dumped? false
         Starting clean up.
Error Release command failed, deployment aborted
➜  senti git:(main) ✗

Here’s my runtime config specifically for database

  config :senti, Senti.Repo,
    url: database_url,
    # IMPORTANT: Or it won't find the DB server
    socket_options: [:inet6],
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

and it matches in my config.ex file

config :senti,
  ecto_repos: [Senti.Repo]

I also run fly postgres attach

➜  senti git:(main) ✗ fly postgres attach --postgres-app senti
Postgres cluster senti is now attached to dawn-hill-7230
The following secret was added to dawn-hill-7230:
  DATABASE_URL=autogenerated

My docker file

###
### Fist Stage - Building the Release
###
FROM hexpm/elixir:1.11.2-erlang-23.3.2-alpine-3.13.3 AS build

# install build dependencies
RUN apk add --no-cache build-base npm

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV as prod
ENV MIX_ENV=prod
ENV SECRET_KEY_BASE=nokey

# Copy over the mix.exs and mix.lock files to load the dependencies. If those
# files don't change, then we don't keep re-fetching and rebuilding the deps.
COPY mix.exs mix.lock ./
COPY config config

RUN mix deps.get --only prod && \
    mix deps.compile

COPY priv priv

# copy source here if not using TailwindCSS
COPY lib lib

# compile and build release
COPY rel rel
RUN mix do compile, release

###
### Second Stage - Setup the Runtime Environment
###

# prepare release docker image
FROM alpine:3.13.3 AS app
RUN apk add --no-cache openssl ncurses-libs

WORKDIR /app

RUN chown nobody:nobody /app

USER nobody:nobody

COPY --from=build --chown=nobody:nobody /app/_build/prod/rel/senti ./

ENV HOME=/app
ENV MIX_ENV=prod
ENV SECRET_KEY_BASE=nokey
ENV PORT=4000

CMD ["bin/senti", "start"]

Is there a step I miss?

Looks like your database_url variable might be empty or malformed. You could add show_sensitive_data_on_connection_error: true to your config :senti, Senti.Repo settings to check what value it is using.

Also try running flyctl secrets list to ensure that DATABASE_URL is set.

However, what you have there looks correct compared to the Phoenix apps I have running on Fly.