Debugging
This page is for developers who need to look inside a running site when the logs don't explain a problem. It covers 3 tools:
- Xdebug, to step through PHP code in your IDE
- Session cookies with curl, to request pages that need a login
- Docker Compose commands, to inspect the containers directly
For container and Drupal logs, see Logs.
Xdebug
The Xdebug website describes the tool as follows:
Xdebug is an extension for PHP, and provides a range of features to improve the PHP development experience.
The Lagoon images that Vortex builds on come with Xdebug installed and configured for local development.
The CLI image also installs PCOV, a coverage-only PHP extension. PHPUnit collects coverage through PCOV whenever it's loaded, so tests report coverage with Xdebug turned off.
➡️ See PHPUnit
Usage
Xdebug is off by default. ahoy debug turns it on by recreating the cli,
php and nginx containers with XDEBUG_ENABLE=true:
- Ahoy
- Docker Compose
ahoy debug # Restart containers with Xdebug enabled.
# Restart containers with Xdebug enabled.
XDEBUG_ENABLE=true docker compose up -d cli php nginx
When it turns Xdebug on, ahoy debug prints
Enabled debug configuration. Use 'ahoy up' to disable. If Xdebug is
already on, ahoy debug changes nothing and prints
Debug configuration is already enabled. Use 'ahoy up' to disable.
The Lagoon images turn Xdebug on only when XDEBUG_ENABLE is true, in
any letter case, so a value such as 1 leaves Xdebug off.
Xdebug connects back to port 9003, its default, on host.docker.internal.
Where that name doesn't resolve, such as on Linux, Xdebug connects to the
container's default gateway. Set your IDE to listen on port 9003.
To turn Xdebug off, recreate the containers without the variable:
- Ahoy
- Docker Compose
ahoy up # Restart containers with Xdebug disabled.
# Restart containers with Xdebug disabled.
docker compose up -d cli php nginx
XDEBUG_ENABLE can also come from .env or .env.local, which Ahoy loads
before every command. With XDEBUG_ENABLE=true in either file, every
ahoy up starts the containers with Xdebug on, so to turn Xdebug off,
remove the line and run ahoy up.
ahoy info shows whether Xdebug is on.
CLI
While Xdebug is on, a PHP script you run in a shell in the cli container,
including through ahoy cli, starts a debugging session with no browser
extension. The Lagoon images set XDEBUG_TRIGGER in every shell session in
that container.
The Lagoon images also set PHP_IDE_CONFIG, which names the server after
the local development URL, so PhpStorm reuses the path mapping you set for
web requests.
Drush is the exception you're most likely to hit. Its shell wrapper,
vendor/bin/drush, sets XDEBUG_MODE=off before it starts PHP, for
performance. To keep Xdebug on, pass --xdebug, as in
ahoy drush --xdebug <command>.
If a breakpoint never fires in another command-line tool, check that tool's documentation: some tools switch Xdebug off in their own way.
IDE configuration
This section covers PhpStorm. Other IDEs need the same mapping between your
project root and /app in the container.
PhpStorm
PhpStorm treats the container as a remote server, so it needs a path
mapping. The mapping matches your project root to /app in the container.
You set it once, and PhpStorm remembers it.
PhpStorm accepts debug connections only while listening is on. The toolbar button with a little bug on it turns listening on. By default, PhpStorm prompts you to map a connection that has no mapping yet.
To use Xdebug on the project for the first time, starting from a fully working site, follow these steps:
-
Install the Xdebug helper extension for your browser:
- Chrome Xdebug Helper by JetBrains extension
- Firefox Xdebug Helper by JetBrains extension
- Edge Xdebug Helper by JetBrains add-on
-
In the extension, turn on debugging for the site, as its instructions describe. The extension sets the
XDEBUG_SESSIONcookie, which starts a debugging session for each request. -
In PhpStorm, click the toolbar button with the little bug to start listening for debug connections.
-
Set a breakpoint in your IDE.
index.phpin your web root is a good place to start. -
To turn on Xdebug in the containers, run
ahoy debugorXDEBUG_ENABLE=true docker compose up -d cli php nginx. -
Refresh the page in your browser. PhpStorm stops on your breakpoint and prompts you to map the incoming connection to a directory in your project.
-
In the prompt, map your project root to
/app.
-
Confirm that debugging works: refresh the page again, and PhpStorm stops on the breakpoint without prompting for a mapping.
For more detail, see the following pages:
- Configure Xdebug in the PhpStorm documentation
- Setting up Xdebug with Lagoon in the Lagoon documentation
Tips and tricks
After your first Xdebug session works, change these PhpStorm Debug settings for everyday debugging:
- Disable the following options:
- Break at first line in PHP scripts
- Force break at first line when no path mapping specified
- Force break at first line when a script is outside the project
- Increase Max. simultaneous connections to
5or more, because with a lower value, a debug session can get stuck without showing up in the IDE.
Request authenticated pages with curl
Use curl with session cookies to request pages that need a login, such as
admin pages and protected routes, without a browser. It suits ad-hoc checks
during troubleshooting:
- An admin page that returns a 500 error
- A permission problem
- Page content you want to verify
For automated tests, use Behat or PHPUnit.
Sign in and request a page
The following commands sign in once and reuse the saved session cookie for later requests:
- Ahoy
- Docker Compose
# Step 1: Get the site URL from project info
ahoy info
# Note the URL (e.g., http://your-project.docker.amazee.io)
# Step 2: Get a one-time login URL
ahoy login
# Output example:
# http://your-project.docker.amazee.io/user/reset/1/1234567890/abc123/login
# Step 3: Authenticate and save session cookies
# Replace LOGIN_URL with the full URL from Step 2
curl -k -c .data/cookies.txt -L "LOGIN_URL"
# Step 4: Access any authenticated page using saved cookies
# Replace SITE_URL with your site URL from Step 1
curl -k -b .data/cookies.txt "SITE_URL/admin/content"
# Step 5: Check for specific errors in page output
curl -k -b .data/cookies.txt "SITE_URL/admin/content" \
| grep -iE "(error|exception|warning|notice)"
# Step 1: Get the site URL from project info
docker compose exec cli ./vendor/bin/vortex-info
# Note the URL (e.g., http://your-project.docker.amazee.io)
# Step 2: Get a one-time login URL
docker compose exec cli drush uli
# Output example:
# http://your-project.docker.amazee.io/user/reset/1/1234567890/abc123/login
# Step 3: Authenticate and save session cookies
# Replace LOGIN_URL with the full URL from Step 2
curl -k -c .data/cookies.txt -L "LOGIN_URL"
# Step 4: Access any authenticated page using saved cookies
# Replace SITE_URL with your site URL from Step 1
curl -k -b .data/cookies.txt "SITE_URL/admin/content"
# Step 5: Check for specific errors in page output
curl -k -b .data/cookies.txt "SITE_URL/admin/content" \
| grep -iE "(error|exception|warning|notice)"
Confirm that the session works: the page from step 4 doesn't contain
You are not authorized to access this page., the message on Drupal's
access denied page.
Curl flags reference
The following table explains the curl flags that these commands use:
| Flag | Purpose |
|---|---|
-k | Ignores SSL certificate errors. Required for self-signed local certificates. |
-c FILE | Saves cookies to FILE after the request. |
-b FILE | Sends cookies from FILE with the request. |
-L | Follows redirects. Required for the login flow. |
Cookies and login links
The following points apply to the saved cookies and the login links:
- Store cookies in the
.data/directory: the shipped.gitignoreexcludes it, so a session cookie stays out of your commits. - Cookies expire after the session timeout, so sign in again to get new ones.
- Login URLs have the format
http://SITE/user/reset/UID/TIMESTAMP/HASH/login. - A login URL works once. Unless
VORTEX_LOGIN_UNBLOCK_ADMINis0,ahoy loginalso unblocks user 1 before it generates the link, which a plaindrush ulidoesn't.
Container debugging
The following commands show the containers' status and logs, and open a shell or a database prompt inside a container:
# Check container status
docker compose ps
# View container logs
docker compose logs [service_name]
# Examples:
docker compose logs cli
docker compose logs database
# Access container shell
docker compose exec cli bash
docker compose exec database mysql -u drupal -p drupal
The database name, user and password are all drupal in the local stack,
so enter drupal when mysql prompts for the password.