Introducing pre-compiled JS assets for self-hosters

:mega: Discourse now publishes pre-built JavaScript assets, which will significantly speed up installation and updates, especially for servers with limited resources.


Compiling and optimizing JavaScript assets has always been one of the most resource-intensive parts of running Discourse. As our codebase and the JavaScript ecosystem have evolved, this process has become even more demanding.

In our tests, these changes reduce the asset-build time on a 1GB RAM Digital Ocean droplet from 45 minutes to just 3 minutes.

How does it work?

On every commit merged to main, a GitHub Actions workflow builds and bundles the assets into .tar.gz files (one for production, one for development). These bundles are published via GitHub releases in a dedicated repository. We ensure assets are published before any commit moves to tests-passed.

When building your own site, Discourse now checks for a matching pre-built bundle and downloads it. Plugins are then built on top. If no bundle is found or there’s an error, Discourse falls back to building from source.

Does this impact end-users?

No. Assets are still served to end-users from your own server / CDN.

Can I opt out?

Yes! If you prefer to build your own assets and have a sufficiently powerful server, set DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS: 0 in your app.yml file.

What if I’m running a forked or patched version of Discourse?

Asset bundles are named by commit hash. If you’re running a fork, no bundle will be found and assets will build from source. If your Discourse copy is patched (i.e., the git working tree isn’t clean), Discourse will not attempt to download a bundle.

What about other asset-related build steps?

Currently, this optimization applies only to core JS assets. In the future, we may expand it to some plugins and other steps like gz/brotli compression.

What about the stable branch?

Pre-built assets for the stable branch will be published starting with the next major version bump, planned for August 2025.

34 Likes

Just did a rebuild of https://discourse-on-a-pi5.falco.dev/ and it took just 3 minutes and 35s, this is very impressive!

17 Likes

Two container setup and without database ~8 minutes.

4 Likes

Whoa :scream: :person_bowing:

I don’t have this option in my config, do I need to add it with a value of 1 so I can opt in? Or will all this just magically happen when I next update?

FYI this is what happens when your installation doesn’t meet the criteria:

I, [2025-08-01T06:43:09.560655 #1]  INFO -- : > cd /var/www/discourse && su discourse -c 'bundle exec rake assets:precompile:build'
[assemble_ember_build] Node.js heap_size_limit is less than 2048MB. Setting --max-old-space-size=2048 and CHEAP_SOURCE_MAPS=1
[assemble_ember_build] No existing build info file found.
[assemble_ember_build] Git working directory is not clean. Cannot download prebuilt assets.
[assemble_ember_build] Running full core build...

I’m doing some tweaks to config/initializers/100-sidekiq.rb in app.yml to add support for a retry count on all sidekiq jobs (which I presume is the only way to achieve this and not within a plugin? But could revisit) so I believe this is enough not to meet the criteria …

1 Like

It’s enabled by default. You only need to specify the config if you want to opt-out.

Yeah exactly. Any kind of diff in the git repo will cause it to bail out.

Re. the sidekiq thing, if you open a topic about it, I’m sure we can work out a way to do it either from a plugin, or maybe we could introduce a new GlobalSetting for it.

2 Likes

btw, a very simple way to time your builds without having to remain present at your console is the following:

time ./launcher rebuild app

(or whatever flavour of container if you aren’t using just one container)

which will report the elapsed time at the conclusion of the build :stopwatch:

3 Likes