Skip to main content

Problem

I'm using DDEV on a Drupal project with Radix v6.

After creating a Radix sub-theme and configuring everything according to the quick start instructions, I got a Bad Gateway error when I ran ddev npm run watch and went to https://my-site.ddev.site:3000.

Solution

Note on ddev/ddev-browsersync

When I looked at ddev/ddev-browsersync I didn't fully understand all of the pieces of this puzzle, got errors out of the box, and didn't pursue it. Since ddev/ddev-browsersync opens the port and has instructions for using with Laravel Mix (which Radix currently uses for sub-theme tooling), it may be a partial solution. I didn't go back to try it after I figured all of this out.

Note that:

  • ddev/ddev-browsersync doesn't solve the missing watch files problem described in step 3 below.
  • You cannot use the helper command ddev browsersync. You must use ddev npm run watch to use Radix's Laravel Mix setup.

If you want to set everything up manually or want the gory details, read on.

1. Open Port 3000 on the Web Container

Running ddev npm run watch (when run from the sub-theme directory) starts browsersync inside the web container as it should, but by default the port used by browsersync is not open on the web container and the browser request gets blocked by the web container firewall.

To get past the web container firewall, you need to open port 3000 by adding a web_extra_exposed_ports section to your project's .ddev/config.yml. You may want to remove the line #ddev-generated at the top of the file.

# .ddev/config.yaml

// ... rest of config ...

web_extra_exposed_ports:
  - name: nodejs // change `name` to suit.
    container_port: 3000
    http_port: 2999
    https_port: 3000

2. Set the Browsersync Proxy Option

The Radix instructions say:

Update the DRUPAL_BASE_URL variable in your .env.local to point to your localhost address

I can't explain why, but using a typical DDEV URL value such as this does not work for me:

# .env.local
DRUPAL_BASE_URL=https://my-site.ddev.site

This results in setting the browsersync proxy option to https://my-site.ddev.site, but to get everything working I had to set the browsersync proxy option to localhost. Credit for solution this goes to the ddev/ddev-browsersync docs.

To set the proxy option to localhost, you can specify localhost in .env.local:

# .env.local
DRUPAL_BASE_URL=localhost

Or, you can change the mix.browserSync section in webpack.mix.js to something like this, which will conditionally set the proxy option to localhost if the DDEV environment is detected. 

Note that I also added the open option to prevent browsersync from attempting to open the browser when starting.

// webpack.mix.js

// ... rest of config ...

/*
  |--------------------------------------------------------------------------
  | Browsersync
  |--------------------------------------------------------------------------
*/

// Detect DDEV environment.
const isDdev = process.env.IS_DDEV_PROJECT?.toLowerCase() === 'true';

mix.browserSync({
  proxy: isDdev
    ? 'localhost'
    : process.env.DRUPAL_BASE_URL,
  // `host` option is not necessary.
  files: [
    'components/**/*.css',
    'components/**/*.js',
    'components/**/*.twig',
    'templates/**/*.twig',
  ],
  stream: true,
  open: !isDdev, // Skip launching the site in the browser.
});

// ... rest of config ...

3. Add Missing Files to Browsersync Watch

The browsersync config created with the new sub-theme doesn't watch all of the files that are produced by the SASS and Javascript build. Specifically, main.style.css and main.script.js are not watched.

This means that even though the files get re-compiled when you change their source files, browsersync doesn't push the changes to your browser.

Fix this by adding the missing files to the browsersync files option:

// webpack.mix.js

// ... rest of config ...

/*
  |--------------------------------------------------------------------------
  | Browsersync
  |--------------------------------------------------------------------------
*/
const isDdev = process.env.IS_DDEV_PROJECT?.toLowerCase() === 'true';

mix.browserSync({
  proxy: isDdev
    ? 'localhost'
    : process.env.DRUPAL_BASE_URL,
  // `host` option is not necessary.
  files: [
    'components/**/*.css',
    'components/**/*.js',
    'components/**/*.twig',
    'templates/**/*.twig',
    'build/css/main.style.css',
    'build/js/main.script.js',
  ],
  stream: true,
  open: !isDdev, // Skip launching the site in the browser.
});

// ... rest of config ...

4. Restart DDEV and Run Mix

After all of these changes, you'll need to restart everything. Be sure to run this from within the sub-theme directory on the host:

$ ddev restart && ddev npm run watch

You can optionally open the site in the browser with

$ ddev launch :3000

Then the site should load in the browser at https://my-site.ddev.site:3000, display the Browsersync: connected message in the top-right corner, and receive the injected SASS and Javascript changes as expected.

More Information

Bonus Content: Don't Use ddev nvm use

When you set the nvm version with ddev nvm use, it doesn't persist. In other words, the nvm version resets between DDEV commands. This makes ddev nvm use useless in this case.

If you're having version problems with ddev npm ..., you may need to take extra steps to force the version.

One way to do this is to set the nvm default in the container using the ddev nvm alias command. For example, this will set the nvm default to the current Radix requirement of lts/iron (which can be found in .nvmrc). Note that the version must already be installed.

$ ddev nvm alias default lts/iron

Or, when run in the sub-theme directory this will take the value directly from .nvmrc, at least in Bash.

$ ddev nvm alias default `cat .nvmrc`

For other options to specify the node version, see @donquixote's comment on GitHub and the DDEV documentation on nodejs_version.

Comments

PhilY

Oct 7, 2024 - 2:02am

4 easy steps and it now works! Thanks a lot for sharing.

Add new comment