If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWhen you deploy to production, you're supposed to set all these environment variables correctly. If you look back at index.php
:
... lines 1 - 9 | |
// The check is to ensure we don't use .env in production | |
if (!isset($_SERVER['APP_ENV'])) { | |
if (!class_exists(Dotenv::class)) { | |
throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.'); | |
} | |
(new Dotenv())->load(__DIR__.'/../.env'); | |
} | |
... lines 17 - 40 |
If the APP_ENV
environment variable is set already, it knows to skip loading the .env
file.
Tip
If you start a new project today, you won't see this APP_ENV
logic. It's
been moved to a config/bootstrap.php
file.
In reality... in a lot of server environments, setting environment variables can be a pain. You can do it in your Apache virtual host or in PHP-FPM. Oh, and you'll need to make sure it's set at the command line too, so you can run bin/console
.
If you use a Platform-as-a-Service like Platform.sh or Heroku, then setting environment variables is super easy! Lucky you!
But if setting environment variable is tough in your situation, well, you could still use the .env
file. I mean if we deployed right now, we could create this file, put all the real values inside, and Symfony would use that! Well, if you're planning on doing this, make sure to move the dotenv library from the require-dev
section of your composer.json
to require
by removing and re-adding it:
composer remove symfony/dotenv
composer require symfony/dotenv
The reason that using .env
isn't recommended is mostly because the logic to parse this file isn't optimized: it's not meant for production! So, you'll lose a small amount of performance - probably just a couple of milliseconds, but you can profile it to be sure.
Tip
The performance cost of .env
has been shown to be low. It is ok to use
a .env
file in production if that's the most convenient way for you to set
environment variables.
But... there is one other limitation of environment variables that affects everyone: environment variables are always strings! But, what if you need an environment variable that's set to true or false? Well... when you read it with the special syntax, "false" will literally be the string "false". Boo!
Don't worry! Environment variables have one more trick! You can cast values by prefixing the name with, for example, string:
:
nexy_slack: | |
endpoint: '%env(string:SLACK_WEBHOOK_ENDPOINT)%' |
Well, this is already a string, but you get the idea!
To show some better examples, Google for Symfony Advanced Environment Variables to find a blog post about this feature. Cooooool. This DATABASE_PORT
should be an int
so... we cast it! You can also use bool
or float
.
This is great... but then, the Symfony devs went crazy. First, as you'll see in this blog post, you can set default environment variable values under the parameters
key. For example, by adding an env(SECRET_FILE)
parameter, you've just defined a default SECRET_FILE
environment value. If a real SECRET_FILE
environment variable were set, it would override this.
More importantly, there are 5 other prefixes you can use for special processing:
First, resolve:
%foo%
things - if you have them inside your environment variable;Second, you can use file:
to return the contents of a file,
Third, base64:
will base64_decode
a value: that's handy if you have a value
base64_encode
it to make it easier to set as an environment variable;Fourth, constant:
allows you to read PHP constants;
And finally, json:
will, yep,
json_decode()
a string.And, ready for the coolest part? You can chain these: like, open a file, and then decode its JSON:
app.secrets: '%env(json:file:SECRETS_FILE)%'
Actually, sorry, there's more! You can even create your own, custom prefix - like blackhole:
and write your own custom processing logic.
Ok, I'll shut up already about environment variables! They're cool, yadda, yadda, yadda.
Let's move on to a super fun, super unknown "extra" with autowiring.
Hey Venimus,
Good question! Well, it's good to not use the $ chat in your password anyway, you van always change your generated password and remove all $ chars or replace them with another special char. Otherwise, nothing much you can do here, though there's a workaround for it - use some variable defined with module which doesn't support variable interpolation, e.g. you may do so with geo:
geo $dollar {
default "$";
}
And then use the variable instead.
The source is: https://forum.nginx.org/read.php?2,218536,218559
Unfortunately, this workaround won't work in your case anyway I think. So, just don't use $ chars.
Cheers!
Well, it is not only passwords (I just gave it as an example). For instance I wanted to store a regular expression which is passed from the deployment server and which is not under my control, so I can not simply avoid $ in such cases
Hey Venimus,
So, does that trick with non-interpolated variable I shown in my previous comment works for you? Otherwise, nothing much we can do here. You can try to ask your question on StackOverflow, probably someone has a similar problem and solved it in some way.
Cheers!
Whats the best possible option to store env variables on a apache web server, if you don't have access to the vhost config file? To still use the dotenv bundle with the .env file or to write it inside the .htaccess file inside /public? (On shared webhosting you can't access the vhost file)
Hey Mike,
It's totally OK to use Dotenv Component on production if you don't have access to the vhost config files on your hosting. I don't think it would be better to store them in .htaccess, at least because doing it in .env files are more convenient than in .htaccess. Also, it might depend on if you're able to set env vars in .htaccess or no, probably this feature might be disabled as well. So, as far as your .env files are secure and nobody can get access to them via web - it's totally OK to use them instead for storing your env vars, i.e. it's the same way as it was before with well-known parameters.yaml files.
Cheers!
Hello, I'm in the same situation as Mike, so, sorry to bother you with a really naïve question...But how can I be sure that nobody can get access to my .env file ? What can I do to be certain ? Some htaccess magic ? Could you give me an example ? I'm about to put my very first S4 app online, I spent a lot of time securing it (thanks to your awesome site), I would be quite disappointed if my .env file was easily readable. While researching about that matter on the web I found that resource which stressed me out : https://medium.com/@marcelp...
Thanks for your help.
Hey Jean-tilapin!
It's all good - security is important! In short, you have nothing to worry about. In both Symfony and Laravel (to continue from what that article says), your .env file is NOT in your public/ directory and so it is NOT accessible on the web. It's just that simple. Only things in public/ can be read by the outside world. The fact that there are some sites out there (as proven by his Google search) that DO expose their .env file is actually pretty impressive: it means they (for some reason) decided to change the default structure of their Symfony or Laravel app so that the "document root" was the root of their project, instead of public/. They probably did that because they're on shared hosting and didn't know a better way to do it.
So, as long as you're using the default directory structure, you're good, because your web server is pointing at your public/ directory, and so only things there are accessible.
Cheers!
I'm having a problem with deployment to an Ubuntu server running Apache.
I want to define the environment variables in the Apache vhost configuration and that works for the web site.
Unfortunately, it does not work for console commands since the console component has no way to get the values from Apache.
I don't really want to also put them in a .env.prod file because that breaks the DRY principle (an knowing me it will certainly break at some point).
So, what is the "proper" solution?
Thanks
Hey David P.
Nice job getting the environment variables into the vhost config. But... in my opinion (and most of the core Symfony team I believe agrees with me), you should not use real environment variables at all and should instead create a .env.local file when you deploy to production (you could create a .env.prod file, but then it would be committed to your repository).
Basically, env vars make *incredible* things possible in certain environments. They make using Docker way easier and most PAAS (like Symfony Cloud or Heroku) leverage env vars - it is *the* way to set config and they make it *super* easy. But in more traditional setups, like a server you maintain (we still have "normal" servers too), setting env vars is a pain for the exact reason you highlighted. So... don't. There is zero disadvantage to reading the .env.local file vs setting env vars. Do it, and simplify :).
Let me know if this helps!
Cheers!
On dev machine, if you don't want to setup environment variables (with fastcgi_params) you can use this list to override the .env template file:
0) symfony/dotenv package install (composer require symfony/dotenv)
1) .env file with some "template" data (database_url, app_env, so on..)
2) .env DO NOT contain any sensitive data (password, hash, so on)
3) create a copy of .env file: named ".env.local" or similar
4) add the new file name to .gitignore
5) insert/replace the code below to use the newly created .env.local file into your public/index.php file (this is one of the "missing / incomplete / not clear" part of the documentation):
// The check is to ensure we don't use .env in production
if (!isset($_SERVER['APP_ENV']) && !isset($_ENV['APP_ENV'])) {
if (!class_exists(Dotenv::class)) {
throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.');
}
(new Dotenv())->load(__DIR__.'/../.env', __DIR__.'/../.env.local');
}
5) The key to override .env is this part: , __DIR__.'/../.env.local'
6) Set all environment variable (local dev database connection, so on) in the .env.local file.
Now the .env file template will not cause db connection or other errors on local dev, because the second file overrides it.
Hey Csaba B.
Thanks for sharing it! Just in case, here are the docs of local env vars: https://symfony.com/blog/ne...
Cheers!
Example:
Apache production webhost, do I understand you right that it is recommended to set the env variable via (by example):
SF-Project/public/.htaccess
SetEnv SLACK_WEBHOOK_ENDPOINT https://...
[...Symfony other stuff...]
Is this the correct way for apache?
I keep asking myself because it seems to make sense to keep this project related informations inside the project itself rather than inside an apache vhost config file.
Hey Mike P.
If the variable may change depending on the machine (server) where the project lives, then, it must be an "Environment" variable, if not, then, it can be an application's variable.
I would recommend storing your env variables inside the vhost config file instead of ".htaccess" file because it's more secure
Cheers!
Thanks for the fast reply!
Since most "shared web hosting companys" do not provide access to the apache virtual host file, whats the best solution for this case?
Hmm, nice question and to be honest I'm not totally sure but I would choose a place that is outside of the public web directory (I'm not sure if you can do that in a shared hosting)
For the environment variables, previous convention in Symfony 2 and 3, (or so I thought), was to store them in separate environment config yaml files that are ignored and not committed, but live in the config directory.
Hey Kegan,
You're right, it was app/config/parameters.yml file for server-specific parameters, which should not be committed, but there's should be another one - app/config/parameters.yml.dist which should and hold the same keys but different (dummy) values which work for dev/test environment.
Cheers!
Hi, on 1:16 I don't think you meant to have the --dev on the composer call, because you are explaining how to add it for production. Thanks!
Hey Jose Diaz
Good observation, but remember that is not recommended to use it on production, maybe if you have developed a small app, and you just want to deploy it, you could rely on it so you can avoid the over head of configuring environment variables manually, but as soon as your project starts growing, you should consider removing that bundle from production, and use "your" particular method of setting up env variables
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.1.4
"symfony/asset": "^4.0", // v4.0.4
"symfony/console": "^4.0", // v4.0.14
"symfony/flex": "^1.0", // v1.17.6
"symfony/framework-bundle": "^4.0", // v4.0.14
"symfony/lts": "^4@dev", // dev-master
"symfony/twig-bundle": "^4.0", // v4.0.4
"symfony/web-server-bundle": "^4.0", // v4.0.4
"symfony/yaml": "^4.0" // v4.0.14
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.2", // v1.0.4
"symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
"symfony/dotenv": "^4.0", // v4.0.14
"symfony/maker-bundle": "^1.0", // v1.0.2
"symfony/monolog-bundle": "^3.0", // v3.1.2
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
"symfony/profiler-pack": "^1.0", // v1.0.3
"symfony/var-dumper": "^3.3|^4.0" // v4.0.4
}
}
There is a problem with escaping $ in fastcgi_params in fact you can not set variable to a value that contains $ using the nginx config. This renders is quite useless because many generated passwords have $'s in them. How to overcome this?