This tutorial has a new version, check it out!

Building for Production

I love our new setup! So it's time to talk about optimizing our build files for production. Yep, it's time to get serious, and make sure our files are minified and optimized to kick some performance butt!

Because, right now, if you check out the size of the build directory:

ls -la public/build

... yea! These files are pretty huge - rep_log.js is over 1 megabyte and so is layout.js! If you looked inside, you would find the problem immediately:

... lines 1 - 2
import $ from 'jquery';
... lines 4 - 24

jQuery is packaged individually inside each of these! That's super wasteful! Our users should only need to download jQuery one time.

The Shared Entry


The createdSharedEntry() feature still works great, but in the latest version of Encore, there is a new way to solve this problem called splitChunks(). Read about it here:

No problem! Webpack has an awesome solution. Open webpack.config.js. Move the layout entry to the top - though, order doesn't matter. Now, change the method to createSharedEntry():

... lines 1 - 3
... lines 5 - 10
.createSharedEntry('layout', './assets/js/layout.js')
... lines 12 - 25
... lines 27 - 30

Before we talk about this, move back to your terminal and restart Encore:

yarn run encore dev --watch

Then, I'll open a new tab - I love tabs! - and, when it finishes, check the file sizes again:

ls -la public/build

Woh! rep_log.js is down from 1 megabyte to 300kb! layout.js is still big because it does still contain jQuery. But login.js - which was almost 800kb is now... 4!

What is this magical shared entry!? To slightly over-simplify it, each project should have exactly one shared entry. And its JS file and CSS file should be included on every page.

When you set layout.js as a shared entry, any modules included in layout.js are not repeated in other files. For example, when Webpack sees that jquery is required by login.js, it says:

Hold on! jquery is already included in layout.js - the shared entry. So, I don't need to also put it in login.js.

It's a great solution to the duplication problem: if you have a library that is commonly used, just make sure that you import it in layout.js, even if you don't need it there. You can experiment with the right balance.

The manifest.js File

As soon as you do this, if you refresh, it works! I'm kidding - you'll totally get an error:

webpackJsonp is not defined

To fix that, in your base layout, right before layout.js, add one more script tag. Point it to a new build/manifest.js file:

... lines 1 - 96
{% block javascripts %}
... lines 98 - 100
<script src="{{ asset('build/manifest.js') }}"></script>
<script src="{{ asset('build/layout.js') }}"></script>
{% endblock %}
... lines 104 - 107

The reason we need to do this is... well.. a bit technical. But basically, this helps with long-term caching, because it allows your giant layout.js file to change less often between deploys.

Production Build

Ok, this is great, but the files are still pretty big because they're not being minified. How can we tell Encore to do that? In your terminal, run:

yarn run encore production

That's it! This will take a bit longer: there's more magic happening behind the scenes. When it finishes, go back to your first open tab and run:

ls -la public/build

Let's check out the file sizes! The development rep_log.js that was 310kb is down to 74! Layout went from about 1Mb to 125kb. The CSS files are also way smaller. Yep, building for production is just one command: Encore handles all the details.

Adding Shortcut scripts

Oh, and here's a trick to be even lazier. Open package.json. I'm going to paste a new script section:

