Mutating Application env with put_env in containers, good or bad?

I have this slack bot app with an api to check for stats. As its containerised with docker. I was using env file via docker-compose. The app was using System.get_env to fetch values every time which obviously doesn’t sounds good to make a system level call for every incoming request. So I decided to put them in config file and as module level constants but for both the values are not available at runtime as they are set via docker-compose and are only available once the container is running.
One approach would be to embed those values in the Dockerfile or pass them along the build phase but that is same as embedding it in, which I don’t prefer. This missing values also causes issues with libraries like sentry where it depends on environment variables.

One way I solved it is upon application starting I call put_all_env and set the values from System.get_env and then use Application,.fetch_env!

so what I would like to know is if that is ok to do or are there any other practices for containers to have compile time and runtime values available for releases.

It sounds like you’re probably looking for the releases functionality that was added in Elixir 1.9. Here’s a good guide about setting up a Docker container that builds using mix release, where the runtime config is set from environment variables.

Thanks for the reply , my app is already a multistage docker build using docker-compose to run it. From the article it seems it relies on the vars to be passed while bringing up the container. Maybe thats the way to go by which I might have to make peace with but then again one could have many such variables for the config making it error prone to start up your application or the command complex enough to remember.

for my 2nd part of the question is mutating Application env acceptable ? is it bad or just a choice ?

Sorry, I think I misunderstood the question.

I usually have a config/rel/runtime.exs file, which sets the application config from the system’s environment variables. e.g.:

config :app, :component, api_key: System.fetch_env!("API_KEY")

Then I make as many calls to Application.fetch_env! as I need to from the runtime code - they’re cheap enough.

I wouldn’t mutate the Application environment further at runtime. I think of it as layers: System Environment -> Application Environment -> Runtime code, with each layer reading the layer outside it, but never writing to it.

That means accepting that your docker run command might reference a lot of environment variables. I’d prefer to keep everything explicit, so I’d tend to solve that through other means, perhaps a bash script, depending on your environment.

1 Like

Are you actually running into concrete performance problems with this? I wouldn’t really expect you to unless you’re calling that in a very tight loop.

2 Likes

not really for my current use case but considering it is called quite frequently I wouldn’t do it in ruby, new to elixir so trying to understand its impact for high volume apps. Does it have any additional overhead compared to Application.fetch_env. the bot can get enough request to simulate a high traffic app and I haven’t seen any impact with reading multiple values from System yet

so list down whats missing or happening while using docker-compose
I have the config sentrey set via env variable

config :sentry,
  dsn: System.get_env("SENTRY_DSN"),
  included_environments: [:prod, :dev],
  environment_name: Mix.env()

The value is set via env_file option in the docker-compose file.

after doing a build the
docker-compose build
values are available inside the container when I run it
docker-compose up

docker exec -it ultronex_bot sh

I can see the value for $SENTRY_DSN and is successfully set by Application.put_all_env at runtime.

but the sys.config which is in the build failed to fetch the values while building a release which means its not available to the build process

{sentry,[{dsn,nil},{included_environments,[prod,dev]},{environment_name,prod}]}

Seems this could be due to use of docker-compose wher its env_file only sets values inside the container and not in build process and using the docker command with command line vars is the way to build with runtime dependencies. If that is the case then how to do a build with docker-compose with runtime configs or maybe I got wrong docker-compose settings

what I understand the env variables set via env_file can only be available after container runs, and to do a elixir release the build would require ARGS being set that then are set as ENV in the build phase to be available as runtime variables for the release to use

I think I have for runtime variables figured out for docker-compose. need to define the env variables on the build machine and add values via args to docker-compose and then refer to them in the Dockerfile as ARG which are then available to the build process.

thanks for your insights helped me put things in better order as newbie to elixir