Amazon Web Services Feed
The serverless LAMP stack part 4: Building a serverless Laravel application

In this post, you learn how to deploy a Laravel application with a serverless approach.

This is the fourth post in the “Serverless LAMP stack” series, previous posts covered:

Laravel is an open source web application framework for PHP. Using a framework helps developers to build faster by reusing generic components and modules. It also helps long-term maintenance by complying with development standards. However, there are still challenges when scaling PHP frameworks with a traditional LAMP stack. Deploying a framework using a serverless approach can help solve these challenges.

There are a number of solutions that simplify the deployment of a Laravel application onto a serverless infrastructure. The following solution uses an AWS Serverless Application Model (AWS SAM) template. This deploys a Laravel application into a single Lambda function. The function uses the Bref FPM custom runtime layer to run PHP. The AWS SAM template deploys the following architecture, explained in detail in “The Serverless LAMP stack Part 3: Replacing the web server”:

The serverless LAMP stack

Deploying Laravel and Bref with AWS SAM

Composer is a dependency management tool for PHP. It allows you to declare and manage your project libraries and dependencies such as Laravel and Bref.

Deploy Laravel and Bref with AWS SAM using the following steps:

  1. Download the Laravel installer using Composer:
    composer global require Laravel/installer
  2. Install Laravel:
    composer create-project --prefer-dist laravel/laravel blog
  3. In the Laravel project, install Bref using Composer:
    composer require bref/laravel-bridge
  4. Clone the AWS SAM template in your application’s root directory:
    git clone https://github.com/aws-samples/php-examples-for-aws-lambda/
  5. Change directory into “0.4-Building-A-Serverless-Laravel-App-With-AWS-SAM”:
    cd 0.4-Building-A-Serverless-Laravel-App-With-AWS-SAM
  6. Deploy the application using the AWS SAM CLI guided deploy:
    sam deploy -g

Once AWS SAM deploys the application, it returns the Amazon CloudFront distribution’s domain name. This distribution serves the serverless Laravel application.

CloudFront domain name

CloudFront domain name from AWS SAM template

Configuring Laravel for Lambda

There are some configuration changes required for Laravel to run in a Lambda function.

Session data store

While Lambda includes a 512 MB temporary file system, this is an ephemeral resource not intended for durable storage. This is because there is no guarantee of reusing the same Lambda function environment for each invocation.

For this reason, if you need Laravel session data, it must be stored outside of the Lambda function. There are a range of different options available for managing state with serverless applications. In this instance, it is recommended to store session data either in a database or using browser cookies.

Update the Laravel .env file to set the session_driver to cookie.

SESSION_DRIVER=cookie

Logging

Laravel implements a PHP logging library called Monolog as a common interface to write logs to a number of destinations. Laravel Monolog uses log channels to specify these destinations. Each channel is defined within the /config/logging.php file as an associative array.

Since the Lambda filesystem is not shared between multiple Lambda function invocations, application logs must be written to an external central location such as Amazon CloudWatch Logs. All errors, warnings, and notices emitted by PHP are forwarded onto CloudWatch Logs. This makes it easy to view, search, filter, or archive logs for future analysis from a single location. To configure this, add the following to the Laravel .env file:

LOG_CHANNEL=stderr

This ensures that the stderr channel is used to write all application logs which are automatically forwarded to CloudWatch Logs. This channel is defined in /config/logging.php:

'stderr' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'stream' => 'php://stderr', ], ],
CloudWatch Logs for a single Lambda invocation

CloudWatch Logs for a single Lambda invocation

Compiled views

Views contain the HTML served by an application, separating application logic from presentation logic. By default, views are compiled on demand inside the application’s storage directory.

As Lambda does not have write access to the storage directory, Laravel must be configured to write views to the function’s /tmp directory. This is a temporary file system for ephemeral data that’s only needed for the duration of each HTTP request.

In the .env file, add the following line to configure Laravel to use a new directory path for compiled views:

VIEW_COMPILED_PATH=/tmp/storage/framework/views

