How to Set Up and Use Shopware's Admin Watcher with DDEV for Shopware 6.7 and Vite

Starting with Shopware 6.7, the administration is built with Vite. Because of the new build tool, the structure and how hot module reloading works is different now.

I know that this guide is not bulletproof regarding update safety, because each Shopware core update will overwrite the changes. But you have to keep in mind that it is also hard for Shopware to maintain different development environments. I'm pretty sure Shopware will add a fix if there is time left to implement it, because as you will see, there is not much to change to make it work with DDEV.

So let's jump right into it.

The Problem

There is an issue on GitHub titled Vite watch administration not loading plugin modules, which outlines the problem developers face when using DDEV with Shopware 6.7's Vite-based administration. In short, each plugin (or extension) starts its own Vite dev server. However, because DDEV uses the ddev-router in the background, this setup does not work out of the box.

If you try to use the admin watcher without applying the necessary changes, you will encounter errors such as:

Of course, the administration is still working but your plugins (or extensions) will not get injected.

The root cause is that DDEV cannot map or, more accurately, does not allow access to the ports used by the Vite dev servers. To resolve this, you need to apply the changes described in this guide.

Changes in plugins.vite.ts

On line 184, there is the extensionEntries config starting. Within the forEach loop change this

from

// vendor/shopware/administration/Resources/app/administration/build/plugins.vite.ts

if (extension.isApp) {
    swPluginDevJsonData[extension.technicalName].html = `http://${host}:${availablePorts[index]}/index.html`;
}

if (extension.isPlugin) {
    swPluginDevJsonData[extension.technicalName].js = `http://${host}:${availablePorts[index]}/${fileName}`;
    swPluginDevJsonData[extension.technicalName].hmrSrc = `http://${host}:${availablePorts[index]}/@vite/client`;
}

to

// vendor/shopware/administration/Resources/app/administration/build/plugins.vite.ts

const realHost = process.env.DDEV_PRIMARY_URL
              ? `${process.env.DDEV_PRIMARY_URL.replace(/:\d+$/, '')}`
              : `http://${host}`;

if (extension.isApp) {
    swPluginDevJsonData[extension.technicalName].html = `${realHost}:${availablePorts[index]}/index.html`;
}

if (extension.isPlugin) {
    swPluginDevJsonData[extension.technicalName].js = `${realHost}:${availablePorts[index]}/${fileName}`;
    swPluginDevJsonData[extension.technicalName].hmrSrc = `${realHost}:${availablePorts[index]}/@vite/client`;
}

Afterwards, change the server config for app and the plugin system.

Add const host = '0.0.0.0'; within the loop for (let i = 0; i < extensionEntries.length; i++) to make it accessible via the host machine.

// vendor/shopware/administration/Resources/app/administration/build/plugins.vite.ts

// Start dev servers
 for (let i = 0; i < extensionEntries.length; i++) {
    const host = '0.0.0.0';
    // ... rest of code
}

Next, search for createServer and replace from

// vendor/shopware/administration/Resources/app/administration/build/plugins.vite.ts

server: {
    port,
    host,
    cors: true,
},

to

// vendor/shopware/administration/Resources/app/administration/build/plugins.vite.ts

server: {
    port,
    host,
    origin: process.env.DDEV_PRIMARY_URL
        ? `${process.env.DDEV_PRIMARY_URL.replace(/:\d+$/, '')}:` + port
        : undefined,
    cors: {
        origin: /https?:\/\/([A-Za-z0-9\-\.]+)?(\.ddev\.site)(?::\d+)?$/,
    },
},

Changes in vite.config.mts

Add the cors config to the server config which starts at line 65 and add it after the origin property.

// vendor/shopware/administration/Resources/app/administration/vite.config.mts

cors: {
    origin: /https?:\/\/([A-Za-z0-9\-\.]+)?(\.ddev\.site)(?::\d+)?$/,
},

Changes in the .env file

Add a entry for host like

HOST=0.0.0.0

Changes in .ddev/config.yaml

In order to expose all needed ports, there has to be an entry for each port. From my experience, it is sufficient to expose the ports 5173-5400. Each plugin will get its own dev server starting at port 5333. Because DDEV does not support port ranges natively in config.yaml, we have to define each port separately.

You can create the entries with this bash script:

for i in $(seq 5173 5400); do
  echo "- name: webdev-dev-server-admin-port-$i"
  echo "  container_port: $i"
  echo "  http_port: $(((i-1)*10))"
  echo "  https_port: $i"
done > /path/to/file/ports.txt

Next, take the contents of the generated ports.txt file and add them to your ./ddev/config.yaml file. Look for the commented line containing web_extra_exposed_ports—this is where you should insert the port entries to keep the configuration organized.

Finally, restart your DDEV project with:

ddev restart

You should now be able to use the admin watcher by running

ddev exec ./bin/watch-administration.sh

Summary

By following the steps above, you can successfully configure Shopware 6.7's Admin Watcher to work seamlessly with DDEV. The adjustments ensure that hot module reloading and plugin development work reliably in your local environment. While these changes may need to be reapplied after Shopware updates, they provide a practical solution for efficient development workflows with DDEV and Shopware 6.7 for extensions within the administration.


Let me know if it worked for you, or if you found anything that could be improved.

0
Subscribe to my newsletter

Read articles from Wolfgang Kreminger directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Wolfgang Kreminger
Wolfgang Kreminger

Hi there! My name is Wolfgang and I used to work as a paramedic for several years. However, in 2018 I decided to switch careers and become a fullstack developer. I achieved this by completing the zero-to-mastery course and gaining the necessary skills to embark on this new path. Since then, I have been working as an e-commerce developer, building and maintaining online stores for various clients. It has been an exciting and challenging journey, but I am loving every moment of it. I am grateful for the opportunity to learn and grow in this field, and I am excited to see where my skills will take me next. As someone who has personally experienced the transformative power of learning to code, I have a deep passion for teaching others how to do the same. I believe that with the right tools and guidance, anyone can learn to code and create amazing things. That's why, in addition to my work as an e-commerce developer, I also enjoy sharing my knowledge and experience with others through teaching and mentorship. Whether it's through in-person workshops or online courses, I love helping people discover their own potential and unlock their creativity through programming. It's a rewarding and fulfilling experience for me, and I hope to continue doing so for many years to come.