Running Laravel on AWS Lambda

Running Laravel on AWS Lambda

Learn how to create serverless Laravel applications by deploying them to AWS Lambda.

Prerequisites

Before deploying to AWS Lambda, you will need:

  • An AWS account

Install Node JS and serverless CLI

Download and import the Nodesource GPG key

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

Create deb repository

NODE_MAJOR=18
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list

Run Update and Install

sudo apt-get update
sudo apt-get install nodejs -y

Install the serverless CLI using NPM

npm install -g serverless

Update Package Lists

sudo apt-get update

Install PHP and Required Extensions


sudo apt install php libapache2-mod-php php-mbstring php-xmlrpc php-soap php-gd php-xml php-cli php-zip php-bcmath php-curl php-tokenizer php-json

Install Composer

Downloads and installs Composer globally. Create Laravel Project:

curl -sS https://getcomposer.org/installer | php 
sudo mv composer.phar /usr/local/bin/composer 
sudo chmod +x /usr/local/bin/composer

Create A sample Laraval Project

Uses Composer to create a new Laravel project named "serverless-laravel"

composer create-project laravel/laravel serverless-laravel

cd to serverless-laravel

install Bref and its Laravel integration

composer require bref/bref bref/laravel-bridge --update-with-dependencies

create a serverless.yml configuration file

php artisan vendor:publish --tag=serverless-config

connect the serverless to your AWS account

connect the serverless CLI to your AWS account via AWS access and secret key keys

Create AWS access and secret keys by following bref.sh documentation https://bref.sh/docs/setup/aws-keys,

Adding keys

serverless config credentials --provider aws --key <key> --secret <secret>

Deploy

serverless deploy

Using RDS as the database

Edit the .env configuration file to set up the database connection.Also, Make sure Lambda has access to this Database;

DB_CONNECTION=mysql
DB_HOST=<host url>
DB_PORT=3306
DB_DATABASE=<database_name>
DB_USERNAME=<user>
DB_PASSWORD=<password>

Deploying for production

composer install --prefer-dist --optimize-autoloader --no-dev

Stages

Serverless Framework has a concept of stages (environments). We can deploy the same application multiple times in completely separated environments

serverless deploy --stage=prod

Automating deployments

If you are using CodePipeline you can use this in AWS CodeBuild as buildspec.yml

version: 0.2
phases:
  install:
    runtime-versions:
      nodejs: 18
      php: 8.2

    commands:
      - npm install -g serverless
      - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
      - composer install --prefer-dist --optimize-autoloader --no-dev

  build:
    commands:
      - serverless deploy --stage=prod

Note: Make sure the AWS code build has permission (Role) attached to it so the code build is allowed to deploy.

Modify the IAM Role of CodeBuild and add this Policy, this is a Minimal AWS policy that allows to deployment of a serverless application using the Serverless Framework.

{
    "Statement": [
        {
            "Action": [
                "apigateway:*",
                "cloudformation:CancelUpdateStack",
                "cloudformation:ContinueUpdateRollback",
                "cloudformation:CreateChangeSet",
                "cloudformation:CreateStack",
                "cloudformation:CreateUploadBucket",
                "cloudformation:DeleteStack",
                "cloudformation:DeleteChangeSet",
                "cloudformation:Describe*",
                "cloudformation:EstimateTemplateCost",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:Get*",
                "cloudformation:List*",
                "cloudformation:UpdateStack",
                "cloudformation:UpdateTerminationProtection",
                "cloudformation:ValidateTemplate",
                "dynamodb:CreateTable",
                "dynamodb:DeleteTable",
                "dynamodb:DescribeTable",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:UpdateTimeToLive",
                "ec2:AttachInternetGateway",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateInternetGateway",
                "ec2:CreateNetworkAcl",
                "ec2:CreateNetworkAclEntry",
                "ec2:CreateRouteTable",
                "ec2:CreateSecurityGroup",
                "ec2:CreateSubnet",
                "ec2:CreateTags",
                "ec2:CreateVpc",
                "ec2:DeleteInternetGateway",
                "ec2:DeleteNetworkAcl",
                "ec2:DeleteNetworkAclEntry",
                "ec2:DeleteRouteTable",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteSubnet",
                "ec2:DeleteVpc",
                "ec2:Describe*",
                "ec2:DetachInternetGateway",
                "ec2:ModifyVpcAttribute",
                "events:DeleteRule",
                "events:DescribeRule",
                "events:ListRuleNamesByTarget",
                "events:ListRules",
                "events:ListTargetsByRule",
                "events:PutRule",
                "events:PutTargets",
                "events:RemoveTargets",
                "iam:AttachRolePolicy",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:DeleteRolePolicy",
                "iam:DetachRolePolicy",
                "iam:GetRole",
                "iam:PassRole",
                "iam:PutRolePolicy",
                "iot:CreateTopicRule",
                "iot:DeleteTopicRule",
                "iot:DisableTopicRule",
                "iot:EnableTopicRule",
                "iot:ReplaceTopicRule",
                "kinesis:CreateStream",
                "kinesis:DeleteStream",
                "kinesis:DescribeStream",
                "lambda:*",
                "logs:CreateLogGroup",
                "logs:DeleteLogGroup",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:FilterLogEvents",
                "logs:GetLogEvents",
                "logs:PutSubscriptionFilter",
                "logs:TagResource",
                "logs:DeleteSubscriptionFilter",
                "s3:CreateBucket",
                "s3:DeleteBucket",
                "s3:DeleteBucketPolicy",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "s3:PutBucketNotification",
                "s3:PutBucketPolicy",
                "s3:PutBucketTagging",
                "s3:PutBucketWebsite",
                "s3:PutEncryptionConfiguration",
                "s3:PutObject",
                "sns:CreateTopic",
                "sns:DeleteTopic",
                "sns:GetSubscriptionAttributes",
                "sns:GetTopicAttributes",
                "sns:ListSubscriptions",
                "sns:ListSubscriptionsByTopic",
                "sns:ListTopics",
                "sns:SetSubscriptionAttributes",
                "sns:SetTopicAttributes",
                "sns:Subscribe",
                "sns:Unsubscribe",
                "states:CreateStateMachine",
                "states:DeleteStateMachine",
                "states:TagResource"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ],
    "Version": "2012-10-17"
}

Did you find this article valuable?

Support Muhammad Usama by becoming a sponsor. Any amount is appreciated!