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(usingleague/flysystem-aws-s3-v3andaws/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:GetObjectpolicy is in place and identical keys are used across working and failing environments. - Network/DNS:
pingandcurlfrom the container\’s shell to the S3 endpoint function correctly. - SSL Certificates:
ca-certificateswere added to the Docker image, andopenssl.cafile/openssl.capathwere verified. The PHPcurl_execalso 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) inconfig/filesystems.phpdid 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.