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.

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 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, 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 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 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:

  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. In PhpStorm, click the toolbar button with the little bug to start listening for debug connections.

  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

  8. 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, 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 5 or 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:

# 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
-kIgnores SSL certificate errors. Required for self-signed local certificates.
-c FILESaves cookies to FILE after the request.
-b FILESends cookies from FILE with the request.
-LFollows redirects. Required for the login flow.

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

  • Store cookies in the .data/ directory: the shipped .gitignore excludes 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_ADMIN is 0, ahoy login also unblocks user 1 before it generates the link, which a plain drush uli doesn'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.