Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

The "prod" Environment

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 $12.00

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

Login Subscribe

Our app is currently running in the dev environment. Let's switch it to prod... which is what you would use on production. Temporarily change APP_ENV=dev to prod:

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

then head over and refresh. Whoa! The web debug toolbar is gone. That... makes sense! The entire web profiler bundle is not enabled in the prod environment.

You'll also notice that the dump from our controller appears on the top of the page. The web profiler normally captures that and displays it down on the web debug toolbar. But... since that whole system isn't enabled anymore, it now dumps right where you call it.

And there are a lot of other differences, like the logger, which now behaves differently thanks to the configuration in monolog.yaml.

Clearing the prod Cache

The way pages are built has also changed. For example, Symfony caches a lot of files... but you don't notice that in the dev environment. That's because Symfony is super smart and rebuilds that cache automatically when we change certain files. However, in the prod environment, that doesn't happen.

Check it out! Open up templates/base.html.twig... and change the title on the page to Stirred Vinyl. If you go back over and refresh... look up here! No change! The Twig templates themselves are cached. In the dev environment, Symfony rebuilds that cache for us. But in the prod environment? Nope! We need to clear it manually.

How? At your terminal, run:

php bin/console cache:clear

Notice it says that it's clearing the cache for the prod environment. So, just like how our app always runs in a specific environment, the console commands also run in a specific environment. And, it reads that same APP_ENV flag. So because we have APP_ENV=prod here, cache:clear knew that it should run in the prod environment and clear the cache for that environment.

Thanks to this, when we refresh... now the title updates. I'll change this back to our cool name, Mixed Vinyl.

Changing the Cache Adapter for prod Only

Let's try something else! Open up config/packages/cache.yaml. Our cache service currently uses the ArrayAdapter, which is a fake cache. That might be cool for development, but it won't be much help on production:

... lines 3 - 10
app: cache.adapter.array
... lines 12 - 25

Let's see if we can switch that back to the filesystem adapter, but only for the prod environment. How? Down here, use when@prod and then repeat the same keys. So framework, cache, and then app. Set this to the adapter we want, which is called cache.adapter.filesystem:

... lines 3 - 10
app: cache.adapter.array
... lines 12 - 20
app: cache.adapter.filesystem

It's going to be really easy to see if this works because we're still dumping the cache service in our controller. Right now, it's an ArrayAdapter. If we refresh... surprise! It's still an ArrayAdapter. Why? Because we're in the prod environment... and pretty much any time you make a change in the prod environment, you need to rebuild your cache.

Go back to your terminal and run

php bin console cache:clear

again and now... got it - FilesystemAdapter!

But... let's reverse this config. Copy cache.adapter.array and change it to filesystem. We'll use that by default. Then at the bottom, change to when@dev, and this to cache.adapter.array:

... lines 3 - 10
app: cache.adapter.filesystem
... lines 12 - 20
app: cache.adapter.array

Why am I doing that? Well, that literally makes zero difference in the dev and prod environments. But if we decide to start writing tests later, which run in the test environment, with this new config, the test environment will use the same cache service as production... which is probably more realistic and better for testing.

To make sure this still works, clear the cache one more time. Refresh and... it does! We still have FilesystemAdapter. And... if we switch back to the dev environment in .env:

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

and refresh... yes! The web debug toolbar is back, and down here, we are once again using ArrayAdapter!

Now, in reality, you probably won't ever switch to the prod environment while you're developing locally. It's hard to work with... and there's just no point! The prod environment is really meant for production! And so, you will run that bin/console cache:clear command during deployment... but probably almost never on your local machine.

Before we go on, head into VinylController, go down to browse(), and take out that dump():

... lines 1 - 12
class VinylController extends AbstractController
... lines 15 - 32
#[Route('/browse/{slug}', name: 'app_browse')]
public function browse(HttpClientInterface $httpClient, CacheInterface $cache, string $slug = null): Response
$genre = $slug ? u(str_replace('-', ' ', $slug))->title(true) : null;
$mixes = $cache->get('mixes_data', function(CacheItemInterface $cacheItem) use ($httpClient) {
$response = $httpClient->request('GET', 'https://raw.githubusercontent.com/SymfonyCasts/vinyl-mixes/main/mixes.json');
return $response->toArray();
return $this->render('vinyl/browse.html.twig', [
'genre' => $genre,
'mixes' => $mixes,

Okay, status check! First, everything in Symfony is done by a service. Second, bundles give us services. And third, we can control how those services are instantiated via the different bundle configuration in config/packages/.

Now, let's go one important step further by creating our own service.

Leave a comment!

Login or Register to join the conversation
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "knplabs/knp-time-bundle": "^1.18", // v1.19.0
        "symfony/asset": "6.1.*", // v6.1.0-RC1
        "symfony/console": "6.1.*", // v6.1.0-RC1
        "symfony/dotenv": "6.1.*", // v6.1.0-RC1
        "symfony/flex": "^2", // v2.1.8
        "symfony/framework-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/http-client": "6.1.*", // v6.1.0-RC1
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/runtime": "6.1.*", // v6.1.0-RC1
        "symfony/twig-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/ux-turbo": "^2.0", // v2.1.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.14.1
        "symfony/yaml": "6.1.*", // v6.1.0-RC1
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.0
    "require-dev": {
        "symfony/debug-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/maker-bundle": "^1.41", // v1.42.0
        "symfony/stopwatch": "6.1.*", // v6.1.0-RC1
        "symfony/web-profiler-bundle": "6.1.*" // v6.1.0-RC1