Addressing S3 fileExists Issues in Laravel with Docker (php:8.2-fpm-alpine)

Developers often rely on Storage::fileExists() for efficient checks on Amazon S3. However, a specific and puzzling issue has been observed where this function consistently fails when a Laravel application runs within a php:8.2-fpm-alpine Docker container, throwing League\Flysystem\UnableToCheckFileExistence. The underlying problem points to an Error parsing response for HeadObject: AWS parsing error: Error parsing XML: String could not be parsed as XML.

The Heart of the Problem

While the same Laravel codebase operates flawlessly with identical AWS credentials on Laravel Valet (macOS), and crucially, other S3 operations like Storage::put(), readStream(), download(), and get() work perfectly inside the Docker container, fileExists() remains problematic. Even a direct PHP curl_exec command within the container making a HEAD request to the same S3 file succeeds with an HTTP 200 OK. This indicates the issue is highly specific to the HeadObject request initiated by the AWS SDK/Guzzle stack within the Alpine-based PHP-FPM environment.

Environment Details

  • Laravel: Latest stable version
  • Docker Image: php:8.2-fpm-alpine
  • Filesystem Driver: s3 (using league/flysystem-aws-s3-v3 and aws/aws-sdk-php)
  • AWS Region: me-central-1
  • AWS_USE_PATH_STYLE_ENDPOINT: false

Extensive Debugging Efforts (and What Was Ruled Out)

A thorough debugging process eliminated many common culprits:

  • IAM Permissions: Confirmed s3:GetObject policy is in place and identical keys are used across working and failing environments.
  • Network/DNS: ping and curl from the container\’s shell to the S3 endpoint function correctly.
  • SSL Certificates: ca-certificates were added to the Docker image, and openssl.cafile/openssl.capath were verified. The PHP curl_exec also succeeded with SSL verification.
  • Clock Skew: Container time is accurate.
  • Interfering Processes: Laravel Nightwatch was disabled.
  • Guzzle Configuration: Attempts to force HTTP/1.1, modify stream options, and disable SSL verification (verify => false) in config/filesystems.php did not resolve the XML parsing error.

The Core Question

Given that other S3 operations work, direct HEAD requests via curl_exec succeed, and the entire setup works outside Docker, why does the HeadObject request, specifically when handled by the AWS SDK/Guzzle within PHP-FPM on Alpine, fail with an XML parsing error? Is there a known bug or a subtle configuration conflict unique to this combination that targets only HEAD requests?

Currently, a workaround involves using readStream() within a try...catch block instead of fileExists(), but a root cause solution is highly preferred. Any insights or similar experiences with HeadObject failures on Alpine would be greatly appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.
You need to agree with the terms to proceed