# How to Set Up and Use Shopware's Admin Watcher with DDEV for Shopware < 6.7.3.0 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](https://github.com/shopware/shopware/issues/9393), 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:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1752131702931/6a57b42d-93b3-4ebe-8ea1-46b0c845554b.png align="center")

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

```typescript
// 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

```typescript
// 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.

```typescript
// 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

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

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

to

```typescript
// 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.

```typescript
// 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

```bash
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:

```bash
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:

```bash
ddev restart
```

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

```bash
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.
