elixirphoenix-frameworkreleasedistillery

Unable to run release application in elixir after creating release build


I have created the build using command MIX_ENV=prod mix release --env=prod --verbose. It successfully creates the build and I can able to run the console and ping commands and give me the pid. Even when I run the start command it successfully started but when I go to htttp://localhost:4000, the server does not run. When I run _build/prod/rel/project/bin/project foreground command it just hangs in there with no output.

I am using MacOS version: 10.13.2, elixir: 1.6.5 (compiled with OTP 19), otp: Erlang/OTP 20. Here is the log

$ MIX_ENV=prod mix release --env=prod --verbose
Generated project app
==> Loading configuration..
==> Assembling release..
==> Building release project:0.0.1 using environment prod
==> One or more direct or transitive dependencies are missing from
    :applications or :included_applications, they will not be included
    in the release:

    :jsx

    This can cause your application to fail at runtime. If you are sure that this is not an issue, you may ignore this warning.
==> Release successfully built!
You can run it in one of the following ways:
  Interactive: _build/prod/rel/project/bin/project console
  Foreground: _build/prod/rel/project/bin/project foreground
  Daemon: _build/prod/rel/project/bin/project start

I have already included all the application that shows as warning except jsx because it shows an error of undefined application.

I have also gone through the distillery issue https://github.com/bitwalker/distillery/issues/276 and as this issue suggest I checked my app name and server is true set in the config file so it did not help. I have also logged issue here https://github.com/bitwalker/distillery/issues/433 in more detail.

Here is my release file config

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :"lfHBC,7lDxe6kbZJ%M.x4=r!>[F*DhL)ly`?d$>%iE=9y)V4_Oulis?4Rvm)~!55"
end

# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default

release :project do
  set version: current_version(:project)
  set applications: [
    :runtime_tools
  ]
end

When I tried to create a new phoenix application and do the same it runs properly and listen to port 4000 and output foreground command but with the same configuration in my application, it does not listen 4000 port and hangs on foreground command. When I see the netstat for both it seems that 4000 port is not running for my app see enter image description here

I am not sure how else I should debug this problem I tried all things I could. Please let me know if anyone needs any more information. I would appreciate any help/suggestion in this regard.

EDIT: Here are my config and prod files.I just pasted the endpoint detail let me know if anything else needed.

# config.exs
config :project, Project.Endpoint,
  url: [host: "localhost"],
  secret_key_base: some_secret_key,
  render_errors: [view: Project.ErrorView, accepts: ~w(html json)],
  check_origin: false,
  pubsub: [name: Project.PubSub, adapter: Phoenix.PubSub.PG2]

# prod.exs
config :project, Project.Endpoint,
  http: [port: 4000],
  url: [scheme: "https", host: "server.example.com", port: 443],
  server: true,
  code_reloader: false

Solution

  • Alrighty so based on the debugging steps in the comments I think our answer lies in the difference between configuration when using the development phoenix server, and the release itself.

    The key reason configuration works differently is that the development server is most often intended to run locally on your machine - so it's natural for it to run off of environment variables present on the dev machine, and to have configuration available in the local runtime.

    However, releases are intended to run on remote machines, often on machines physically or virtually distinct from the machine the release was built on.

    This means that features that depend heavily on runtime configuration have to be handled differently in the release. (Several crucial values of Phoenix.Endpoint like :port, :host, etc fall under this)

    There have been several different best-practices or patterns to use to bridge this difference in configuration as the use of releases has evolved in Elixir thanks largely to Distillery. I think where the community is at today is a really healthy place.

    When a release is compiled, it doesn't have access to the runtime of the machine it will eventually run on. Therefore, if a module needs runtime configuration, its a good approach to delegate that config to the Supervisor that boots the module. At the time the Supervisor initializes the module, its been booted on the remote machine and now has access to that remote environment.

    There are two modules I almost always configure when the supervisor starts: Ecto's Repo and Phoenix's Endpoint.

    You can actually tell Endpoint to configure itself from the remote machine's runtime by setting the load_from_system_env value to true. You'll need to make sure values for PORT HOST are set in the environment, via export PORT=4000 and export HOST=localhost or sourcing a .env file, and this will result in the same behavior on your local machine when running the release and the remote machine you eventually run the release on (assuming both machines have the same environment values):

    # config/prod.exs
    config :trackbees, TrackbeesWeb.Endpoint,
      load_from_system_env: true,
      ...
    

    One of the roles of Phoenix.Endpoint is to act as a wrapper for starting and stopping the endpoint as part of the Supervision tree. It provides an init/2 callback as part of this wrapping strategy. You'll see that when init/2 fires, it will pattern match on :load_from_system_env:

    # trackbees/lib/trackbees_web/endpoint.ex
    def init(_key, config) do
      if config[:load_from_system_env] do
        Keyword.put ...
    

    I think this is the specific solution to the issue that Trackbees is having right now. I'd recommend adding the load_from_system_env config value, and probably inserting a debug statement like IO.inspect above the if statement to verify the value is getting through.

    FYI, in case you're using Ecto as well, there's a matching init wrapper that you can use in a similar way if you want too:

    # lib/trackbees/repo.ex
    def init(_, opts) do
      case Application.get_env(:trackbees, :env) do
        :test ->
          {:ok, opts}
    
        _ ->
          opts =
            opts
            |> Keyword.put(:url, System.get_env("DATABASE_URL"))
            |> Keyword.put(:username, System.get_env("DB_USERNAME"))
            |> Keyword.put(:password, System.get_env("DB_PASSWORD"))
            |> Keyword.put(:database, System.get_env("DB_NAME"))
            |> Keyword.put(:hostname, System.get_env("DB_HOSTNAME"))
    
          {:ok, opts}
      end
    end