Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Using & Overriding Secrets

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We have successfully added the SENTRY_DSN secret value to both the dev and prod vaults.

Listing the Secrets

How can I prove that? By running:

php bin/console secrets:list

Because we're in the dev environment, this reads the dev vault. There's our one secret. To see its value, add --reveal:

php bin/console secrets:list --reveal

Behind-the-scenes, that used the dev "decrypt" key to decrypt the value: it's an empty string. Ignore this "local value" thing for a minute.

We can do the same thing for the prod vault by passing --env=prod:

php bin/console secrets:list --env=prod

Including adding --reveal to see the value.

php bin/console secrets:list --env=prod --reveal

Reading Secrets in your App

Ok: because we're in the dev environment, the dev secret - the empty string - is the one that should be used. Refresh the page, check the dump, and expand it a few times. It's still using the production value.

Go back into config/packages/sentry.yaml:

dsn: '%env(SENTRY_DSN)%'

We're still using the syntax for reading environment variables. How can we tell it to read the SENTRY_DSN secret instead? Surprise! To tell Symfony to read a SENTRY_DSN secret, we use the exact same syntax.

Environment Variables vs Secrets

Let me explain: when Symfony sees the %env()% syntax, it first looks to see if an environment variable called SENTRY_DSN exists. If it does, it uses it. If there is not, it then looks for a secret in the vault called SENTRY_DSN. So reading environment variables and secrets uses the same syntax, but environment variables take priority.

This means one important thing: when you identify an environment variable that you want to convert into a secret, you need to remove it entirely as an environment variable. Set a value as an environment variable or a secret, but not both. Delete the SENTRY_DSN entry from .env and .env.local:

26 lines .env
... lines 1 - 22
###> sentry/sentry-symfony ###

2 lines .env.local

Now Symfony should be read from our dev vault. Refresh... expand the object and... yes! All the values are null! It works!

Let's try out production. Until now, to switch to the prod environment, I've been updating the .env file:

26 lines .env
... lines 1 - 15
###> symfony/framework-bundle ###
... lines 18 - 20
... lines 22 - 26

But now that we understand .env.local, let's add APP_ENV=prod there instead:

2 lines .env.local

Next, clear your cache:

php bin/console cache:clear

Then spin back to your browser and refresh. This time the dump is on top. If I expand it... yes! It's using the production values. Booya! That works because my project has the prod decrypt key. If that was not there, we would get an error.

Go ahead and take out the APP_ENV= line in .env.local to get back to the dev environment:

2 lines .env.local

And in QuestionController, let's cleanup: remove the dump(), the new Exception and the HubInterface argument:

