How to Use IMAP Extension with PHP 8.4 on AWS Lambda using Bref
If you've ever tried to use the IMAP extension with PHP 8.4 on AWS Lambda using Bref, you've probably hit a wall. The IMAP extension isn't available as a pre-built layer for PHP 8.4, and compiling PHP extensions for Lambda's Amazon Linux 2 environment can be tricky.
In this article, I'll show you how I solved this problem using Docker multi-stage builds to compile the IMAP extension and deploy it with Bref.
Table of Contents
- The Problem
- The Solution: Docker Multi-Stage Builds
- Understanding the Dockerfile
- Complete Dockerfiles
- Serverless Configuration
- How It All Works Together
- Conclusion
The Problem
Bref provides excellent PHP runtimes for AWS Lambda, including many common extensions through bref/extra-php-extensions. However, the IMAP extension for PHP 8.4 isn't available as a pre-built layer.
Why? The IMAP extension has several system-level dependencies:
-
c-clientlibrary (uw-imap) - Kerberos libraries
- OpenSSL development files
These dependencies need to be compiled specifically for Amazon Linux 2, which is Lambda's underlying operating system.
The Solution: Docker Multi-Stage Builds
Instead of using Lambda layers, we'll use Docker container images for our Lambda functions. This approach gives us full control over the environment and allows us to:
- Compile the IMAP extension in a build stage using Bref's build image
- Copy the compiled extension to the final runtime image
- Deploy as container images to AWS ECR
The key insight is that Bref provides bref/build-php-84 - a Docker image specifically designed for compiling PHP extensions that will work on Lambda.
Understanding the Dockerfile
Let's break down what each part of the Dockerfile does:
Stage 1: Build the IMAP Extension
FROM bref/build-php-84:2 AS ext
This uses Bref's build image, which contains all the tools needed to compile PHP extensions for Lambda's environment.
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install amazon-linux-extras
RUN LD_LIBRARY_PATH=/lib:/lib64 amazon-linux-extras install epel -y
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install uw-imap-devel krb5-devel openssl-devel libc-client-devel
Here we install the IMAP dependencies. The LD_LIBRARY_PATH is necessary because of how libraries are organized in the Bref build image.
RUN pecl download imap-1.0.3 && tar -xzf imap-1.0.3.tgz
We download the IMAP extension from PECL. Note that we use version 1.0.3 which is compatible with PHP 8.4.
RUN LD_LIBRARY_PATH=/lib64:/lib phpize \
&& LD_LIBRARY_PATH=/lib64:/lib ./configure \
--with-imap \
--with-kerberos \
--with-imap-ssl=/usr \
--with-libdir=lib64 \
CFLAGS="-I/usr/include" \
LDFLAGS="-L/lib64" \
&& LD_LIBRARY_PATH=/lib64:/lib make -j$(nproc) \
&& make install
This compiles the extension with:
- IMAP support
- Kerberos authentication support
- SSL/TLS support for secure IMAP connections
RUN cp $(php-config --extension-dir)/imap.so /tmp/imap.so \
&& echo 'extension=imap.so' > /tmp/ext-imap.ini \
&& mkdir -p /tmp/extension-libs \
&& php /bref/lib-copy/copy-dependencies.php /tmp/imap.so /tmp/extension-libs || true \
&& rm -f /tmp/extension-libs/libssl.so.3 /tmp/extension-libs/libcrypto.so.3 \
&& rm -f /tmp/extension-libs/libz.so.1
This is crucial! We:
- Copy the compiled
imap.soextension - Create a PHP ini file to load it
- Use Bref's
copy-dependencies.phpto find and copy all required shared libraries - Remove libraries that are already present in the runtime (to avoid conflicts)
Stage 2: The Runtime Image
The second stage uses the appropriate Bref runtime image and copies our compiled extension.
Complete Dockerfiles
Here are the complete Dockerfiles for different Lambda function types:
Dockerfile.web (for HTTP requests via API Gateway)
FROM bref/build-php-84:2 AS ext
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install amazon-linux-extras
RUN LD_LIBRARY_PATH=/lib:/lib64 amazon-linux-extras install epel -y
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install uw-imap-devel krb5-devel openssl-devel libc-client-devel
WORKDIR /tmp
RUN pecl download imap-1.0.3 && tar -xzf imap-1.0.3.tgz
WORKDIR /tmp/imap-1.0.3
RUN LD_LIBRARY_PATH=/lib64:/lib phpize \
&& LD_LIBRARY_PATH=/lib64:/lib ./configure \
--with-imap \
--with-kerberos \
--with-imap-ssl=/usr \
--with-libdir=lib64 \
CFLAGS="-I/usr/include" \
LDFLAGS="-L/lib64" \
&& LD_LIBRARY_PATH=/lib64:/lib make -j$(nproc) \
&& make install
RUN cp $(php-config --extension-dir)/imap.so /tmp/imap.so \
&& echo 'extension=imap.so' > /tmp/ext-imap.ini \
&& mkdir -p /tmp/extension-libs \
&& php /bref/lib-copy/copy-dependencies.php /tmp/imap.so /tmp/extension-libs || true \
&& rm -f /tmp/extension-libs/libssl.so.3 /tmp/extension-libs/libcrypto.so.3 \
&& rm -f /tmp/extension-libs/libz.so.1
FROM bref/php-84-fpm:2
# Copy the IMAP extension and its dependencies
COPY --from=ext /tmp/imap.so /opt/bref/extensions/imap.so
COPY --from=ext /tmp/ext-imap.ini /opt/bref/etc/php/conf.d/ext-imap.ini
COPY --from=ext /tmp/extension-libs/ /opt/lib/
# Add other extensions from bref/extra-php-extensions if needed
COPY --from=bref/extra-redis-php-84:1 /opt /opt/
COPY --from=bref/extra-ssh2-php-84:1 /opt /opt/
# Copy your application code
COPY . /var/task
CMD ["public/index.php"]
Dockerfile.cli (for Artisan commands and scheduled tasks)
FROM bref/build-php-84:2 AS ext
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install amazon-linux-extras
RUN LD_LIBRARY_PATH=/lib:/lib64 amazon-linux-extras install epel -y
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install uw-imap-devel krb5-devel openssl-devel libc-client-devel
WORKDIR /tmp
RUN pecl download imap-1.0.3 && tar -xzf imap-1.0.3.tgz
WORKDIR /tmp/imap-1.0.3
RUN LD_LIBRARY_PATH=/lib64:/lib phpize \
&& LD_LIBRARY_PATH=/lib64:/lib ./configure \
--with-imap \
--with-kerberos \
--with-imap-ssl=/usr \
--with-libdir=lib64 \
CFLAGS="-I/usr/include" \
LDFLAGS="-L/lib64" \
&& LD_LIBRARY_PATH=/lib64:/lib make -j$(nproc) \
&& make install
RUN cp $(php-config --extension-dir)/imap.so /tmp/imap.so \
&& echo 'extension=imap.so' > /tmp/ext-imap.ini \
&& mkdir -p /tmp/extension-libs \
&& php /bref/lib-copy/copy-dependencies.php /tmp/imap.so /tmp/extension-libs || true \
&& rm -f /tmp/extension-libs/libssl.so.3 /tmp/extension-libs/libcrypto.so.3 \
&& rm -f /tmp/extension-libs/libz.so.1
FROM bref/php-84-console:2
# Copy the IMAP extension and its dependencies
COPY --from=ext /tmp/imap.so /opt/bref/extensions/imap.so
COPY --from=ext /tmp/ext-imap.ini /opt/bref/etc/php/conf.d/ext-imap.ini
COPY --from=ext /tmp/extension-libs/ /opt/lib/
# Add other extensions from bref/extra-php-extensions if needed
COPY --from=bref/extra-redis-php-84:1 /opt /opt/
COPY --from=bref/extra-ssh2-php-84:1 /opt /opt/
# Copy your application code
COPY . /var/task
CMD ["artisan"]
Dockerfile.queue (for SQS queue workers)
FROM bref/build-php-84:2 AS ext
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install amazon-linux-extras
RUN LD_LIBRARY_PATH=/lib:/lib64 amazon-linux-extras install epel -y
RUN LD_LIBRARY_PATH=/lib:/lib64 yum -y install uw-imap-devel krb5-devel openssl-devel libc-client-devel
WORKDIR /tmp
RUN pecl download imap-1.0.3 && tar -xzf imap-1.0.3.tgz
WORKDIR /tmp/imap-1.0.3
RUN LD_LIBRARY_PATH=/lib64:/lib phpize \
&& LD_LIBRARY_PATH=/lib64:/lib ./configure \
--with-imap \
--with-kerberos \
--with-imap-ssl=/usr \
--with-libdir=lib64 \
CFLAGS="-I/usr/include" \
LDFLAGS="-L/lib64" \
&& LD_LIBRARY_PATH=/lib64:/lib make -j$(nproc) \
&& make install
RUN cp $(php-config --extension-dir)/imap.so /tmp/imap.so \
&& echo 'extension=imap.so' > /tmp/ext-imap.ini \
&& mkdir -p /tmp/extension-libs \
&& php /bref/lib-copy/copy-dependencies.php /tmp/imap.so /tmp/extension-libs || true \
&& rm -f /tmp/extension-libs/libssl.so.3 /tmp/extension-libs/libcrypto.so.3 \
&& rm -f /tmp/extension-libs/libz.so.1
FROM bref/php-84:2
# Copy the IMAP extension and its dependencies
COPY --from=ext /tmp/imap.so /opt/bref/extensions/imap.so
COPY --from=ext /tmp/ext-imap.ini /opt/bref/etc/php/conf.d/ext-imap.ini
COPY --from=ext /tmp/extension-libs/ /opt/lib/
# Add other extensions from bref/extra-php-extensions if needed
COPY --from=bref/extra-redis-php-84:1 /opt /opt/
COPY --from=bref/extra-ssh2-php-84:1 /opt /opt/
# Copy your application code
COPY . /var/task
CMD ["Bref\\LaravelBridge\\Queue\\QueueHandler"]
Key differences between the Dockerfiles:
| Dockerfile | Base Runtime Image | CMD | Use Case |
|---|---|---|---|
.web |
bref/php-84-fpm:2 |
public/index.php |
HTTP requests via API Gateway |
.cli |
bref/php-84-console:2 |
artisan |
CLI commands, scheduled tasks |
.queue |
bref/php-84:2 |
QueueHandler |
SQS queue processing |
Serverless Configuration
Here's how to configure serverless.yml to use these Docker images with AWS ECR:
service: my-laravel-app
provider:
name: aws
region: us-east-1
stage: ${opt:stage, 'dev'}
runtime: provided.al2
# Optional: Configure API Gateway
httpApi:
id: ${ssm:/my-app/apigw_id} # Or create a new one
# Environment variables for your Lambda functions
environment:
APP_ENV: ${self:provider.stage}
# Add your other environment variables here
# VPC configuration (if needed)
vpc:
securityGroupIds:
- sg-xxxxxxxxx
subnetIds:
- subnet-xxxxxxxx
- subnet-yyyyyyyy
# S3 bucket for Serverless deployments
deploymentBucket:
name: my-serverless-deployments
# ECR image definitions - this is the key part!
ecr:
images:
app-web:
path: ./
file: Dockerfile.web
app-cli:
path: ./
file: Dockerfile.cli
app-queue:
path: ./
file: Dockerfile.queue
# Exclude unnecessary files from the Docker build context
package:
patterns:
- '!node_modules/**'
- '!public/storage'
- '!storage/**'
- '!tests/**'
- '!.git/**'
- '!vendor/**/test/**'
- '!vendor/**/tests/**'
- '!vendor/**/docs/**'
functions:
# Web function - handles HTTP requests
web:
name: ${self:provider.stage}-${self:service}-web
image:
name: app-web
timeout: 28
events:
- httpApi: '*'
# Artisan function - for CLI commands and scheduled tasks
artisan:
name: ${self:provider.stage}-${self:service}-artisan
image:
name: app-cli
timeout: 120
events:
# Run Laravel scheduler every minute
- schedule:
rate: rate(1 minute)
input: '"schedule:run"'
# Queue worker - processes SQS messages
queue:
name: ${self:provider.stage}-${self:service}-queue
image:
name: app-queue
timeout: 60
events:
- sqs:
arn: arn:aws:sqs:us-east-1:123456789:my-queue
plugins:
- ./vendor/bref/bref
- ./vendor/bref/extra-php-extensions
Important Configuration Notes
ECR Images Section: The
provider.ecr.imagessection tells Serverless to build Docker images and push them to ECR automatically during deployment.Function Image Reference: Each function uses
image.nameto reference the ECR image defined above, instead of usinghandlerandlayers.Package Patterns: Since we're building Docker images, the package patterns help reduce the Docker build context size, making builds faster.
How It All Works Together
When you run serverless deploy:
- Serverless builds the Docker images using the specified Dockerfiles
- Images are pushed to AWS ECR (Elastic Container Registry) in your account
- Lambda functions are created/updated to use these container images
- API Gateway, SQS triggers, and schedules are configured
The multi-stage build ensures that:
- The build dependencies (compilers, dev packages) stay in the build stage
- Only the compiled extension and runtime dependencies go to the final image
- The final image is as small and efficient as possible
Testing Locally
You can test your Docker images locally before deploying:
# Build the CLI image
docker build -f Dockerfile.cli -t my-app-cli .
# Test that IMAP is loaded
docker run --rm my-app-cli php -m | grep imap
# Run an artisan command
docker run --rm my-app-cli php artisan list
Conclusion
Using Docker multi-stage builds with Bref is a powerful solution when you need PHP extensions that aren't available as pre-built layers. This approach:
- Works with any PHP extension - not just IMAP
- Keeps your images small - build dependencies don't ship to production
- Is reproducible - the same Dockerfile builds the same image every time
- Integrates seamlessly with Serverless Framework and AWS ECR
The key is using Bref's bref/build-php-* images for compilation, which ensures compatibility with Lambda's Amazon Linux 2 environment.
If you found this helpful, feel free to reach out with questions or share your own experiences with PHP on Lambda!
Resources
- Bref Documentation
- Bref Docker Images
- bref/extra-php-extensions
- Serverless Framework - ECR Images
- PECL IMAP Extension
Connect with me: LinkedIn
Top comments (0)