Laravel uses service providers to register or “bootstrap” components to your application. The AppServiceProvider.php file provides a central location to share data with all views. Add the following code to the Providers/AppServiceProvider.phpfile.

public function boot() { // Make sure the directory for compiled views exist if (! is_dir(config('view.compiled'))) { mkdir(config('view.compiled'), 0755, true); } }

This ensures that the view directory is automatically created for each Lambda function invocation, if it does not already exist.

File system abstraction with Amazon S3

Laravel uses a filesystem abstraction package called Flysystem. This provides a simple driver mechanism to configure the filesystem location. As Lambda’s /tmp directory is ephemeral, the filesystem location must be outside of the Lambda function. Configure Laravel to use the Amazon S3 filesystem driver by adding the following line to the .env file:

FILESYSTEM_DRIVER=s3

The AWS SAM template deploys an S3 bucket to store these objects:

Storage: Type: AWS::S3::Bucket Properties: BucketName: php-example-laravel-FileSystemBucket

The bucket name is provided to the Lambda function as an environment variable from within the AWS SAM template:

 Environment: Variables: AWS_BUCKET: !Ref Storage

The Lambda function is granted permission to read/write to the S3 bucket, using an IAM policy definition:

Policies: - S3FullAccessPolicy: BucketName: !Ref Storage

Laravel’s filesystem configuration is found at config/filesystems.php. This is where the S3 filesystem disk is defined using the AWS SAM environment variable.

's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'token' => env('AWS_SESSION_TOKEN'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), ],

The AWS account information and bucket ARN are provided by the Lambda environment that is running PHP, using Laravel’s env() function.

Public asset files

Laravel has a public disk driver for storing publicly accessible files such as images
and CSS files. By default, the public disk driver stores these files in storage/app/public/. These files must rather be stored in S3. Change the configuration in config/filesystems.php to the following:

+ 'public' => env('FILESYSTEM_DRIVER_PUBLIC', 'public_local'), 'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], - 'public => [
+ 'public_local' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL').'/storage', 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'token' => env('AWS_SESSION_TOKEN'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), ], + 's3_public' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'token' => env('AWS_SESSION_TOKEN'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_PUBLIC_BUCKET'), + 'url' => env('AWS_URL'), + ], ],

This adds a new filesystem disk named s3_public, which uses an S3 driver. Laravel’s env() function retrieves the environment variable env(‘AWS_PUBLIC_BUCKET’) to set/configure the bucket location. The bucket name is passed to the Lambda function as an environment variable.

Add the following line to the .env file to configure the public disk to use S3:

FILESYSTEM_DRIVER_PUBLIC=s3

Referencing static assets in view templates

Laravel’s asset() helper function generates a URL for an asset using the current scheme of the request (HTTP or HTTPS):

$url = asset('img/photo.jpg');

These assets must be stored on S3 and served via CloudFront’s global CDN. Configure the URL host by setting the ASSET_URL variable in your .env file:

ASSET_URL=https://{YourCloudFrontDomain}.cloudfront.net

This allows the application to correctly reference assets from S3, via the CloudFront domain. Laravel’s native asset() helper function is used from within the view templates with the following format:

<img src="{{ asset('assets/icons.png') }}">
Serverless Laravel App with Lambda

Serverless Laravel App with Lambda

Alternative deployments methods for a serverless Laravel application

1. Bref, an open source custom runtime for PHP, recently merged a new pull request to automatically configure Laravel for Lambda. This new package also provides a way to integrate Amazon SQS with the Laravel Queues Jobs system.

2. Laravel Vapor is a serverless deployment platform for Laravel. This is a paid service, built by the Laravel team on the AWS Cloud.

Conclusion

This post explains how to deploy a PHP Laravel application using a serverless approach with AWS SAM. It explains the initial Laravel configuration steps required to implement a session store and centralised logging with an external filesystem and static assets in S3.

PHP development teams can focus on shipping code without changing the way they build. Start building serverless applications with PHP.

Visit this GitHub repository for accompanying code and instructions.