How can I set local defaults when local config is compile-time and production uses runtime config?

I’m trying to find out a good way to manage config in production, but the default Dockerfile from mix phx.gen.release --docker` compiles everything under /config into the release except for config/runtime.exs. For example, when I want to do this in config/config.exs:

config :app, value: “local_value”

And then this in config/runtime.exs:

config :app, value: System.get_env(“VALUE”) || raise “Message”

The problem is that it complains about how I’m trying to modify a compile-time value with a runtime one. This is a pretty standard env workflow and I’m not sure how other people are solving it.

How do you setup local defaults with runtime config in production?

Can you show the specific error?

You’ll generally get this error when the application config is used by Application.compile_env, which generally means the value can indeed only be changed when compiling the application and not at runtime.

But the second argument to Application.compile_env can be a key or a path (unlike Application.get_env, which only takes a key). Doing Application.compile_env(:myapp, :key) means all the config nested within :key is considered compile time required.

If only a subset of that is actually required at compile time you can do Application.compile_env(:myapp, [:key, :compile]). That would make all the configuration nested at that path compile time required but e.g. config at [:key, :runtime] would be fine. One might be doing something like compile_time_value = Application.compile_env(:myapp, :key)[:compile], which would be needlessly tainting config values as compile time required. Those should be refactored to Application.compile_env(:myapp, [:key, :compile]).

2 Likes

Okay, so it’s happening with the sentry-elixir codebase. What’s strange is that I can’t find anywhere in the sentry codebase that calls compile_env. I try to leave out the config in development entirely, but sentry complains that it needs the configuration.

This means that I have to give it something in the config.exs even though I don’t need it. IMO this is a mistake in the sentry library, but how do I get around this without messing with my configuration? I believe they have a change set for release that just doesn’t send events if the sentry_dsn value isn’t set but it’s not yet available.

I don’t have the project in front of me to confirm, but I’ve used the sentry sdk and I don’t think it requires any compile time config.

Just re-added the sentry dependency. Can confirm there is not a single reference of the Sentry module in my application, and the sentry library raises an exception just because there is no environment variable set. No idea at all why they would do something like that.

How can I set fake values in the config.exs if it complains when I reset them in runtime.exs? I would much prefer if the Sentry application would sit tight in the case of no provided config.

You can set any required values at runtime for all envs.

With errors like this it really is helpful to copy and paste the full error as well as the full stacktrace.

3 Likes

So the solution was for me to copy and paste config :sentry, environment_name: Mix.env() into config/dev.exs and config/test.exs. Then I put the actual config that comes from environment variables into the prod block of runtime.exs so that the config macro is only being called once per environment.

  # config/runtime.exs

  # Setup Sentry error tracking for production
  sentry_dsn =
    System.get_env("SENTRY_DSN") ||
      raise """
      environment variable SENTRY_DSN is missing.
      For example: https://public_key@app.getsentry.com/1 
      """

  config :sentry,
    dsn: sentry_dsn,
    environment_name: subdomain,
    enable_source_code_context: true,
    root_source_code_paths: [File.cwd!()]

This actually doesn’t work, just threw it up in the production deployment and I’m still getting this error:


`ERROR! the application :sentry has a different value set for key :enable_source_code_context during runtime compared to compile time. Since this application environment entry was marked as compile time, this difference can lead to different behaviour than expected:`

Get this config from one function, something like this.

def config(key) do
   Application.compile_env(:my_app, [:module, key])
end

A word of advice is to use credo, it will always warn you about defining compile-time config incorrectly in your code, and in general is a must-have tool for your elixir codebase, especially if you are new to the ecosystem.

1 Like

This article helped me a lot to understand environment handling.

https://www.theerlangelist.com/article/rethinking_app_env

2 Likes

That’s funny, I’m not new to the ecosystem and still I haven’t heard of credo. Thanks for the recommendation!

Just so this is searchable later, I found a solution to the problem. There appear to be compile-time defaults for that sentry value :enable_source_code_context, so when I set it explicitly in runtime.exs it was going from the default value to the value I set. The solution was to put those particular values into config.exs because they don’t change, and set the other runtime stuff in runtime.exs instead.