20 lines package.json
"devDependencies": {
... lines 3 - 11
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production"

This gives you different shortcut commands for the different ways that you'll run Encore. Oh, we didn't talk about the dev-server, but it's another option for local development.

Anyways, now, in the terminal, we can just say:

yarn watch

Or any of the other script commands - like yarn build for production.

How to Deploy

Talking about production, there's one last big question we need to answer: how the heck do you deploy your assets to production? Do we need to install Node on the production server?

The answer is.... it depends. It depends on how sophisticated your deployment system is. Honestly, if you have a very simple deploy system - like a simple script, or maybe even some commands you run manually - then the easiest option is to install Node and yarn on your server and run encore production on your server after pulling down the latest files.

I know: this isn't a great solution: it's a bummer to install Node just for this reason. But, it is a valid option and totally simple.

A better solution is to run Encore on a different machine and then send the final, built files to your server. This highlights an important point: after you execute Encore, 100% of the files you need live in public/build. So, for example, after you execute:

yarn run encore production

you could send the public/build directory to your production machine and it would work perfectly. If you have a "build" server, that's a great place to run this command. Or, if you watched our Ansistrano Tutorial, you could run Encore locally, and use the copy module to deploy those files.

If you have any questions on your specific situation, you can ask us in the comments.

Leave a comment!

  • 2018-12-12 Victor Bocharsky

    Hey Zorpen,

    Yeah, so probably this is the best way to implement it, at least I can't think about alternative solutions :)


  • 2018-12-12 Zorpen

    Hey Victor Bocharsky
    Thanks, that is exactly the thing i have done. I mean checking public/build directory :)

  • 2018-12-12 Victor Bocharsky

    Hey Zorpen,

    Hm, interesting question! Well, most of the time we neglect it because saving a few seconds in your entire build is not a huge performance improvement. But if you want to optimize things more - I think it's possible, but be aware of some weird edge cases, like when you upgraded webpack encore library but your assets wasn't changed and you'll have a blind spot where the new version of Encore won't be changed :) So, it's always tricky with some edge cases, so my point of view is do not complicate things much and it's not a big deal to re-build your assets every time. Though, if you have a lot of assets and you know you can save a good time with this strategy - why not, go fo it.

    I quickly look at CircleCI docs and didn't find anything they would suggest for this. But I bet you can easily do it yourself - just check if "public/build/" directory is empty and only then execute "yarn run encore production" command. I think it's possible to do with one-line command, but you probably also can create a separate shell script and put all the logic there. Unfortunately, my knowledge of Unix commands are limited, so I can't quickly give you a complete command, but I think if you google - you can find commands you can use for this.


  • 2018-12-12 Zorpen

    Hi weaverryan
    Sorry for bothering you, but i have another question ;)
    I was able to cache assets on circleCI this way:

    # assets cache
    - run: git log --pretty=format:'%H' -n 1 -- assets/ > assets_checksum
    - run: echo "$(cat assets_checksum)"
    - restore_cache:
    - project-{{ .Branch }}-{{ checksum "./assets_checksum" }}-v1
    - run: ls -la public/build #here i can see that public/build directory was downloaded from cache

    - run: yarn run encore production
    - save_cache:
    key: project-{{ .Branch }}-{{ checksum "./assets_checksum" }}-v1
    - public/build

    I can see that when build is ran for the first time, everything is cached like i want to, but on the next runs `- run: yarn run encore production` is always triggered. Soooo how can i check if cache was hit? I mean something like "if assets were downloaded from cache then skip yarn run encore production step"? Hope you understand what i mean :)

  • 2018-12-12 Zorpen

    Thanks weaverryan
    I'll dig into cache idea :)

  • 2018-12-11 weaverryan

    Hey Zorpen!

    Hmm, there's nothing that I'm aware of specifically, though depending on your CI environment, this should be possible. Basically, most CI environments have the ability to cache directories, so it's very easy to cache your public/build directory. However, the tricky part is knowing when you need to invalidate this cache. In theory, if you got a "checksum" if your entire assets/ directory, you could use that as the "cache key" for caching your public/build directory. Then, if any files change, the checksum would change, and the assets would rebuild.

    Sorry I can't give you a more specific answer. We use CircleCI, and they do allow for cache keys like this. Anyways, it's a very interesting idea! Let me know what you find out.


  • 2018-12-11 Zorpen

    Hey weaverryan
    Quick question. Is there any "simple" way to tell if something has changed in the assets? I'm asking because my `yarn encore run production` takes like 5 minutes to complete (yeah i know - lot of stuff), so my CI builds takes forever. I would like to be able to check if something has changed, and only then build assets from scratch.

  • 2018-11-08 weaverryan

    Hey Cédric GNIEWEK!

    You're right! And I did it to myself :p. We released the new Webpack Encore version 3 days ago, which uses Webpack 4 where the shared entry is no longer the "recommended" solution. We will upgrade this tutorial, but I'm not sure exactly when - I want to make sure we're done with big changes inside of Encore before we do that. Until then, we would be happy to answer any questions about the new "split chunks" feature. And, the shared entry will not go away any time soon - it's still a valid solution.


  • 2018-11-08 Cédric GNIEWEK


    Seems like shared entry is no longer the best solution for sharing assets in Symfony's documentation.

    Will there be an update of this tutorial ?

  • 2018-08-20 Diego Aguiar

    NP man, you are welcome!

  • 2018-08-18 Eric

    Welp... you are right. Tried an incognito window and it showed no error. Then I started toggling the plugins for Chrome one-by-one and the culprit was the "JSONView" plugin. Totally forgot I had that plugin installed. My apologies for the wild goose chase! But thank you for helping me figure it out!

  • 2018-08-17 Diego Aguiar

    Indeed it's odd, I don't have that problem on Chrome but I'm on Windows. You said that the site works correctly, right? Probably a Chrome's plugin that you have installed is causing this weird behavior, just for curiosity, try disabling them all and open a new window in "incognito" mode.

  • 2018-08-16 Eric

    Hi Diego Aguiar

    Thank you for the speedy reply. I updated everything I could think of (node, npm, yarn) and error still shows up when I navigate to the file directly. I am not sure why Chrome is showing the error. I was going to update Chrome as well but it says I am already on the latest version.

    My versions of packages are:
    node: v10.9.0
    npm: 6.2.0
    yarn: 1.9.4

    Running yarn build instead of npm run build yields the same result as well. I downloaded Firefox and cant see any error or anything. Maybe something with Chrome on mac? I've tried searching online for the error but have not come up with anything concrete. Very odd.

  • 2018-08-16 Diego Aguiar

    Hey Eric

    Hmm, interesting, makes me wonder if you run `yarn build` instead of `npm run build` would cause any difference. Also try updating your node & yarn version

    If nothing changes, let us know!


  • 2018-08-16 Eric

    After building for production (npm run build) and I navigate directly to the build JS file (ex: <host>/build/app.f40a0e24.js) I get the following error in Chrome:

    Error: Parse error on line 1:
    Expecting 'EOF'

    When building for dev, I get no such error. The manifest file, gets no such error either. But, the shared JS file and page specific JS files do get this error when I build for production. The odd things is that I do not see any errors in my console when loading the page and such and the JavaScript that I do have on the page still seems to work... I only seem to get the error when opening the file directly in the browser.. any idea why this would be occurring? I'm a little hesitant to actually ship the code to production with the error present.

    The first couple lines of the file getting the error are:

    webpackJsonp([0],{"7t+N":function(e,t,n){var r,i;/*!
    * jQuery JavaScript Library v3.3.1
    * Includes Sizzle.js
    * Copyright JS Foundation and other contributors
    * Released under the MIT license
    * Date: 2018-01-20T17:24Z
    !function(t,n){"use strict";"object"==typeof e&&"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}

  • 2018-06-04 Victor Bocharsky

    Hey Matt,

    So there're a few strategies:
    - Deploy changes to prod and build assets on the prod server, such tools as Ansistrano allow you to do so in a background, i.e. your website won't be totally style-broken when you re-build your assets.
    - Or build assets locally and just push them to production, so you even do not need to have NodeJS installed on production. And once again, Ansistrano handles it well.

    In case your wondering about Ansistrano deploy tool, we have a screencast about it here:

    > What about just committing your build files?

    That's another, 3rd and good strategy... but have some disadvantages, but mostly depends on your configuration. Here's a one of them which I do not like a lot: If you use a hash strategy to solve the cache assets problem (like file's names as main_9foiwhf.js), each build the same files will have a totally different names which means Git unable to manually understand that it was just renaming, and Git will think you removed old file and created a totally new file, and as a result, your Git repository will be slowly bloated. Btw, sometimes you want to handle some images with Encore which will copy to the build directory that also will bloat your Git repo.

    So, I think 3rd strategy is not a good one, so I'd recommend to choose between those 2 I mentioned in the beginning.


  • 2018-06-03 Matt Johnson

    That sucks about prod... I wonder if you could use something like salt to build it on the master and then push it to the minion when you deploy.

    What about just committing your build files?

  • 2018-05-22 Victor Bocharsky

    Hey Cesar,

    Glad you find a working solution for you. And actually many devs lean to this deploy strategy because do not want to install NodeJS on their production server at all. So I even do not consider this as a workaround. Anyway, if it works for you - that's awesome!


  • 2018-05-21 Cesar Delgado

    Thanks Victor. I didn't want to increase the memory because is a server of pre-production. I have followed your second recommendation and it's working well and the deploy it's much faster.


  • 2018-05-21 Andrea

    Ok, now i've understood. Thanks

  • 2018-05-21 Victor Bocharsky

    Hey Andrea,

    Ah, sorry, I totally missed it! Yes, you're right, we do have manifest.js file here due to the shared entry. So, did you change the line for layout.js to:

    .createSharedEntry('layout', './assets/js/layout.js')

    Keep in mind, you don't have to have any other line related to "layout.js" except this one in your webpack.config.js.

    And another important condition: You need to restart the Encore, i.e. stop watching files and re-run that command again:

    yarn run encore dev --watch

    Unless you restart the Encore, you won't see that manifest.js file in the build/ directory.


  • 2018-05-21 Andrea

    I'm talking about the file named in this tutorial, this is the link:
    Maybe i missed something?

  • 2018-05-21 Victor Bocharsky

    Hey Andrea,

    OK, what is manifest.js then? :) Because IIRC we do not talk about manifest.js file but manifest.json instead. Does it your custom JS file? If so, probably some name conflicts? Could you try to rename this file into a different file name?


  • 2018-05-21 Victor Bocharsky

    Hey Cesar,

    OK, great job to find the root of your problem, well done! Well, I haven't heard about this problem before, so nothing much I can tell you about solutions, probably better to google for a good advices in your case. But if you won't find any working solution on the internet I do see a few solutions for you:

    - Increase memory, looks like you really need just a bit more memory to make it working;
    - Or stop installing deps and building assets on your prod server. Instead, install and build them locally, then just use synchronize Ansible module to *rsync* the built assets to your prod server. This way you even do not need to have NodeJS on your server


  • 2018-05-21 Cesar Delgado

    Hi Victor,

    It's not working from Ansible in any way. Neither with command, shell or via nodes that you told me.

    But, I was trying to do it manually in the current directory in my server and sometimes it didn't work and it was giving me an exit code 137. I was looking in the internet about that and, apparently, it's related to memory. However, I don't understand how is that possible because I check my server and it was fine in terms of memory and I am trying in a pre-production environment.

    Have you ever had this error?


  • 2018-05-18 Andrea

    Sorry, i'm talking about manifest.js and not manifest.json (the second one is correctly inside the build folder, the first one not.).

  • 2018-05-18 Diego Aguiar

    Hey Andrea

    The "manifest.json" file is only generated when you execute Encore, when the process finishes, then you will see all your assets and the manifest file inside your build folder


  • 2018-05-18 Victor Bocharsky

    Hey Cesar,

    Hm, your task looks valid, let's try to change it slightly, try to run encore via "nodejs":

    - name: Install Webpack Encore Assets
    command: nodejs ./node_modules/.bin/encore production
    chdir: '{{ ansistrano_release_path.stdout }}'
    NODE_ENV: production

    And probably setting NODE_ENV env var could help here. If not, try to change command module to shell one and try again.


  • 2018-05-18 Andrea

    I'm using webpack encore in my new project, everything going fine but i don't find any manifest.js in my build folder.

    Actually, my console not notifing me the webpackJsonp error but i'm not sure about that.

    Is it normal?
    Is this file generate by encore only in certain situation or i missing something?

    Thanks in advance

  • 2018-05-17 Cesar Delgado

    Hey Victor,

    This is task and it's located in after_symlink_shared.yml

    - name: Install Webpack Encore Assets
    command: './node_modules/.bin/encore production'
    chdir: '{{ ansistrano_release_path.stdout }}'

    Your proposal is to change command for shell?

    Please, let me know.

  • 2018-05-17 Victor Bocharsky

    Hey Cesar,

    Hm, 0 is good here, ok, could you show this entire Ansible task to us? Probably the problem in it. Also, do you use command Ansible module? If so, try to replace it with shell module and try again. Or do the reverse if you use shell module.


  • 2018-05-16 Cesar Delgado

    Hi Victor,

    I was working with ansible 2.4 and I had the error. Now, I upgrade to ansible 2.5 and I have the same error.

    I run the command manually in my server and it works fine (I run it in the current directory). Also, it returns 0.

    Do you have any idea what the problem can be?


  • 2018-05-16 Victor Bocharsky

    Hey Cesar,

    I think somehow the command returns not a 0 code that's why Ansible thinks it fails... Could you double check it? On the prod server run the command manually and then check its status:

    ./node_modules/.bin/encore production
    echo $?

    Does it return 0 or a different number? Because that's weird that you can run this command successfully manually, but not with Ansible :/

    Btw, had you upgraded your Ansible before that error occurred?


  • 2018-05-15 Cesar Delgado

    Hi guys, I am using Ansistrano to deploy my Symfony 3 app and my deploy playbook was working fine until today that I had an weird error executing the task Install Webpack Encore Assets.

    The error is this: FAILED! => {"changed": true, "cmd": ["./node_modules/.bin/encore", "production"], "delta": "0:02:22.350866", "end": "2018-05-15 18:16:34.750211", "failed": true, "msg": "non-zero return code", "rc": -9, "start": "2018-05-15 18:14:12.399345", "stderr": "", "stderr_lines": [], "stdout": "Running webpack ...", "stdout_lines": ["Running webpack ..."]}

    I didn't find anything related to this in Google and my app is working in localhost. Also, I have tried to execute the command './node_modules/.bin/encore production' directly on my server and is working. I didn't make any changes to my webpackconfig.js. So, I don't know what this can be. I have tried many times to deploy and the same error is still there.

    Can you give me a tip or some advice to fix this? I hope you can help me.

  • 2018-04-24 weaverryan

    Haha, cheers buddy! :)

  • 2018-04-23 Dan Meigs

    Hey guys!

    Thanks for another great tutorial. Webpack Encore is awesome!