Phoenix Docker Error: compile vs runtime value not being set

My phoenix app seems to crash when I try to run it in docker, it appears my secret_key_base value is being set at runtime but not at compile time.

  • Compile time value was set to: [url: [host: “localhost”], adapter: Bandit.PhoenixAdapter, render_errors: [formats: [html: FlixirWeb.ErrorHTML, json: FlixirWeb.ErrorJSON], layout: false], pubsub_server: Flixir.PubSub, live_view: [signing_salt: “hLMifOvkqjrh1OjtczWMnigpiE95pC0c”], session: [store: :cookie, key: “_flixir_key”, signing_salt: “session_salt_key_change_in_prod”, encryption_salt: “session_encrypt_salt_change_in_prod”, max_age: 86400, secure: false, http_only: true, same_site: “Lax”], cache_static_manifest: “priv/static/cache_manifest.json”]
    � * Runtime value was set to: [adapter: Bandit.PhoenixAdapter, render_errors: [formats: [html: FlixirWeb.ErrorHTML, json: FlixirWeb.ErrorJSON], layout: false], pubsub_server: Flixir.PubSub, live_view: [signing_salt: “hLMifOvkqjrh1OjtczWMnigpiE95pC0c”], session: [store: :cookie, key: “_flixir_key”, signing_salt: “session_salt_key_change_in_prod”, encryption_salt: “session_encrypt_salt_change_in_prod”, max_age: 86400, secure: false, http_only: true, same_site: “Lax”], cache_static_manifest: “priv/static/cache_manifest.json”, server: true, url: [host: “localhost”, port: 443, scheme: “https”], http: [ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: 8000], secret_key_base: “MoMi9z83mGE0fCVgZom/Y0pFbyV2YWyOPD2b5k8VFUbQnGDnOzCf8G9UUFxs+ZVy”]

To fix this error, you might:

  • Make the runtime value match the compile time one

� * Recompile your project. If the misconfigured application is a dependency, you may need to run “mix deps.clean flixir --build”

� * Alternatively, you can disable this check. If you are using releases, you can set :validate_compile_env to false in your release configuration. If you are using Mix to start your system, you can pass the --no-validate-compile-env flag

I’ve seen a similar post here but haven’t found a solution. I’ve set up SECRET_KEY_BASE in my .env file and I know that’s being picked up by docker. Here is my Docker file:

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian

# instead of Alpine to avoid DNS resolution issues in production.

#

# https://hub.docker.com/r/hexpm/elixir/tags?name=ubuntu

# https://hub.docker.com/_/ubuntu/tags

#

# This file is based on these images:

#

# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image

# - https://hub.docker.com/_/debian/tags?name=bookworm-20240701-slim - for the release image

# - https://pkgs.org/ - resource for finding needed packages

# - Ex: docker.io/hexpm/elixir:1.17.2-erlang-27.0-debian-bookworm-20240701-slim

#

ARG ELIXIR_VERSION=1.17.2

ARG OTP_VERSION=27.0

ARG DEBIAN_VERSION=bookworm-20240701-slim

ARG BUILDER_IMAGE=“docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}

ARG RUNNER_IMAGE=“docker.io/debian:${DEBIAN_VERSION}

FROM ${BUILDER_IMAGE} AS builder

# install build dependencies

RUN apt-get update \

&& apt-get install -y --no-install-recommends build-essential git \

&& rm -rf /var/lib/apt/lists/*

# prepare build dir

WORKDIR /app

# Copy entrypoint script early and make it executable.

COPY rel/prod/entrypoint.sh /app/entrypoint.sh

RUN chmod +x /app/entrypoint.sh

# 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 --force

RUN mix assets.setup

COPY priv priv

COPY lib lib

# Compile the release

RUN mix compile --force

COPY assets assets

# compile assets

RUN mix assets.deploy

# 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 \

&& apt-get install -y --no-install-recommends libstdc++6 openssl libncurses5 locales ca-certificates \

&& rm -rf /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

# set runner ENV

ENV MIX_ENV=“prod”

# Only copy the final release from the build stage

COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/flixir ./

# Copy the entrypoint script

COPY --from=builder --chown=nobody:root /app/entrypoint.sh ./entrypoint.sh

USER nobody

# If using an environment that doesn’t automatically reap zombie processes, it is

# advised to add an init process such as tini via `apt-get install`

# above and adding an entrypoint. See GitHub - krallin/tini: A tiny but valid `init` for containers for details

# ENTRYPOINT [“/tini”, “–”]

#CMD [“/app/bin/server”]

# The entrypoint script will run migrations and then start the server.

# The `–init` flag in `podman run` is still recommended.

ENTRYPOINT [“/app/entrypoint.sh”]

At a glance, seeing that the compile-value of what looks like Endpoint config appears to be for dev env and the runtime-value appears to be for prod env (I see 443 port and server: true).

I do not see how this could be happening because you are setting MIX_ENV correctly in both builder and runner images.

I honestly lost patience and just went for this:

  def project() do
    [
      aliases: aliases(),
      deps: deps(),
      releases: [
        OUR_APP: [
+         # Stop yelling at us for having different compile-time vs. run-time
+         # endpoint configuration, please.
+         validate_compile_env: false,
          applications: [
            important_dependency: :permanent,
            opentelemetry: :temporary,
            fun_with_flags: :load
          ]
        ]
      ]
    ]
  end

Our prod runtime.exs diverged significantly from what we have in dev env and I just don’t want to deal with it. I know what I need in both and don’t want to duplicate configs to appease the release process.

That warning only appears if you’re trying to change a value you cannot change at runtime. It’s literally telling you that you’re in the process of shooting yourself in the foot.

:secret_key_base should not be read at compile time in the first place. Do you have any Application.compile_env calls in your application, where you load the endpoint config?

1 Like

Yeah, it was for Endpoint and I absolutely don’t care that they are differing between both environments. And prod is working fine for 2 years or so at this point. :person_shrugging:

So apparently that warning is not as well aimed as it might seem initially.

Turns out I do, I looked at endpoint.ex and there’s this:

@session_options Application.compile_env(:flixir, FlixirWeb.Endpoint)[:session]

Yeah, you want Application.compile_env(:flixir, [FlixirWeb.Endpoint, :session]) instead, so elixir only considers the :session key as read at compile time.

2 Likes