Skip to main content
Version: v1

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.

Vortex comes with Xdebug pre-installed and configured for local development thanks to Lagoon images.

Code coverage doesn't need Xdebug. The CLI image also installs PCOV, a coverage-only PHP extension, and 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 debug # Restart containers with Xdebug enabled.

When it turns Xdebug on, ahoy debug prints Enabled debug configuration. Use 'ahoy up' to disable. If Xdebug is already on, it prints Debug configuration is already enabled. Use 'ahoy up' to disable. and changes nothing.

Lagoon images turn Xdebug on only when XDEBUG_ENABLE is true, in any letter case, so a value such as 1 leaves it off.

Xdebug connects back to port 9003, its default, on host.docker.internal. Where that name doesn't resolve, such as on Linux, it connects to the container's default gateway instead. Make sure your IDE listens on port 9003.

To turn it off, recreate the containers without the variable:

ahoy up # Restart containers with Xdebug disabled.

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 remove the line to turn Xdebug off.

ahoy info shows whether Xdebug is on at the moment.

CLI​

While Xdebug is on, a PHP script you run through a shell in the cli container, such as with ahoy cli, starts a debugging session with no browser extension. The Lagoon images export XDEBUG_TRIGGER in every shell session there. They also set PHP_IDE_CONFIG to name 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​

By default, PhpStorm stops on a debug connection it can't map to your project and prompts you to map it. It accepts connections only while listening is on, and the toolbar button with a little bug on it turns listening on.

PhpStorm needs the mapping because the code runs in the container, which counts as a remote server. The mapping matches a local directory to /app in the container. You set it once, and PhpStorm remembers it.

To use Xdebug on the project for the first time, starting from a fully working site, follow these steps:

  1. Install the Xdebug helper extension for your browser:

  2. In the extension, turn on debugging for the site, as its instructions describe. The extension sets the XDEBUG_SESSION cookie, which starts a debugging session for each request.

  3. To start listening for debug connections, click the toolbar button with the little bug in PhpStorm.

  4. Set a breakpoint in your IDE. index.php in your web root is a good place to start.

  5. To turn on Xdebug in the containers, run ahoy debug or XDEBUG_ENABLE=true docker compose up -d cli php nginx.

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

  7. In the prompt, map your project root to /app.

PhpStorm server settings with the path mapping to the project root within
the container

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:

Tips and tricks​

After your first Xdebug session works, a few changes to PhpStorm's Debug settings make everyday debugging smoother:

  • 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 5 or more, so a debug session can't get stuck without showing up in the IDE.

Testing authenticated pages with curl​

Use curl with session cookies to request pages that need a login, such as admin pages and protected routes, without opening a browser. It's a tool for ad-hoc debugging; automated tests belong in Behat or PHPUnit.

When to use​

The curl approach suits these cases:

  • Quick ad-hoc debugging of admin page errors (500 errors, permission issues)
  • Investigating issues without spinning up a browser
  • Verifying page content during troubleshooting
  • One-off checks that don't warrant a formal test

Step-by-step process​

The following commands sign in once and reuse the saved session cookie for later requests:

# 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)"

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:

FlagPurpose
-kIgnore SSL certificate errors (required for self-signed local certs)
-c FILESave cookies to FILE after request
-b FILESend cookies from FILE with request
-LFollow redirects (required for login flow)

Important notes​

The following points apply to the saved cookies and the login links:

  • Store cookies in the .data/ directory: Git ignores it, so a session cookie doesn't end up in a commit.
  • Cookies expire after the session timeout, so regenerate them as needed.
  • Login URLs have the format http://SITE/user/reset/UID/TIMESTAMP/HASH/login.
  • Login URLs from ahoy login can only be used once. ahoy login also unblocks user 1 before it generates the link, which a plain drush uli doesn't.

Container debugging​

To see what a container is doing, or to open a shell or a database prompt inside it:

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