Laravel on AWS Elastic Beanstalk – Dev Guide

Laravel on AWS Elastic Beanstalk

How to use this guide

This guide will walk you through setting up a Laravel development environment on Elastic Beanstalk.

Before using Elastic Beanstalk, I was using a shared hosting account, and I got fed up with outdated packages and the lack of admin privileges. My goal with this guide was to create a dev server that closely mirrored my intended production environment in Elastic Beanstalk (another post on that coming soon). This is the exact setup I use for SimpliFit’s API dev server.

What is Elastic Beanstalk?

First, a quick overview. Amazon Web Services’ Elastic Beanstalk is a Platform as a Service (PaaS), allowing developers to deploy applications without the hassle of detailed server infrastructure, such as server provisioning or scaling to meet demand.

Elastic Beanstalk uses the following AWS products:

  • Elastic Compute Cloud (EC2)
  • Simple Storage Service (S3)
  • Simple Notification Service (SNS)
  • CloudWatch

Elastic Beanstalk can also manage an AWS Relational Database Service (RDS) instance, however, for a Laravel application, this is not a preferred solution. When an RDS instance is created by and associated with an Elastic Beanstalk environment, it will also be terminated (and thus all data lost) when that environment is terminated. This will be further discussed in a later section.

Prerequisites

Prior to continuing with this guide, ensure the following prerequisites are met:

Create an RDS instance

For our Laravel application, we will be using a MySQL database, so we must first create a MySQL RDS instance in the RDS Management Console.

1. Select Engine

Select the mysql engine.

Select RDS Engine

2. AZ Deployment

Select, as required by your application, whether you want to use Multi-AZ Deployment for this database.

Since we’ll be using this Elastic Beanstalk application for development only (and we’d like to stay within the free tier usage), we’ll select No here. A post in a few weeks will cover this process for a production environment.

Mutli-AZ Deployment

3. Specify DB Details

Now you get to set the instance specifications and settings for your DB. For a development environment, we will use the following settings:

Specify DB Details

To determine the right settings for your Laravel application, it’s best to test various environments to see which fits your needs. As long as you only have only one RDS instance running at a time, you’ll fall within the free usage tier.

4. Advanced Settings

Unless you have created a separate VPC for your application, select the default VPC and default DB Subnet Group. Set this DB to be publicly accessible and select an Availability Zone. This is the AZ that you will want to also launch your Elastic Beanstalk EC2 instances in.

Finally, give your database a name and click Launch DB Instance. Launching the instance may take some time, but luckily we can complete the next steps during this time.

Configure Advanced Settings

Generate Security Credentials

Note: If you already have an Access Key ID and Secret Access Key for your account, skip this section.

In the Identity and Access Management (IAM) console, you will need to create new security credentials for your account.

Note: You need to have access rights to the IAM console. If you are not the owner/admin of the AWS account, you will need to request that security credentials are created for your account.

Navigate to the “Users” section in the left-hand menu. Select your username and then select the “Security Credentials” accordion. Click on “Manage Access Keys”.

In the lower right-hand corner, click “Create Access Key”.

Click “Show User Security Credentials” to view the credentials you just created. Keep this tab open, as this data will be needed in a later step. Alternatively, you can download your credentials as a .csv file to store until needed.

Create Key Pair

To give us the ability to SSH into the EC2 instances that are created as part of an Elastic Beanstalk environment, if the need arrises, we first need to create a key pair in the EC2 Management Console.

Select “Key Pairs” under “Network & Security” in the left-hand menu, and click “Create Key Pair”.

Create Key Pair

Give your Key Pair a name to describe it and click “Create”.

Your Key Pair is downloaded as a .pem file. You’ll need this file if you need to SSH into your EC2 instances, so don’t lose it!

Install the Eb CLI

The simplest method to interact with Elastic Beanstalk may be the GUI interface in the AWS Management Console, however the eb command line interface (CLI) offers many of the same features to deploy applications quickly and easily from your computer, especially when using a git workflow.

Download the eb tool and extract to a folder of your choosing on your computer. Add the following path to your PATH variable:

  • For Windows: <path to unzipped EB CLI package>/eb/windows
  • For Linux/Unix: <path to unzipped EB CLI package>/eb/linux/python2.7/

Initialize Elastic Beanstalk Application

Open command prompt/terminal and navigate to the root directory of your Laravel installation (and git repo).

Type the following command:

eb init

You will now be prompted to enter your AWS Access Key ID and AWS Secret Key, which were generated in a previous step. If you’ve already run eb init on this computer, your previous keys will already be pre-populated; just hit enter to select them.

Next you will choose the service region for you Elastic Beanstalk application. Choose the same region in which you created your RDS instance above. In this case, our RDS instance was created in “1) US East (Virginia)”.

Choose service region

Now give your application a name. The default value is the directory name. Then give your environment a name (e.g., development).

For your environment tier, select “1) WebServer::Standard::1.0”, as we are setting up a webserver.

Next, we get to select our solution stack. As of this writing, the latest version of the Amazon Linux AMI with PHP is v1.0.4 and runs PHP 5.5. We will be choosing that stack (#1).

Select solution stack

Since this will be a development application, we will be choosing a “SingleInstance” environment. This also ensure we’ll stay within the free usage tier for EC2, as long as we only have one running environment at a time. We created an RDS DB Instance separately, so we’ll answer “n” for no to the next question.

Next we need to attach an instance profile. This gives the EC2 instances that are created security permissions to access other AWS services (such as S3 for storing logs and and application versions). If you’ve created a profile already, choose that, otherwise select “1) [Create a default instance profile]”.

Choose instance profile

After a few seconds of waiting, you’re done!

So what just happened?

Congratulations, you created a new Elastic Beanstalk application, associated it with a git repo, and set some initial options for each environment that’s created. This includes which region to launch EC2 instances in, how many instances should be created, and what should be installed on those instances.

If you head over to the Elastic Beanstalk management console, you’ll see your application listed under “All Applications” (with no environments created, yet).

And if you navigate to your project directory in your code editor, you’ll see a new directory, “.elasticbeanstalk”. At the moment, this contains a config file with all the application preferences we just set.

You may also notice that your .gitignore file has changed. I wonder why… let’s take a look:

gitignore

Look at that! The new eb directory has already been added to our .gitignore file. Awesome! The line breaks just need a bit of cleaning up and it’s good to go.

Modifying Application Options

Normally, once eb start (this command starts an environment within your EB application and is covered in a later section) is run, a new file is created in the .elasticbeanstalk directory: optionsettings.app-environment-name. Since we want to set some of these options BEFORE an environment is started, we’ll create the file ourselves. This file contains all the options for Elastic Beanstalk, including options for creating new EC2 instances (and RDS instances). Refer to AWS Elastic Beanstalk Developer Guide – Option Values page for descriptions of each part of the code below. The code below is for an application that does not include an RDS instance (i.e., the RDS instance is created manually).

[aws:autoscaling:asg]
Custom Availability Zones=us-east-1a
MaxSize=1
MinSize=1

[aws:autoscaling:launchconfiguration]
EC2KeyName=sfbeta-aws
InstanceType=t2.micro

[aws:autoscaling:updatepolicy:rollingupdate]
RollingUpdateEnabled=false

[aws:ec2:vpc]
Subnets=
VPCId=

[aws:elasticbeanstalk:application]
Application Healthcheck URL=

[aws:elasticbeanstalk:application:environment]
PARAM1=
PARAM2=
PARAM3=
PARAM4=
PARAM5=

[aws:elasticbeanstalk:container:php:phpini]
allow_url_fopen=On
composer_options=--no-dev
display_errors=Off
document_root=/public
max_execution_time=60
memory_limit=256M
zlib.output_compression=Off

[aws:elasticbeanstalk:hostmanager]
LogPublicationControl=false

[aws:elasticbeanstalk:monitoring]
Automatically Terminate Unhealthy Instances=true

[aws:elasticbeanstalk:sns:topics]
Notification Endpoint=
Notification Protocol=email

First we start off with auto-scaling options. Since we’re running a single instance environment, MaxSize and MinSize are both 1. This means that EB will ensure you always have 1 EC2 instance running at all times (e.g. creating a new instance if the existing one goes down). We’re also specifying the Availability Zone we want our EC2 instances to be created in. EC2 instances in the same AZ as your RDS instance will cost less and connections between them will be faster. Set this option to the AZ in which you created your RDS instance.

