Error on trying to running mix phx.server on dev/prod

I tried to deploy to GCP (Cloud Run) with a Dockerfile, as:

FROM elixir:1.11.2-alpine AS builder

RUN apk --no-cache add build-base

WORKDIR /app

ARG MAILGUN_API_KEY
ARG MAILGUN_DOMAIN

ENV MAILGUN_DOMAIN=$MAILGUN_DOMAIN \
    MAILGUN_API_KEY=$MAILGUN_API_KEY

ENV MIX_ENV=prod

# Cache elixir deps
ADD mix.exs mix.lock ./
RUN mix local.rebar
RUN mix local.hex --force
RUN mix do deps.get, deps.compile

COPY ./lib ./lib
COPY ./config ./config
COPY ./priv ./priv

RUN mix release

FROM alpine:latest AS BANK

ARG DB_URL
ARG SECRET_KEY_BASE

ENV DB_URL=$DB_URL \
    SECRET_KEY_BASE=$SECRET_KEY_BASE

# Base packages
RUN apk --no-cache upgrade && \
    apk add --no-cache openssl \
    ncurses-libs postgresql-client

# Creates a non root user and creates artifact
RUN adduser -D -h home/app app
WORKDIR /home/app

COPY --from=builder /app/_build .
RUN chown -R app: ./prod
USER app

COPY ./entrypoint.sh ./

CMD ["/bin/sh", "entrypoint.sh"]

It builds correctly, but when try to forward traffic, it fails…

So I tried to run locally to see if there was any error, and:

