laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.52k stars 11.02k forks source link

Do not ignore system-level env variables on config:cache #23502

Closed pschaub closed 6 years ago

pschaub commented 6 years ago

Description:

The documentation describes:

Any variable in your .env file can be overridden by external environment variables such as server-level or system-level environment variables.

So the developer also thinks that the config:cache function will cache the system-level environment variable if set and not just values from the .env file. But it does not (yet).

Steps To Reproduce:

  1. Copy .env.example to .env. We use the env DB_HOST in our example.
  2. Set the environment variable DB_HOST on system-level.
  3. Run php artisan config:cache.
  4. See that the cached config is the value from the .env file and not the value from the system environment variable.
tomhatzer commented 6 years ago

@pSchaub

Basically this shouldn't be the case, as Laravel itself loads it's environment variables using DotEnv, which is called with immutable set to true, so if a system environment variable already exists and is called DB_HOST, this should not get overridden.

Which webserver do you use, if I may ask? Apache for example does NOT use environment variables from the operating system itself/set within the operating system itself per default, so you'd have to include those variables (source them) in eg. /etc/apache2/envvars and restart apache for them to be recognized by Laravel. You can also have a look at mod_env which can do the same, with a little bit more configuration (keyword PassEnv Directive): https://httpd.apache.org/docs/2.4/mod/mod_env.html

Can you please try this?

laurencei commented 6 years ago

If you dont want the system env to be overridden by your local .env file - then just remove that variable from your .env file?

rockoo commented 5 years ago

@laurencei with 5.8 I'm having the exact oposite issue. System vars are only considered if I run artisan config:cache otherwise it keeps looking first for .env file even tough its available as system var... I cant find if this was intentional change or...

laurencei commented 5 years ago

@rockoo - not sure mate. There were some issues with the whole env stuff that got fixed in 5.8.8 from memory. Try the latest version and see if the problem persists? If so, open a new ticket with all the details.

osteel commented 5 years ago

@rockoo long shot, but have you got to the bottom of this by any chance? Facing the same thing at the moment and it leaves me a bit baffled

rockoo commented 4 years ago

@osteel sorry for the late reply. Unfortunatelly not for that version. The way I got around it was to execute a shell script on docker build that would run cache command... :/

osteel commented 4 years ago

@rockoo no worries, thanks for getting back to me. I'm facing this with Laravel 6.x. I am populating the .env file via a bash script instead for the time being, although this is not ideal!

rockoo commented 4 years ago

@osteel If I come up with an idea of what is happening I will update you!

osteel commented 4 years ago

Quick update: I've now had to deploy the application to a production environment, where environment variables are injected into the container as server-level ones (no .env file). I had no other choice than to run config:cache as part of an initialisation script for the container, otherwise the environment variables are ignored.

We're using Laravel 6.2 at the moment.

danielcotton commented 4 years ago

@osteel I've also been looking into this in a container environment (although with L7) and it seems the documentation may be a little deceptive. Reading the docs, I got the impression that config:cache would cache references to the environment variables (provided the calls to env() were in config files), so they could still be altered at runtime.

But it looks like when config:cache is run, it'll just grab the current value and cache that. So if your build system's environment variables don't happen to match prod, you're SOL.

This is a pain when trying to use Laravel inside a container, because you effectively have no way to cache at build time AND have dynamic config.

@laurencei Am I sort of on the right track here? Is there simply no way to get environment variables at runtime after a config:cache (even it the calls to env() are inside config files)?

pschaub commented 4 years ago

@danielcotton You are right. You should run config:cache again in your container after you changed the environment variables. It's not enough to do it in your build. You need to run if after deployment.

See the docs:

If you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files. Once the configuration has been cached, the .env file will not be loaded and all calls to the env function will return null.

Source: https://laravel.com/docs/8.x/deployment#optimizing-configuration-loading

eriadam commented 3 years ago

You should run config:cache again in your container after you changed the environment variables.

Hey @pschaub , this is not always feasible. In kubernetes (or even in docker-compose) some ENV variables are set when the container starts up. When supporting multiple environments (dev, staging, production, etc.) and using continuous deployment, .env files are pretty much unworkable.

I would argue that system-level ENVs should take precedence over .env files.

pschaub commented 3 years ago

@eriadam Is there a reason to set the variable in the .env file if you know you will set it through the system-level env? Just remove it from the .env file?

eriadam commented 3 years ago

@pschaub Yes that works, but the problem here is with having to run config:cache when I set a new system level variable at runtime. If an ENV variable is set/changed, its new value should be available in the application the next time it is accessed. Again, this is an issue with kuberentes and multiple environments.

fuzzybaird commented 3 years ago

I agree with @eriadam. I think what is really being asked is a maybe a net-new feature or flag to the config:cache, where we can config:cache on docker build, but have the generated cached config use the native $_ENV['key']. Just a thought. When following the 12 factor app principles its best to build once and then promote with runtime configs. Running bash scripts and commands on startup is ok for more basic deployments, but when we start using Lambdas or Knative time-to-boot is imperative. Also I am happy to look into a creating a PR if that concept is compelling.

eriadam commented 3 years ago

What I ended up doing is removing the .env file completely and injecting the environment variables normally (docker-compose or k8s config map), then changing the start up command in the container like so:

FROM php:7.4-apache
// do stuff...
CMD php artisan config:cache && apache2-foreground

This caches the system environments when the container starts up, so by the time the first request comes in, they are in place. Not super happy with it, but works in our setup. Not sure it would work for @fuzzybaird's lambda use-case.

fuzzybaird commented 3 years ago

@eriadam I think that is a great solution for most people. But for me I am trying to safe precious milliseconds on container/lambda cold starts. But I will give that a try and see how much it impacts the boot time and report back.