[DX] Static vs. runtime env vars #40794
Comments
Good to see you here @webmozart :) |
@nicolas-grekas I think this confusion comes from the fact that the vcs-ignored @webmozart my solution to this problem is actually to reintroduce the parameters.yml file in my project for static values, managed with https://packagist.org/packages/incenteev/composer-parameter-handler (disclaimer: I'm the maintainer of that package), as done in the old symfony standard-edition skeleton. |
Overall, this approach is not endorsable, and goes against https://12factor.net/codebase (one source, multiple deployments), which is sensible when you have one application to be re-configured differently depending on, as it may be surprising, the environment. I only use ENV vars, and also removed bindings for What remains is the annoyance of running tasks like The workaround I found to be working for this is to define the environment variables with defaults that don't affect the system too much: # put this anywhere you want in your configs
parameters:
# empty on purpose - hides absence of env variable
env(DATABASE_URL): ''
# or put a fake value in it - I'm a github comment, not a cop
#env(DATABASE_URL): 'mysql://localhost'
doctrine:
dbal:
# use your env vars as usual
url: '%env(my:preprocessors:here:DATABASE_URL)%' Alternatively, declare This requires a lot of dancing around changes in upstream bundles, but seems to work quite reliably for the build process. Ideally, environment variables should not be touched during compilation steps, but mistakes in upstream are not always avoidable. |
@Ocramius but then, this highlights the need for a way to specify build-specific parameters in a different way than env variables, for settings that must affect the way the container is built. Bundle configurations are not always passed as is to service arguments to be used at runtime. They can also affect the way services are wired (and in such case, env placeholders don't work) |
Generally best to use a runtime factory, if such a runtime decision is needed What sometimes happens is:
These are supposed to not happen during build time at all. Perhaps what's needed is preventing usage of In practice, if I do following, and the bundle did NOT explicitly declare
Then what I could get as useful feedback is a This could potentially allow for more aggressive inlining too. |
Got the same problem here when using PrependExtension: #40198. I could only solve a part of it here, but not the usage with env variables yet. I understand the point with parameters vs env vars, but could not also bool values be this type of env vars, because for me currently most of the time it looks like only string value can be env vars and the dependency injection can not work if it is bool, int, float or other typed values. |
@Ocramius requiring bundles to declare explicitly in their configuration which settings support env placeholders and which don't support it is a separate topic (it would indeed provide better feedfack when using an env placeholder in an unsupported place, but it is hard to introduce in a BC way as we would have to support an intermediate state where bundles have not updated their config definition yet). But that's not solving this issue if we don't provide an official way to configure static values |
@alexander-schranz if you need a boolean value for which the value is only needed at runtime, you can do it with env placeholders, by using |
@stof but this doesn't work when the value is given from a bundle as value to a $resolvingBag = $container->getParameterBag();
$configs = $resolvingBag->resolveValue($configs); it fails with |
I don't think that's true. The For other readers: @nicolas-grekas pointed to his presentation on Twitter that I find quite informative: https://speakerdeck.com/nicolasgrekas/configuring-symfony-from-localhost-to-high-availability The gist is:
There are various problems with this approach:
Especially for the last reason, the Using static env vars by default would flatten the learning curve:
Much simpler IMO. |
@webmozart Should not the bundle define which env variables would be able to be changed at runtime and which are directly resolved when the container is build e.g.: ->booleanNode('enabled') // config option which does not support runtime change of env
->end()
->booleanNode('someValue')
->lazyEnv() // or ->runtimeVariable()
->end() Because I think if the bundle does something like the if ($config['enabled'])) { // when env is used the resolved env variable is given here so this will always be bool with true or false and no call for resolving itself is needed
$loader->load('some-services.xml');
}
// for the config lazyEnv the env placeholder is given here so its changeable on runtime
$container->setParameter('some_env', $config['someValue']); At this case the bundle will always get the resolved values and only defines the config values which are changeable at runtime be given to the bundle as env placeholders. |
@alexander-schranz Yes, although I would probably take the opposite approach and only mark those configuration options that explicitly don't support runtime variables. The status quo is that bundles will fail anyway if you pass runtime variables in those options. Adding Requiring all bundles that already support runtime variables today to add |
@webmozart the problem I see there is that if we automatical say all config options are possible as env vars something like this will fail: if ($config['enabled'])) {
$loader->load('some-services.xml');
} The MY_ENV_VAR=false my_config:
enabled: "%env(bool:MY_ENV_VAR)%" So I think it would better to define for which
Sure this is a BC break and could only be done in the next release or can be enabled over a flag in the config rootTree. |
The main design issue is Symfony doesn't have a distinct build vs run phase, even though it obviously has one implicitly. Having a build phase would mean requiring a reference to a env var to be fully resolved would lead to an exception because env variables are not available during the build phase. We discussed this in some detail in Symfony Slack already, it was dismissed then too. |
see #28896, i've re-opened it :) |
I'm can't say for sure that I grasp fully the scope of this, nor I am fully up to date with what the best practices are or the most commonly used patterns, but here's my feedback:
The proposal from @webmozart seems to me to add one more layer of complexity adding a concept which is quite unusual "static env vars" as it clashes with the way the rest of the OS works. More specifically:
I thought that bundle authors would only have to ever support parameters to drive the config of their bundles, and the app developers would be left with the task of having config that sets the values of those from env var values. (*) = a similar scenario: in my docker-compose builds I have come to use a single .env file to declare env vars which are used to configure everything, from mount volumes paths, to versions of php in use, to ports to expose, etc... All of the vars are used in docker-compose.yaml, some being passed on to the containers as env vars, some as variables to the dockerfiles, and hence only usable at image build time. The simplicity of having a single config file is unrivaled. However, when changing one var value in the .env file, sometimes a container restart is enough, sometimes a rebuild is needed. I found no better way to surface this requirement than making the .env file heavily documented... |
IIUC that be
|
@ro0NL thx, but those are not available in all Smfony versions I am using (currently migrating one app from 2.8 to 3.4...) |
@gggeek 2.8 does not support env variables at all... |
@stof yep. Otoh 3 does, and that's where the story about parameters started to get very complicated (imho of course) |
Description
Since switching Symfony to env vars, there is a systematic issue with bundle configurations. Env vars are replaced by string placeholders when compiling the container and those placeholders will be passed to bundle configurations. Any configuration value that expects anything else but a string will fail with an error like:
A quick research shows up several bug reports of this kind:
Benefits of env vars
Env vars as used in Symfony provide two benefits:
.env
,.env.local
etc. Those are known during container compilation.It is officially recommended to dump the environment variables to a PHP file in production. Hence my assumption would be that the majority of Symfony users is using mostly static variables.
Since static variables are known during container compilation, those could be passed to bundle configurations without ever changing a bundle to "support env vars".
Proposal: Static vs. runtime env vars
I propose to distinguish between static and runtime env vars. Static env vars (Group 1) are resolved at compile time and statically compiled into the container. Runtime env vars (Group 2) are replaced by placeholders and resolved at runtime.
For BC, all env vars are regarded as resolved at runtime. With a switch (e.g.
framework.static_env_vars
), all env vars can be switched to static.When individual variables are required to be resolved at runtime, those could be marked with a
runtime:
processor. Such variables are resolved at runtime even ifstatic_env_vars
is set totrue
.In a later major release, I propose to flip
static_env_vars
totrue
by default to improve DX.Example
Benefits
.env
) rather than two (.env
andparameters.yaml
) for deployment specific configuration.env.local
(not easily achievable withparameters.yaml
)The text was updated successfully, but these errors were encountered: