Symfony on a lambda: first deployment
Tags: php, symfony, lambda, serverless
Published: 2019-11-08 07:27:54

---
title: Symfony on a lambda: first deployment
published: true
description: Deploy easely Symfony application on a AWS Lambda
tags: PHP, Symfony, Lambda, Serverless
series: Symfony on a lambda
canonical_url: https://julesmatsounga.com/en/article/symfony-lambda-chapter-1
---
# Symfony on a lambda: first deployment

***English isn't my first language, help me improve my posts by pointing my mistakes***

We can run serveless on many platforms, and same goes for cloud functions. As Bref only support AWS, we will focus on this platform.

As mentioned, we will use [Bref](https://bref.sh/). It help run PHP on a AWS Lambda.

We will use it with Symfony, a popular PHP framework.

If you are lost, you can find the project on Github: [link](https://github.com/hyoa/symfony-on-lambda)
Each branch will match a chapter.

### Requirements

* Have the requirements to run Symfony on your computer (PHP and some extensions)

* Have an AWS account(you can create one here: [registration](https://portal.aws.amazon.com/billing/signup?nc2=h_ct&src=default&redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start)). We will stay below free tier limit, so no worries for your wallet.

* Create an access key:

  * Create a new user [here](https://console.aws.amazon.com/iam/home?#/users$new?step=details)

  * Add a user name

  * Enable ` Programmatic access`

  * Click **Attach existing policies directly**, search for **AdministratorAccess **and select it.

    Warning: it is recommended to only select right that you really need. But too keep this presentation simple, we will use a full access key.

  * Finish creating the user

  * Take note of keys generated, we will need them after

* Install serverless: `npm install -g serverless`

* Create the configuration of serverless: `serverless config credentials --provider aws --key <key> --secret <secret>` where key and secret are from the keys generated earlier

### Symfony

We now have everything we need to start our development. Let's go !

#### Installation of Symfony

In your terminal, type the following command at the root of your projects `composer create-project symfony/website-skeleton [my_project_name]` (remember to replace [my_project_name] with yours)

Once the installation done, you can go in the folder where is your Symfony application.

We can now install Bref, which is required to deploy PHP on a lambda. To do so, once in the project, type `composer require bref/bref`

### Creation of the serverless.yml

This file will define the architecture that we will deploy on AWS, that will be called **CloudFormation**. It's in this file that we will define services and resources and their configurations that we want on AWS.

To create this file, Bref give us a command that will help init a project. Type `vendor/bin/bref init` et select `HTTP application`.

The command create our **serverless.yml** file and an **index.php** file at the root of our project. You can delete **index.php** we wont use it.

The **serverless.yml** should look like this:

```yaml
service: app #Name of your application

provider:
    name: aws #Provider used by Serverless
    region: us-east-1 #Region where you will deploy your CloudFormation
    runtime: provided

plugins:
    - ./vendor/bref/bref

functions:
    api: #Name of the function
        handler: index.php
        description: ''
        timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
        layers:
            - ${bref:layer.php-73-fpm}
        events:
            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'
```

The most interesting part is **functions** where we will be able to define our functions that we need (it can be commands, apis, crons, etc...).

* handler: the php file used by the lambda
* description: well, a description ?
* timeout: maximum execution time of the function
* layers: Layers are environment used by the lambda to run, you can have multiple layers to add other extensions, dependencies etc. Here, we use the layer of Bref, that run PHP with the layer of Node ([more on layers](https://docs.aws.amazon.com/fr_fr/lambda/latest/dg/configuration-layers.html))
* events: events that will trigger the function. On this function, the HTTP event will trigger the lambda, but there is a lot of other events triggered by AWS that we can listen. We will see an other one later

Now that we have a better understanding of our file, lets changed it to run Symfony.

```yaml
service: cloud-project

provider:
    name: aws
    region: eu-west-2
    runtime: provided
    stage: dev
    environment:
        APP_ENV: prod
plugins:
    - ./vendor/bref/bref

functions:
    website:
        handler: public/index.php
        timeout: 28
        layers:
            - ${bref:layer.php-73-fpm}
        events:
            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'

    console:
        handler: bin/console
        timeout: 120
        layers:
            - ${bref:layer.php-73}
            - ${bref:layer.console}
```

Not too many change. I changed the name of the application in `cloud-project` (you can put whatever you want). I also add a stage that define the environment publish.
In **environment** we have the environment variables used by our functions.
I also create 2 functions:

* website that is the same than the api function we had in the file. We only change the handler that now target the **index.php** of Symfony.
* console used to run Symfony command

#### Symfony configuration

We will have to change some files in Symfony to make it work on a lambda.

System file is **readonly** except **/tmp**, we have to change where are stored cache and logs.
In **src/Kernel.php**, we need to add 2 methods:

```php
// src/Kernel.php
...
  
public function getLogDir(): string
{
  if (getenv('LAMBDA_TASK_ROOT') !== false) {
    return '/tmp/log/';
  }

  return parent::getLogDir();
}

public function getCacheDir(): string
{
  if (getenv('LAMBDA_TASK_ROOT') !== false) {
    return '/tmp/cache/'.$this->environment;
  }

  return parent::getCacheDir();
}
```

We also need to change **index.php**.

Once deployed, the lambda that use API Gateway have a domain that is created and that end with th stage deployed (ex: https://lamnda/dev). It can create some issue with PHP framework. You can easily solve this by creating a custom domain that get ride of this suffix  (more information](https://bref.sh/docs/environment/custom-domains.html)). But to keep this presentation accessible, we won't do it. 
We have to change some servers variables so the Symfony routing wont break.

```php
// public/index.php

$_SERVER['SCRIPT_NAME'] = '/dev/index.php';
if (strpos($_SERVER['REQUEST_URI'], '/dev') === false) {
    $_SERVER['REQUEST_URI'] = '/dev'.$_SERVER['REQUEST_URI'];
}
```

You need to put this line before `new Kernel(...)`. `/dev` is the stage defined in **serverless.yml**. If you create your own application, you should really create your own domain. It can be a bit long (propagation of DNS) but otherwise it's quite simple.

We don't need anything else. Let's code a bit.

#### Building a homepage

We will create a simple landing page si we have something to deploy. But we will keep it simple:

Create a file **HomeController** in **src/Controller**

```php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class HomeController extends AbstractController
{
    /**
     * @Route("/", name="home")
     */
    public function homeAction(): Response
    {
        return $this->render('home/index.html.twig');
    }
}
```

Create a file **index.html.twig** in **templates/home**

```twig
{% extends 'base.html.twig' %}

{% block body %}
    <h1>Symfony and lambdas</h1>
{% endblock %}
```

Yes, it's really simple but we don't really need anything else.

To see if it's working, type: `php bin/console server:run` and go to the url displayed. You should see your page.

Now, let's deploy it !

#### Deploy in the cloud

It might disappoint you, but you just need to run `serverless deploy`. The command will package your project, create the required resources on AWS. After few minutes, the endpoints of your function will be displayed. Use the first one, and you should see your site deployed !

You can see your **CloudFormation** on AWS [here](https://eu-west-2.console.aws.amazon.com/cloudformation/home?region=eu-west-2#/stacks?filteringText=&filteringStatus=active&viewNested=true&hideStacks=false). You can also see the resources created by selecting **Model** then **Display in Designer**.

***

We now have a Symfony application running on AWS. Of course we are only at the beginning, but we will discover more in the next chapters.

In the next chapter we will talk about assets. Because without assets, there is no CSS or JS !