Module.Application.start/2 undefined (Module.Application is not available) when running in Docker

Here is my Dockerfile

# elixir builder

FROM elixir:1.7-alpine as elixir-builder

ENV HOME=/opt/app

WORKDIR $HOME

RUN mix do local.hex --force, local.rebar --force

COPY config/ $HOME/config/

COPY mix.exs mix.lock $HOME/

RUN mix deps.get

# node builder

FROM node:11-alpine as asset-builder

ENV HOME=/opt/app

WORKDIR $HOME

COPY --from=elixir-builder $HOME/deps $HOME/deps

WORKDIR $HOME/assets

COPY assets/ $HOME/assets/

RUN npm install && npm run deploy

# app release

FROM alpine:3.8 as builder

ENV MIX_ENV=prod HOME=/opt/app

WORKDIR $HOME

RUN sed -i -e 's/v[[:digit:]]\.[[:digit:]]/edge/g' /etc/apk/repositories &&\
  apk update &&\
  apk upgrade &&\
  apk add\
  build-base\
  elixir\
  erlang-xmerl\
  erlang-runtime-tools

COPY config $HOME/config/

COPY rel $HOME/rel/

COPY mix.exs mix.lock $HOME/

COPY --from=asset-builder $HOME/priv/static $HOME/priv/static

RUN mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.get &&\
mix deps.compile &&\
mix phx.digest &&\

mix release --verbose

# release image

FROM alpine:3.8

LABEL maintainer="shadowlegend@focuzsolution.com"

ENV LANG=en_US.UTF-8\
  HOME=/opt/app\
  MIX_ENV=prod\
  REPLACE_OS_VARS=true\
  LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64\
  SHELL=/bin/bash

WORKDIR $HOME

RUN sed -i -e 's/v[[:digit:]]\.[[:digit:]]/edge/g' /etc/apk/repositories &&\
  apk update && apk upgrade --no-cache &&\
  apk --no-cache add openssl bash &&\
  addgroup genx &&\
  adduser -D -G genx xuser &&\
  chown -R xuser:genx $HOME

COPY --from=builder $HOME/_build/prod/rel/app_server/releases/**/*.tar.gz $HOME

RUN tar -xzf app_server.tar.gz; rm app_server.tar.gz; ls bin/;

USER xuser

ENTRYPOINT ["bin/app_server"]

CMD ["foreground"]

The mix.exs

defmodule AppServer.MixProject do
  use Mix.Project

  def project do
    [
      app: :app_server,
      version: "1.0.5",
      elixir: "~> 1.7",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      test_coverage: [tool: ExCoveralls],
      preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test, "coveralls.html": :test],
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {AppServer.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.4.0"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},
      {:absinthe, "~> 1.4.0"},
      {:absinthe_plug, "~> 1.4.0"},
      {:absinthe_phoenix, "~> 1.4.0"},
      {:absinthe_ecto, ">= 0.0.0"},
      {:excoveralls, "~> 0.10", only: :test},
      {:dataloader, "~> 1.0.0"},
      {:guardian, "~> 1.0"},
      {:distillery, "~> 2.0", runtime: false},
      {:faker, "~> 0.10", only: [:test, :dev]},
      {:hackney, "~> 1.14.0"},
      {:sweet_xml, "~> 0.6"},
      {:comeonin, "~> 4.0"},
      {:argon2_elixir, "~> 1.3"},
      {:cors_plug, "~> 2.0"},
      {:phoenix_pubsub_redis, "~> 2.1.0"},
      {:bamboo, "~> 1.1"},
      {:bamboo_smtp, "~> 1.6.0"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.reset", "coveralls"]
    ]
  end
end

The AppServer.Application inside lib/app_server/application.ex

defmodule AppServer.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    import Supervisor.Spec

    # Define workers and child supervisors to be supervised
    children = [
      # Start the Ecto repository
      AppServer.Repo,
      # Start the endpoint when the application starts
      AppServerWeb.Endpoint,

      supervisor(Absinthe.Subscription, [AppServerWeb.Endpoint])
      # Start your own worker by calling: AppServer.Worker.start_link(arg1, arg2, arg3)
      # worker(AppServer.Worker, [arg1, arg2, arg3]),
    ] 

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: AppServer.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    AppServerWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

I could successfully follow the step I did inside Dockerfile(like MIX_ENV=prod mix deps.get ... mix release and release-build the app outside of Docker and run it without any problem. The image also successfully built but when I try running it with docker container run app_server:latest or with docker-compose up -d with docker-compose.yml, this is what I get

03:16:05.974 [info] Application app_server exited: exited in: AppServer.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (UndefinedFunctionError) function AppServer.Application.start/2 is undefined (module AppServer.Application is not available)
            AppServer.Application.start(:normal, [])
            (kernel) application_master.erl:277: :application_master.start_it_old/4
{"Kernel pid terminated",application_controller,"{application_start_failure,app_server,{bad_return,{{'Elixir.AppServer.Application',start,[normal,[]]},{'EXIT',{undef,[{'Elixir.AppServer.Application',start,[normal,[]],[]},{application_master,start_it_old,4,[{file,\"application_master.erl\"},{line,277}]}]}}}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,app_server,{bad_return,{{'Elixir.AppServer.Application',start,[normal,[]]},{'EXIT',{undef,[{'Elixir.AppServer.Application',st

Crash dump is being written to: erl_crash.dump...done

I have found the problem. I forgot to copy the source code to Dockerfile, that is why it yields AppServer.Application.start/2 undefined. I just simply add COPY . $HOME inside Dockerfile before the release, so now it work as expected. So sorry for such stupid mistake.

1 Like