The next option we’re editing is the EC2KeyName, which is the name of the key pair we created several sections ago. This will allow you to SSH into your EC2 instance(s) if the need arises (though you likely won’t need to).

The aws:elasticbeanstalk:application:environment contains your environment variables, which are added by your config files. You don’t need to edit this section, and even if you did, this doesn’t seem to actually set the environment variables (it just displays them).

Finally, we’re going to modify a few options under aws:elasticbeanstalk:container:php:phpini. We will set composer_options to –no-dev, so that dev add-ons aren’t installed when composer install is run. Last, we’ll set document_root to /public so that it points to Laravel’s public folder.

Add Environment Config Files

Located in the .ebextensions directory in the root of the project, the config files (*.config) contain commands for the environment to run and options to set. These config files run every time git aws.push is run (i.e., the environment is updated or a new EC2 instance within the environment is started), and they are run in alphabetical order. These config files SHOULD NOT be in your .gitignore file.

To start, we will create three files: 00environmentVariables.config, 01composer.config,  and 02artisan.config.

Additional resources:

Environment Variables

In the 00environmentVariables.config file, we will place all of the instructions for the application to modify the environment’s options, in this case to create environment variables (e.g., DB_HOST). Add the following code to this file:

option_settings:
   - namespace: aws:elasticbeanstalk:application:environment
     option_name: DB_HOST
     value: mysqldbname.dragegavysop.us-east-1.rds.amazonaws.com
   - option_name: DB_PORT
     value: 3306
   - option_name: DB_NAME
     value: dbname
   - option_name: DB_USER
     value: username
   - option_name: DB_PASS
     value: password

Here, namespace refers to the specific groups of options in .elasticbeanstalk/optionsettings.app-environment-name. Using the namespace elasticbeanstalk:application:environment we are stating that the options and their values below are for that namespace.

DB_HOST will be the endpoint shown in the RDS dashboard for the RDS instance set up earlier.
DB_PORT is usually 3306, unless changed during the RDS instance setup.
DB_NAME is the name of the database within the RDS instance (not the RDS instance name).
DB_USER is the username that was created during the RDS setup process.
DB_PASS is the username’s password.

Here is where you will add other environment variables, such as a MailChimp API key, an SQS host, etc.

Note: These environment variables can also be set manually in the EB Software Confirguration panel. If you do not want to have your DB credentials in your git repo, then you would manually set these variables.

Composer Commands

In the 01composer.config file, we will place all the composer commands to be run when a new instance is created or an existing instance is updated. Add the following code to this file:

commands:
   01updateComposer:
      command: export COMPOSER_HOME=/root && /usr/bin/composer.phar self-update

option_settings:
   - namespace: aws:elasticbeanstalk:application:environment
     option_name: COMPOSER_HOME
     value: /root

container_commands:
   01optimize:
      command: "/usr/bin/composer.phar dump-autoload --optimize"

First, commands are executed, which are run before the application and web server are set up. Here we self-update composer.phar to ensure the latest version is running on the instance.

Next we set a COMPOSER_HOME environment variable.

Last, container commands are executed, which are for the environment’s app container. These are run after the application and web server have been set up, and these commands have access to environment variables. Here we run composer optimize.

Note: EB will automatically run composer.phar install if it sees a composer.json file in the root directory AND does not find a vendor folder in the root directory. If your vendor folder is not in .gitignore, you will need to add composer.phar install to this file yourself.

Artisan Commands

In the 02artisan.config file, we specify container commands to run migrations and seeding.

These commands should ideally be run only once, or if you are adding/modifying tables. I also tend to just migrate:refresh the database every now and then on the development server, as error tend to compound themselves and exceptions start cropping up in my app.

container_commands:
   01migrateSeed:
      command: "php artisan migrate --force"
   02seed:
      command: "php artisan db:seed --force"

Here we migrate to create the new database (including migrating the Auth Token package) and seed the database.

You’ll notice that the migrate and db:seed command were separated. Why not just run migrate –seed? In Laravel 4.2, this seems to cause an error when run on Elastic Beanstalk. Separating the two commands allows the environment to set up properly without errors when using the –force flag.