** (Mix) Could not start application bank: Bank.Application.start(:normal, []) returned an error: shutdown: failed to start child: BankWeb.Endpoint
    ** (EXIT) an exception was raised:
        ** (FunctionClauseError) no function clause matching in Phoenix.Endpoint.Cowboy2Adapter.port_to_integer/1
            (phoenix 1.5.7) lib/phoenix/endpoint/cowboy2_adapter.ex:123: Phoenix.Endpoint.Cowboy2Adapter.port_to_integer(nil)
            (phoenix 1.5.7) lib/phoenix/endpoint/cowboy2_adapter.ex:63: anonymous fn/5 in Phoenix.Endpoint.Cowboy2Adapter.child_specs/2
            (elixir 1.11.0) lib/enum.ex:2181: Enum."-reduce/3-lists^foldl/2-0-"/3
            (phoenix 1.5.7) lib/phoenix/endpoint/cowboy2_adapter.ex:55: Phoenix.Endpoint.Cowboy2Adapter.child_specs/2
            (phoenix 1.5.7) lib/phoenix/endpoint/supervisor.ex:106: Phoenix.Endpoint.Supervisor.init/1
            (stdlib 3.13.2) supervisor.erl:301: :supervisor.init/1
            (stdlib 3.13.2) gen_server.erl:417: :gen_server.init_it/2
            (stdlib 3.13.2) gen_server.erl:385: :gen_server.init_it/6
            (stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

Here are my configs files:

config.exs

# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.

# General application configuration
use Mix.Config

config :bank,
  ecto_repos: [Bank.Repo],
  generators: [binary_id: true]

# Configures the endpoint
config :bank, BankWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "OrBd2a0fDrfuHkYprTerljuYWcFxr3O+MwVYcLIQVck9XTRMwDKOEig9CFH9h83D",
  render_errors: [view: BankWeb.ErrorView, accepts: ~w(json), layout: false],
  pubsub_server: Bank.PubSub,
  live_view: [signing_salt: "/+XhTad/"]

# Configures Elixir's Logger
config :logger, :console,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

# Git Hooks
if Mix.env() == :dev do
  config :git_hooks,
    auto_install: true,
    verbose: true,
    hooks: [
      pre_commit: [
        tasks: [
          {:cmd, "mix format"},
          {:cmd, "mix compile --warning-as-errors"},
          {:cmd, "mix credo --strict"}
        ]
      ],
      pre_push: [
        verbose: false,
        tasks: [
          {:cmd, "mix dialyzer"},
          {:cmd, "mix test"}
        ]
      ]
    ]
end

# Mailing
config :bank,
  mailing_default_from_name: "Bank Live",
  mailing_default_from_email: "noreply@bank.com"

# Guardian
config :bank, BankWeb.Auth,
  issuer: "bank",
  secret_key: "U5Hu+z/iLlr8fz9jsgHSXVc4xd+TciR/aORxebMKGHp4vVB1tnmPBzI+V+IrbUti"

# Documenting
config :bank, Bank.Documenting,
  result_file_path: "README.md",
  default_response_transforms: %{
    inserted_at: "2021-01-04T22:16:56",
    updated_at: "2021-01-04T22:16:56",
    email: "valid@email.com"
  }

# Oban
# Configures Oban
config :bank, Oban,
  repo: Bank.Repo,
  plugins: [{Oban.Plugins.Pruner, max_age: 300}],
  queues: [default: 10, events: 50, media: 20, mailing: 50]

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

prod.exs:

use Mix.Config

# Do not print debug messages in production
config :logger, level: :info

# Mailing
config :bank, Bank.Mailing,
  adapter: Bamboo.MailgunAdapter,
  api_key: System.get_env("MAILGUN_API_KEY"),
  domain: System.get_env("MAILGUN_DOMAIN")

config :bank, Bank.Endpoint, server: true

dev.exs:

use Mix.Config

# Configure your database
config :bank, Bank.Repo,
  username: "postgres",
  password: "postgres",
  database: "bank_dev",
  hostname: "localhost",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
config :bank, BankWeb.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: []

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime

# Mailing
config :bank, Bank.Mailing,
  adapter: Bamboo.LocalAdapter,
  open_email_in_browser_url: "http://localhost:4000/sent_emails"

runtime.exs:

import Config

secret_key_base = System.get_env("SECRET_KEY_BASE")

app_port = String.to_integer(System.get_env("PORT", "4000"))
app_hostname = System.get_env("HOST", "0.0.0.0")

db_url = System.get_env("DB_URL")
pool_size = String.to_integer(System.get_env("POOL_SIZE", "10"))

config :bank, Bank.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: db_url,
  pool_size: pool_size,
  ssl: false

config :bank, BankWeb.Endpoint,
  load_from_system_env: true,
  http: [:inet6, port: {:system, "PORT"}],
  secret_key_base: secret_key_base

config :bank,
  app_port: app_port

config :bank,
  app_hostname: app_hostname

However, on production, it start normally, however I can’t access the app. What am I doing wrongly?

Try to change this code:

http: [:inet6, port: {:system, "PORT"}],

to:

http: [:inet6, port: app_port],

And set the env var PORT in your environment, because from the error it seems that is not set.

1 Like

Ok, I can run locally, however the release seems not to work…

I’m running the release as:

source ./.env && DB_URL=$DB_URL SECRET_KEY_BASE=$SECRET_KEY_BASE HOST=localhost PORT=4000 _build/prod/rel/bank/bin/bank start

Try to mimic this configuration for the url and http sections:

1 Like

same problem, no connection

Bear in mind that if you are running the app in production behind a load balancer, proxy or api gateway then you need to take special attention in how you configure the http and the url bits:

http: [
    # Like the one used inside a docker container and/or behind a proxy
    port: load_from_env.("HTTP_INTERNAL_PORT", nil), # 4000
    transport_options: [
      socket_opts: [:inet6],
    ],
  ],
  url: [
    scheme: url_public_scheme, # https
    host: url_public_host, # example.com
    port: url_public_port  # 443
  ],
 force_ssl: [
    rewrite_on: [:x_forwarded_proto] # header :x_forwarded_proto may be different for your proxy 
  ]

Ok, I don’t know exactly what I made, but worked now!
Thanks for your help!

Your problem may be the same problem of someone., thus you should share your solution so that others can learn from it when reading your post.

One thing that annoys me the most is when I search the Internet for a problem just to find a post about it that says the issue is solved, but doesn’t show how, and more frustrating when that post is a very good match for my case or the only match.

1 Like