How are you handling run-time configuration for Plugs? (poll)

I was wondering how people are handling run-time configuration of their Plugs in their Phoenix applications as of today:

  • Using {:system, “ENV_VAR”} tuple (if lib supports it)
  • Using Mix Releases & Config module (if lib supports it)
  • Wrap Plug libraries with your own Plugs to control configuration
  • I don’t need run-time configuration of plugs
  • Other (please add comment as to what you use)

0 voters

I am also curious what problems people encounter with configuring Plugs at run-time and if people would like to see some community best practices/guidelines around Plugs that allow them to be more configurable in a deployed setting (maybe documentation like this exists and I just don’t know about it??).

Personally, I have been going the route of having my Plug libraries configurable via the Config module and my config/releases.ex file.

Thanks for the input, and stay safe during these tough coronavirus days!!


Pretty sure it was the intent of the option I voted for, but we are still using Distillery releases, not native releases, as the latter are missing some nice-to-haves that we still rely on and there has been no official announcement of deprecation by the Distillery maintainers. We’ll cross that bridge when we get there or when native releases achieve full feature parity.

Essentially all of these approaches means you cannot have complete configuration of your Plug in its init function, and have to put some of it in its call instead. That’s pretty non-negotiable if you’re not willing to absorb a high performance cost. This is true regardless of where you source that configuration detail from. With that in mind, I feel like some of these survey answers aren’t super relevant or are conflating related concerns?

I think it gets to the fundamental problem of what can you do during build-time, and what has to wait during run-time. For example, you may have a CORS plug, but that requires inputs for valid origins. You could pre-populate the list of origins so that it is known at build time, or you could defer that decision to run-time and populate that list with an env var. The latter may be ever so slightly slower, but you also net having your build artifacts be environment agnostic and portable. I def agree that whatever can be determined at build time, should be done when the application is compiled.

As for the performance cost…I think it depends. If you configuration is in an env var or app config, the look up time is negligible. If your config if a network hop away…then yes. At which point you should reach for something like to resolve the configuration and store it in app config (at run-time).


Regarding my “performance cost” phrasing, I was specifically referring to the distinction of turning off compile-time Plug initialization in Plug.Builder and derivatives like Plug.Router or Phoenix.Router, which forces it to perform the way it would under MIX_ENV=dev and is comparatively very expensive as those calculations are re-performed on every request.

:init_mode - the environment to initialize the plug’s options, one of :compile or :runtime. Defaults [to] :compile.

Each Plug.Router has a plug pipeline, defined by Plug.Builder , …

Anything that needs to vary after building your compiled artifact, such as in a Mix/Distillery release based on an environment variable, has to be deferred to your Plug’s call and looked up on every request, so minimizing that effort is key when this becomes necessary for your goals. Something like persistent_term might be a good option if it’s a very hot path or something more complicated than a trivial Application.get_env call. I’d love to see this older but well-researched post updated for modern versions of Erlang/Elixir with some of the new tools like persistent_term included.

Finally, here’s some past discussions here on our forum for additional context: