Production Build & Deployment

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Ok team: just one more thing to talk about: how the heck can we deploy all of this to production?

Well, before that, our files aren't even ready for production yet! Open the public/build/ directory. If you open any of these files, you'll notice that they are not minified. And at the bottom, each has a bunch of extra stuff for "sourcemaps": a bit of config that makes debugging our code easier in the browser.

Building For Production

We get all of this because we've been creating a development build. Now, at your terminal, run:

yarn build

This is a shortcut for yarn encore production. When we installed Encore, we got a pre-started package.json file with... this scripts section:

30 lines package.json
... lines 2 - 19
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress"
... lines 26 - 28

So, the real command to build for production is encore production, or, really:

./node_modules/.bin/encore production

Anyways, that's the key thing: Encore has two main modes: dev and production.

And... done! On a big project, this might take a bit longer - production builds can be much slower than dev builds.

Now we have a very different build/ directory. First, all of the names are bit obfuscated. Before, we had names that included things like app~vendor, which kind of exposed the internal structure of what entry points we had and how they're sharing data. No huge deal, but that's gone: replaced by these numbered files.

Also, if you look inside any of these, they're now totally minified and won't have the sourcemap at the bottom. You will still see these license headers - that's there for legal reasons, though you can configure them to be removed. Those are the only comments that are left in these final files.

And even though all the filenames just changed, we instantly move over, refresh, and... it works: the Twig helpers are rendering the new filenames.

Free Versioning

In fact, you may have noticed something special about the new filenames: every single one now has a hash in it. Inside our webpack.config.js file, this is happening thanks to this line: enableVersioning():

... lines 1 - 2
... lines 4 - 45
// enables hashed filenames (e.g. app.abc123.css)
... lines 48 - 76
... lines 78 - 79

And check it out, the first argument - which is a boolean of whether or not we want versioning - is using a helper called Encore.isProduction(). That disables versioning for our dev builds, just cause we don't need it, but enables it for production.

The really awesome thing is that every time the contents of this article_show.css file changes, it will automatically get a new hash: the hash is built from the contents of the file. Of course, we don't need to change anything in our code, because the Twig helpers will automatically render the new filename in the script or link tag. Basically... we get free file versioning, or browser cache busting.

This also means that you should totally take advantage of something called long-term caching. This is where you configure your web server - like Nginx - to set an Expires header on every file it serves from the /build directory with some super-distant value, like 1 year from now:

server {
    # ...

    location ~ ^\/build\/ {
        expires 365d;
        add_header Cache-Control "public";

The result is that, once a user has downloaded these files, they will never ask our server for them again: they'll just use their browser cache. But, as soon as we update a file, it'll have a new filename and the user's browser will ask for it again. It's just free performance. And if you got a step further and put something like CloudFlare in front of your site, your server will receive even less requests for your assets.


Now that we have these, optimized, versioned files, how can we deploy them up to production? Well... it depends. It depends on how sophisticated your deployment is.

If you have a really simple deployment, where you basically, run git pull on production and then clear the Symfony cache, you're probably going to need to install node on your production server, run yarn install, and then run yarn build up on production, each time you deploy. That's not ideal, but if you have a simple deployment system, that keeps it simple.


We show this on practice in our Animated Deployment with Ansistrano course.

If you have a slightly more sophisticated system, you can do it better. The key thing to understand is that, once you've run yarn build, the only thing that needs to go to production is the public/build directory. So you could literally run yarn build on a different server - or even locally - and then just make sure that this build/ directory gets copied to production.

That's it! You don't need to have node installed on production and you don't need to run anything with yarn. If you followed our tutorial on Ansistrano, you would run yarn wherever you're executing Ansistrano, then use the copy module to copy the directory.

More Features

Ok, that's it! Actually, there are more features inside Encore - many more, like enabling TypeScript, React or Vue support. But getting those all going should be easy for you now. Go try them, and report back.

And, like always, if you have any questions, find us in the comments section.

All right friends, seeya next time.

Leave a comment!

This tutorial works great with Symfony5!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "aws/aws-sdk-php": "^3.87", // 3.91.4
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.9.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "liip/imagine-bundle": "^2.1", // 2.1.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
        "oneup/flysystem-bundle": "^3.0", // 3.0.3
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.3.1
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.2.5
        "symfony/console": "^4.0", // v4.2.5
        "symfony/flex": "^1.0", // v1.6.2
        "symfony/form": "^4.0", // v4.2.5
        "symfony/framework-bundle": "^4.0", // v4.2.5
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/security-bundle": "^4.0", // v4.2.5
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "^4.0", // v4.2.5
        "symfony/validator": "^4.0", // v4.2.5
        "symfony/web-server-bundle": "^4.0", // v4.2.5
        "symfony/webpack-encore-bundle": "^1.4", // v1.5.0
        "symfony/yaml": "^4.0", // v4.2.5
        "twig/extensions": "^1.5" // v1.5.4
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
        "symfony/dotenv": "^4.0", // v4.2.5
        "symfony/maker-bundle": "^1.0", // v1.11.5
        "symfony/monolog-bundle": "^3.0", // v3.3.1
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "^3.3|^4.0" // v4.2.5

What JavaScript libraries does this tutorial use?

// package.json
    "devDependencies": {
        "@symfony/webpack-encore": "^0.27.0", // 0.27.0
        "autocomplete.js": "^0.36.0",
        "autoprefixer": "^9.5.1", // 9.5.1
        "bootstrap": "^4.3.1", // 4.3.1
        "core-js": "^3.0.0", // 3.0.1
        "dropzone": "^5.5.1", // 5.5.1
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.4.0", // 3.4.0
        "node-sass": "^4.11.0", // 4.11.0
        "popper.js": "^1.15.0",
        "postcss-loader": "^3.0.0", // 3.0.0
        "sass-loader": "^7.0.1", // 7.1.0
        "sortablejs": "^1.8.4", // 1.8.4
        "webpack-notifier": "^1.6.0" // 1.7.0