... lines 1 - 12
class QuestionController extends AbstractController
... lines 15 - 42
public function show($slug, MarkdownHelper $markdownHelper)
if ($this->isDebug) {
$this->logger->info('We are in debug mode!');
$answers = [
'Make sure your cat is sitting `purrrfectly` still ?',
'Honestly, I like furry shoes better than MY cat',
'Maybe... try saying the spell backwards?',
... lines 54 - 62

After this... things are working again.

Overriding Secrets Locally

You are now ready to use Symfony's secrets system. But! The fact that environment variables take precedent over secrets is something that we can use to our advantage.

Find your terminal and run:

php bin/console secrets:list --reveal

In the dev environment, the SENTRY_DSN value is set to an empty string. Let's pretend that, while developing, I want to temporarily set SENTRY_DSN to a real value so I can test that integration.

We could use secrets:set to override the value... but that would update the secrets file... and then we would have to be super careful to avoid committing that change.

There's a better way. In .env.local, set SENTRY_DSN to the real value. Well, I'll put "FOO" here so it's obvious when this value is being used.

Now run that command again:

php bin/console secrets:list --reveal

The "Value" is still empty quotes, but now it has a "Local Value" set to the string we just used! The "Local Value" is the one that will be used. Why? Because our new environment variable overrides the secret: environment variables always win over secrets. This "Local Value" is a fancy way of saying that.

I'll take that value out of .env.local so that my secret is once again used.

Next: let's have some fun! We're going to install MakerBundle and start generating some code!

Leave a comment!

Login or Register to join the conversation
gazzatav Avatar
gazzatav Avatar gazzatav | posted 1 year ago

Now that Knp markdown is abandoned I tried to implement the markdown caching service using the suggested twig filter. However that does not support/expose MarkdownParserInterface. I noticed that the twig filter uses michelf php-markdown though, which does support that interface. Luckily I did this in another branch which is now named 'broken' :( Autowiring does not pick up the michelf classes, do I need to put Michelf\Markdown in autoload.php? How do I stop twig from inlining the classes and making them disappear?

Ruslan Avatar

I get :
How to see DSN?

Nick-F Avatar
Nick-F Avatar Nick-F | Ruslan | posted 2 years ago | edited

So in your controller method, you want to run


Hey Name->Nick,

Thank you for this tip again!


Victor Avatar Victor | SFCASTS | Ruslan | posted 2 years ago | edited

Hey Ruslan,

If you want to dump SENTRY_DSN, you can do it in whatever PHP file you want, e.g. add this in a controller where you need this value:

// or

Both should print you your DSN value for Sentry if you set any in your .env files

I hope this helps!


akincer Avatar
akincer Avatar akincer | posted 2 years ago

How do you access secrets in code? It doesn't seem like getenv() will pull a secret.

akincer Avatar

I figured out what I was doing wrong, so ignore this question.

Joao P. Avatar
Joao P. Avatar Joao P. | posted 3 years ago

Hi, I'm getting:

Environment variable not found: "SENTRY_DSN".

But the secret is configured as below:

$ bin/console secrets:list --reveal --env=dev

// Use "%env(<name>)%" to reference a secret in a config file.

------------ ------- -------------
Secret Value Local Value
------------ ------- -------------
------------ ------- -------------

And my sentry.yaml file show:

dsn: "%env(SENTRY_DSN)%"

I've delete the SENTRY_DSN lines from .env and .env.local

Joao P. Avatar

It started working, I'm not sure why, but the only thing I think changed was that I restarted the Symfony server.


Hey Joao P.

That's weird. Perhaps it was due to the cache? Who knows :)

Joao P. Avatar

No, it wasn't cache. Cleared it alright

Marwâne C. Avatar
Marwâne C. Avatar Marwâne C. | posted 3 years ago | edited

I'm getting the following error when trying to reveal secrets in prod environment:

PHP Fatal error: Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException: Circular reference detected for parameter "env(base64:default::SYMFONY_DECRYPTION_SECRET)" ("env(base64:default::SYMFONY_DECRYPTION_SECRET)" > "env(default::SYMFONY_DECRYPTION_SECRET)" > "env(SYMFONY_DECRYPTION_SECRET)" > "env(base64:default::SYMFONY_DECRYPTION_SECRET)"). in cauldron-overflow\vendor\symfony\dependency-injection\Container.php:389

I used this command: php bin/console secrets:list --env=prod --reveal, Symfony version: 5.1.2

Any ideas?


Hey @Beam OP!

I know this :). You have found a Symfony bug! https://symfonycasts.com/sc...

It is fixed in Symfony 5.1.3, but that hasn't been released yet - I expect that to happen any day now - it's been 28 days since the last bug release.


1 Reply
msniezko Avatar
msniezko Avatar msniezko | posted 3 years ago

I am getting the following error in logs when switching to prod:

[2020-06-19T16:54:53.389249+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\OptionsResolver\Exception\InvalidOptionsException: "The option "dsn" with value "" is invalid." at C:\Users\Michael\code\code-symfony-fundamentals\start\vendor\symfony\options-resolver\OptionsResolver.php line 987 {"exception":"[object] (Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException(code: 0): The option \"dsn\" with value \"\u0016\" is invalid. at C:\\Users\\Michael\\code\\code-symfony-fundamentals\\start\\vendor\\symfony\\options-resolver\\OptionsResolver.php:987)"} []

Hey Michał,

If you use Git - try to commit changes and run:

$ git grep dsn

This command will show you all the spots where you use dsn. Make sure that all dsn options are set properly in your project. Also, keep eyes closer where those values should come from, probably you need to set up an env var in your .env or .env.local files? Or probably specifically in .env.prod in your case?


erop Avatar
erop Avatar erop | posted 3 years ago | edited

Hey! Doesn't secrets work for console commands? Something weird is happening with my project. When I ran ./bin/console doctrine:database:drop it returns <blockquote>Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.</blockquote> I set a breakpoint at exception line and found out that $params local variable does <b>not</b> contain data for my PostgreSQL connection: at least $params["driver"] = pdo_mysql . I faced with the issue after deploying my whole Docker Compose stack to remote machine through Docker Machine. I set all the demo env var through secrets intentionally. After deploying I decided to re-create database and started with doctrine:database:drop and got that error. Trying to execute doctrine:migrations:migrate shows $dsn == mysql:host=localhost;dbname=;charset=utf8mb4; Nonsense! To be honest I just lost debugging call stack to find out why the app takes default env values instead of stored in secrets. Any ideas?

erop Avatar

The problem is that the secrets are NOT read for some reason. They are successfully displayed with `./bin/console secrets:list --env=demo --reveal` but do not work until decrypting into `.env.demo.local`. `debug:config framework secrets` confirms that secrets are enabled. Some googling showed that there might be a problem with sodium extension but I have one both on dev and demo machine/environments.

erop Avatar

Event setting SYMFONY_DECRYPTION_SECRET environment variable gives no help...

erop Avatar

OMG! Shame on me! I've just forgot to comment out env vars in .env file. I use an empty env vars in .env just not to forget to set them in .env.<env_name> file. Thus empty values in .env just override secrets. And only decrypting secrets with `secrets:decrypt-to-local` writes .env.<env_name>.local which in its turn override empty values in `.env`.


Dang! what a sneaky error. I'm glad you figured it out. FYI what you put on the .env.<environment_name> will override any other value you already have


Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.3.0 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "symfony/asset": "5.0.*", // v5.0.11
        "symfony/console": "5.0.*", // v5.0.11
        "symfony/debug-bundle": "5.0.*", // v5.0.11
        "symfony/dotenv": "5.0.*", // v5.0.11
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.0.*", // v5.0.11
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/profiler-pack": "*", // v1.0.5
        "symfony/routing": "5.1.*", // v5.1.11
        "symfony/twig-pack": "^1.0", // v1.0.1
        "symfony/var-dumper": "5.0.*", // v5.0.11
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.0.*" // v5.0.11
    "require-dev": {
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/profiler-pack": "^1.0" // v1.0.5