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 useddev 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
.