Note: The –force option needs to be used here otherwise the CLI will ask for confirmation to run each command and your commands will timeout.

References: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html#customize-containers-format-options

Add Environment Variables to Database.php

Now that we have the config files set to create the database environment variables, we need to tell Laravel to use those for production.

Edit your database.php file so that your mysql connection parameters are set as below:

		'mysql' => array(
			'driver'    => 'mysql',
			'host'      => $_ENV['DB_HOST'],
			‘port’	=> $_ENV['DB_PORT’],
			'database'  => $_ENV['DB_NAME'],
			'username'  => $_ENV['DB_USER'],
			'password'  => $_ENV['DB_PASS'],
			'charset'   => 'utf8',
			'collation' => 'utf8_unicode_ci',
			'prefix'    => '',
		),

If you configured your RDS instance with your EB app (not recommended), then you could also set your connection options to:

		'mysql' => array(
			'driver'    => 'mysql',
			'host'      => $_SERVER['RDS_HOSTNAME'],
			‘port’	=> $_SERVER['RDS_PORT'],
			'database'  => $_SERVER['RDS_DB_NAME'],
			'username'  => $_SERVER['RDS_USERNAME'],
			'password'  => $_SERVER['RDS_PASSWORD'],
			'charset'   => 'utf8',
			'collation' => 'utf8_unicode_ci',
			'prefix'    => '',
		),

Note: If you are using a .env.*.php file to specify local database connection parameters, remember to add that file to your .gitignore.

If you do have a .env.local.php file, it would look something like this:

<?php return array( 	'DB_HOST' => 'hostname',
	'DB_PORT' => 'port',
	'DB_NAME' => 'dbname',
	'DB_USER' => 'username'
	'DB_PASS' => 'password',
	
);

Git Commit

With all of these changes now complete, commit these changes to your git repo.

Add Security Group Inbound Rule

The last thing left to do before starting up your environment is to give the Elastic Beanstalk application access to the MySQL RDS instance.

Head to the AWS EC2 management console and click on “Security Groups” under “Network & Security”. Here you should see two security groups: the default security group and a security group created for you Elastic Beanstalk application.

Click on the Elastic Beanstalk security group (for us, it’s called development, just like our environment) and copy the Group ID.

Security Groups

Right click on the default security group and select “Edit Inbound Rules”.

Here we need to add a MySQL type rule with a Custom IP equal to the Elastic Beanstalk security group’s Group ID. While you’re here, add a MySQL type rule for your IP [Tip: in the Source drop-down, you can simply select My IP].

Inbound Rules

Start the EB environment

Run eb start in the command line/terminal. Choose “no” if you are asked whether you want to use the latest commit for the environment.

AWS will now set up all the resources necessary for you environment. This may take some some time. Once complete, it will give you a URL at which you can access your server.

You can also view the status of your environment setup in the Elastic Beanstalk management console. Here you’ll see that’s it’s running Amazon’s Sample Application, but if you try to visit the URL you won’t receive a response. This is because the server is pointed to the /public folder where it would find Laravel, but you haven’t pushed your Laravel app onto the environment yet.

Run eb status to see the status of your environment (you can also see your environment’s status in the management console). If it’s green, move on to the next section.

Git Push

At this point, your environment is ready to update with your Laravel application.

Run git aws.push. After a few moments, it will upload your git repo to the environment and begin updating the environment.

If you run eb status at this point (or visit the management console), you’ll see the environment is still updating. I find it’s best to view the environment status via the management console, as you can see a running list of events below the status.

If an error occurs during the update, the event will list the command that caused the error (usually it’s an artisan command for me). Go to the Logs option in the left-hand menu and click the Snapshot Logs button. Once a log snapshot is available, click the “View log file” link to view the latest logs. Here you can investigate why an error occured. Just search for the command name that cause the error to occur.

Snapshot Logs

Done!

And that’s it! Navigate to the URL for your environment (e.g., environmentname-hdb582lbjd.elasticbeanstalk.com) to see your Laravel application live.

Deleting your Environment

Since we’ve added this environment’s Security Group ID to the inbound rules of the default Security Group, we need to first remove that rule in the EC2 Management Console.

After that rule is removed, deleting the environment is a simple command line/terminal command:

eb stop

Alternatively, you can terminate the environment from the management console from within your environment.

This may take some time as all the AWS resources created for your environment need to be deleted. You can monitor progress from the command line/terminal or from the Elastic Beanstalk Management Console.

Deleting your Application

To delete your application, ensure all environments have been successfully terminated. If there are un-terminated environments, check the event log for errors in that environment during the termination process.

Now run the command:

eb delete

A few seconds later you application will be deleted. This can also be done from the management console.

42 comments

  1. Very good blog! Do you have any suggestions for aspiring writers?

    I’m planning to start my own site soon but I’m a little lost on everything.
    Would you suggest starting with a free platform like WordPress or go
    for a paid option? There are so many choices out there that I’m totally overwhelmed ..

    Any suggestions? Thank you!

  2. Hi there, love your post with larval and elastic beanstalk. Have you have some experience with a web + worker tier + s3 storage with laravel 5? Im thinking of moving from a single cps to this kind of setup, but can’t find resources for it anywhere.

    Any plans for the blog on the production environment?

    With kind regards,
    Peter

  3. Hey Paul, thanks so much for this. I’d like to add my name to the list of folks looking to move a Laravel app from Homestead to EB. Even a rough blog would be helpful. Thanks so much!

  4. hi,
    Do you have any plans on a production tutorial? I am curious as to how to deploy my local laravel 5, running on homestead, up to amazon elastic beanstalk. There are less than a handful of tutorials and information online.

  5. Hi,
    I read over your instructions. Did you ever write the production guide for AWS?

    What do you think about EB and Homestead for laravel?

  6. Thank you Paul! Your post is encouraging for us. We are writing a new application stack on Laravel 5 and are going to use AWS for hosting. I’m currently in the process of ramping up EB for our deployments. Would love to compare notes, and help beta your new blog post.

    Can you help me understand why you choose to deploy your database outside of eb? It seems to me that our staging deployment should have it’s own database should something catastrophic happen. This would suggest having EB deploy a new RDS instance for staging and production.

    Thanks!

    1. If you launch an RDS instance with EB, then if you take down the EB environment, the RDS instance goes down too. So if you’re looking for a zero-downtime app, your best option is to create a new environment with your new app and swap environment URLs. And from my understanding, there’s no way to move an RDS instance from the owning EB environment to a new environment. So to just simplify things, I keep the RDS instance separate.

      1. Thanks Paul. That makes perfect sense for a production database environment. For our staging environments, we will be deploying a cloned image of the database to test against. So we will probably add them to our EB environments. Of course this causes a lot of complexity when moving from staging to production. Would love to hear how others handle this. Thanks!

  7. Hey Paul. I am trying to execute a laravel web application and I am following your guide, but I am unable to do few steps, can you help me in executing the application? It would be great help. Thanks.

    1. Andres, apologies for the delay. I plan on writing the production guide for AWS within the next week or so. There have either been changes with EC2 instance permissions or with the permissions that Laravel requires in Laravel 5, but I spent many hours with AWS support getting deployment working properly. This may be causing your issues. I’ll add all of my findings in my new blog post. You can also reply here if you have a specific question.

    1. Alfred, apologies for the delay. We are still using AWS, and I plan on writing the production guide for AWS within the next week or so. There have either been changes with EC2 instance permissions or with the permissions that Laravel requires in Laravel 5, but I spent many hours with AWS support getting deployment working properly. I’ll add all of my findings in my new blog post.

  8. Hi Paul! Thanks for sharing. I am currently evaluating AWS for a new project. Can you please update your blog with a Production Guide for AWS using Laravel? Are you currently using AWS for SimpliFit? How was your experience? Thanks in advance.

    1. Andres, apologies for the delay. We are still using AWS, and I plan on writing the production guide for AWS within the next week or so. There have either been changes with EC2 instance permissions or with the permissions that Laravel requires in Laravel 5, but I spent many hours with AWS support getting deployment working properly. I’ll add all of my findings in my new blog post.

Leave a Reply

Your email address will not be published. Required fields are marked *