function Mix.env/0 is undefined (module Mix is not available)

I’m trying to migrate my app over to Fly, but I’m getting a function Mix.env/0 is undefined (module Mix is not available) when i try to visit the page after deploy. Any help would be much appreciated!

Log output

2022-02-22T18:04:58.737 app[53cbdd68] lax [info] Starting clean up.

2022-02-22T18:08:49.656 app[fc323489] sea [info] 18:08:49.648 request_id=FtYuCOib1m4Tyu8AAAFB [info] GET /

2022-02-22T18:08:49.661 app[fc323489] sea [info] 18:08:49.660 request_id=FtYuCOib1m4Tyu8AAAFB [info] Sent 500 in 12ms

2022-02-22T18:08:49.663 app[fc323489] sea [info] 18:08:49.661 [error] #PID<0.2381.0> running HelloWeb.Endpoint (connection #PID<0.2380.0>, stream id 1) terminated

2022-02-22T18:08:49.663 app[fc323489] sea [info] Server: hello.fly.dev:80 (http)

2022-02-22T18:08:49.663 app[fc323489] sea [info] Request: GET /

2022-02-22T18:08:49.663 app[fc323489] sea [info] ** (exit) an exception was raised:

2022-02-22T18:08:49.663 app[fc323489] sea [info] ** (UndefinedFunctionError) function Mix.env/0 is undefined (module Mix is not available)

2022-02-22T18:08:49.663 app[fc323489] sea [info] Mix.env()

2022-02-22T18:08:49.663 app[fc323489] sea [info] (benchmark 0.1.0) lib/benchmark_web/templates/layout/root.html.heex:78: anonymous fn/2 in HelloWeb.LayoutView."root.html"/1

2022-02-22T18:08:49.663 app[fc323489] sea [info] (phoenix_live_view 0.17.5) lib/phoenix_live_view/engine.ex:124: Phoenix.HTML.Safe.Phoenix.LiveView.Rendered.to_iodata/1

2022-02-22T18:08:49.663 app[fc323489] sea [info] (phoenix 1.6.2) lib/phoenix/controller.ex:772: Phoenix.Controller.render_and_send/4

2022-02-22T18:08:49.663 app[fc323489] sea [info] (phoenix 1.6.2) lib/phoenix/router.ex:355: Phoenix.Router.__call__/2

2022-02-22T18:08:49.663 app[fc323489] sea [info] (benchmark 0.1.0) lib/benchmark_web/endpoint.ex:1: HelloWeb.Endpoint.plug_builder_call/2

2022-02-22T18:08:49.663 app[fc323489] sea [info] (benchmark 0.1.0) lib/benchmark_web/endpoint.ex:1: HelloWeb.Endpoint.call/2

2022-02-22T18:08:49.663 app[fc323489] sea [info] (phoenix 1.6.2) lib/phoenix/endpoint/cowboy2_handler.ex:43: Phoenix.Endpoint.Cowboy2Handler.init/4

DockerFile

ARG BUILDER_IMAGE="hexpm/elixir:1.12.2-erlang-23.3.4-debian-bullseye-20210902-slim"

ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"

FROM ${BUILDER_IMAGE} as builder

# install build dependencies

RUN apt-get update -y && apt-get install -y build-essential git nodejs npm \

&& apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir

WORKDIR /app

# install hex + rebar

RUN mix local.hex --force && \

mix local.rebar --force

# set build ENV

ENV MIX_ENV="prod"

# install mix dependencies

COPY mix.exs mix.lock ./

RUN mix deps.get --only $MIX_ENV

RUN mkdir config

# copy compile-time config files before we compile dependencies

# to ensure any relevant config change will trigger the dependencies

# to be re-compiled.

COPY config/config.exs config/${MIX_ENV}.exs config/

RUN mix deps.compile

# COPY priv priv - Moved down

# note: if your project uses a tool like https://purgecss.com/,

# which customizes asset compilation based on what it finds in

# your Elixir templates, you will need to move the asset compilation

# step down so that `lib` is available.

# COPY assets assets - Moved down

# For Phoenix 1.6 and later, compile assets using esbuild

# RUN mix assets.deploy - Moved down

# For Phoenix versions earlier than 1.6, compile assets npm

# RUN cd assets && yarn install && yarn run webpack --mode production

# RUN mix phx.digest

# Compile the release

COPY lib lib

COPY priv priv

COPY assets assets

RUN cd assets && npm install

RUN mix assets.deploy

RUN mix compile

# Changes to config/runtime.exs don't require recompiling the code

COPY config/runtime.exs config/

COPY rel rel

RUN mix release

# start a new build stage so that the final image will only contain

# the compiled release and other runtime necessities

FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \

&& apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale

RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG en_US.UTF-8

ENV LANGUAGE en_US:en

ENV LC_ALL en_US.UTF-8

WORKDIR "/app"

RUN chown nobody /app

# Only copy the final release from the build stage

COPY --from=builder --chown=nobody:root /app/_build/prod/rel ./

USER nobody

# Create a symlink to the application directory by extracting the directory name. This is required

# since the release directory will be named after the application, and we don't know that name.

RUN set -eux; \

ln -nfs /app/$(basename *)/bin/$(basename *) /app/entry

CMD /app/entry start

fly.toml

# fly.toml file generated for hello on 2022-02-20T14:00:44-08:00

app = "hello"

kill_signal = "SIGTERM"
kill_timeout = 5
processes = []

[deploy]
  release_command = "/app/entry eval Hello.Release.migrate"

[env]

[experimental]
  allowed_public_ports = []

[[services]]
  http_checks = []
  internal_port = 4000
  processes = ["app"]
  protocol = "tcp"
  script_checks = []

  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "30s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

runtime.exs

import Config

# config/runtime.exs is executed for all environments, including
# during releases. It is executed after compilation and before the
# system starts, so it is typically used to load production configuration
# and secrets from environment variables or elsewhere. Do not define
# any compile-time configuration in here, as it won't be applied.
# The block below contains prod specific runtime configuration.
if config_env() == :prod do
  database_url =
    System.get_env("DATABASE_URL") ||
      raise """
      environment variable DATABASE_URL is missing.
      For example: ecto://USER:PASS@HOST/DATABASE
      """

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

  # The secret key base is used to sign/encrypt cookies and other secrets.
  # A default value is used in config/dev.exs and config/test.exs but you
  # want to use a different value for prod and you most likely don't want
  # to check this value into version control, so we use an environment
  # variable instead.
  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 :hello, HelloWeb.Endpoint,
    url: [host: "#{app_name}.fly.dev", port: 80],
    http: [
      # Enable IPv6 and bind on all interfaces.
      # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
      # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
      # for details about using IPv6 vs IPv4 and loopback vs public addresses.
      ip: {0, 0, 0, 0, 0, 0, 0, 0},
      port: String.to_integer(System.get_env("PORT") || "4000")
    ],
    secret_key_base: secret_key_base

  # ## Using releases
  #
  # If you are doing OTP releases, you need to instruct Phoenix
  # to start each relevant endpoint:
  #
  config :hello, HelloWeb.Endpoint, server: true

  # ## Configuring the mailer
  #
  # In production you need to configure the mailer to use a different adapter.
  # Also, you may need to configure the Swoosh API client of your choice if you
  # are not using SMTP. Here is an example of the configuration:
  #
  #     config :hello, Hello.Mailer,
  #       adapter: Swoosh.Adapters.Mailgun,
  #       api_key: System.get_env("MAILGUN_API_KEY"),
  #       domain: System.get_env("MAILGUN_DOMAIN")
  #
  # For this example you need include a HTTP client required by Swoosh API client.
  # Swoosh supports Hackney and Finch out of the box:
  #
  #     config :swoosh, :api_client, Swoosh.ApiClient.Hackney
  #
  # See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
end

if config_env() == :dev do
  database_url = System.get_env("DATABASE_URL")

  if database_url != nil do
    config :hello, Hello.Repo,
      url: database_url,
      socket_options: [:inet6]
  end
end

It looks like the app is trying to call Mix.env function at runtime here:

2022-02-22T18:08:49.663 app[fc323489] sea [info] ** (UndefinedFunctionError) function Mix.env/0 is undefined (module Mix is not available)
2022-02-22T18:08:49.663 app[fc323489] sea [info] Mix.env()
2022-02-22T18:08:49.663 app[fc323489] sea [info] (benchmark 0.1.0) lib/benchmark_web/templates/layout/root.html.heex:78: anonymous fn/2 in HelloWeb.LayoutView."root.html"/1

It probably works fine if you start the app with something like mix phx.server, however, this function not available when you’re running a compiled release (that is, an app built with RUN mix release and then started with CMD /app/entry start instructions in the Dockerfile):

This function should not be used at runtime in application code (as opposed to infrastructure and build code like Mix tasks). Mix is a build tool and may not be available after the code is compiled (for example in a release).

To differentiate the program behavior depending on the environment, it is recommended to use application environment through Application.get_env/3 . Proper configuration can be set in config files, often per-environment (see the Config module for more information).

Hope this helps!

1 Like

@kkonstant is exactly right. The elixir docs point us to the solution where you should reference application config at runtime for those areas that you want to handle conditional behavior. Let us know if you have any questions. This should be a quick fix!

1 Like