How to run Laravel Dusk inside a Docker container

This tutorial assumes that you use laravel/dusk:^1.0 and version 3 of Docker Compose file format.

Make sure you have Dusk installed. For more details on the subject, refer to documentation.

Before jumping straight into Docker shenanigans, let’s first mention a couple details that may trip you up.

Laravel Dusk won’t work with in-memory SQLite database or DatabaseTransactions trait because it’s running in a separate process. For similar reasons, it won’t respect environment variables declared in phpunit.dusk.xml. Also, it is slow. Like, really slow. After all, it opens an actual browser window to run tests.

STEP 1. Run Selenium Server in a container

Add a service based on selenium/standalone-chrome image to your docker-compose.yml. Note, that this service must share a network with both Web server and PHP. Other than that, it does not require any additional configuration.

STEP 2. Point DuskTestCase to a Selenium Server URL

Change URL in DuskTestCase::driver

return RemoteWebDriver::create(
'http://selenium:4444/wd/hub', DesiredCapabilities::chrome()

By default, Selenium Server will be exposed on port 4444. In the example above, selenium is a magic hostname that resolves service name to container IP address.

Do not hard-code the URL, though. It’s likely that not everyone on your team will commit to using Docker, so you should make the setup reasonably flexible:

switch (config('dusk.driver')) {
case 'container':
return RemoteWebDriver::create(
'http://selenium:4444/wd/hub', DesiredCapabilities::chrome()
default: // local
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()

STEP 3. Configure APP_URL

For browser tests, your APP_URL should point to the Web Server IP address inside Docker network. In the example below, we have nginx service running in a container, while sed simply performs find and replace on a given file in command line.

sed -i "s#APP_URL=.*#APP_URL=http://nginx#" .env.dusk.local

Alternatively, if you use jwilder/nginx-proxy and have your original APP_URL set to any value other than localhost, you can alias your proxy service to the desired URL in docker-compose.yml:


And that’s about it. Basically, the only tricky part is correctly resolving application URL.