Compilation and Static Content

setup:di:compile writes the generated PHP; setup:static-content:deploy writes pub/static per area, theme and locale.

Syntaxbin/magento setup:static-content:deploy [<locales>] [-t theme] [-a area] [-j jobs] [-s quick|standard|compact] [-f]

Two commands turn a checkout into something production mode can serve. They are separate because they fail for different reasons and cost different amounts of time.

setup:di:compile

bin/magento setup:di:compile reads every di.xml in every enabled module and writes the PHP that nobody typed by hand: factories, proxies, the interceptor subclass that makes each plugin run, and the merged dependency injection configuration for each area. Output goes to generated/code and generated/metadata. Production mode never creates these during a request, so a plugin whose interceptor was not compiled simply never runs. The compile needs no database, which is why build servers can do it.

setup:static-content:deploy

This command compiles LESS, resolves theme and asset inheritance, and writes files to pub/static, one set per area, per theme, per locale. Two themes and four store view locales is eight sets, so this is the long step. Locales are positional arguments; -t (long form --theme), -a (--area) and -j (--jobs) are the same options as their long forms. The locales that matter are the ones configured on your store views, plus the interface locale of each admin user.

What trips people up

Compile first, deploy second. If you pass -t or -l, anything you leave out is not written and nothing warns you. Outside production mode the deploy refuses to run without -f (--force). The default strategy is quick; standard writes every file for every package and compact trades time for disk space.

Example

Example · bash
# build machine, Magento root, no database needed for these two steps

composer install --no-dev --prefer-dist

bin/magento setup:di:compile
# ... factories, proxies, interceptors and the DI config land in generated/

bin/magento setup:static-content:deploy en_US de_DE \
    --theme Vendor/storefront \
    --theme Magento/backend \
    --jobs 4
# ... one directory per area, theme and locale under pub/static

# pub/static and generated/ are now part of the artifact you ship

When to use it

  • Building a release artifact on CI so the production server never compiles anything.
  • Working out which locales a multi-store site actually has to deploy.
  • Re-deploying one theme after a LESS change instead of waiting for the full run.
  • Diagnosing a plugin that works in developer mode and does nothing in production.

More examples

Find the locales you are obliged to deploy

Each store view's locale decides which pub/static locale directory the storefront asks for. A store view set to de_DE with no de_DE deployed produces a page whose assets 404, because production mode will not generate them on request. Admin users have their own interface locale, which is why the automatic deploy run by deploy:mode:set covers store locales and admin user locales together.

Example · bash
# the default scope value
bin/magento config:show general/locale/code

# and the override on a particular store view
bin/magento config:show --scope=stores --scope-code=uk_store general/locale/code

# valid locale identifiers, if you are unsure of a code
bin/magento info:language:list

Re-deploy only what changed

Narrowing the run to one locale, one theme and one area turns twenty minutes into one, which is what you want after a LESS or template edit. Static URLs carry a version segment taken from pub/static/deployed_version.txt, so browsers keep the old files until that value changes; passing --content-version yourself keeps several web nodes agreeing on the same URLs instead of each generating its own timestamp.

Example · bash
bin/magento setup:static-content:deploy en_US -t Vendor/storefront -a frontend
bin/magento cache:clean full_page block_html

# all servers behind a load balancer should share one version string
bin/magento setup:static-content:deploy en_US -t Vendor/storefront \
    --content-version=release-2026-10-05

Why the compile is a separate step on CI

setup:di:compile reads di.xml files from disk and setup:static-content:deploy reads the scopes and themes recorded in app/etc/config.php, which is why that file belongs in source control. Neither needs the live database, so both can run before a deploy window instead of inside it. The server then only runs setup:upgrade and cache:flush, which take seconds rather than minutes.

Example · bash
# CI: build stage, no database, no OpenSearch, no env.php secrets
composer install --no-dev --no-progress
bin/magento setup:di:compile
bin/magento setup:static-content:deploy en_US -j "$(nproc)"

# ship the tree, including generated/ and pub/static, to the web nodes
tar -czf release.tgz .

Discussion

  • Be the first to comment on this lesson.