Hot on the heels of my Laravel on AWS Elastic Beanstalk Dev Guide (i.e., 2.5 years later), I’m happy to publish my Laravel 5 on AWS Elastic Beanstalk Production Guide! So let’s dive right into it.
There have been a lot of changes with Laravel and with Elastic Beanstalk since my Dev Guide in 2014. This guide steps you through the process of deploying a Laravel 5 app to AWS Elastic Beanstalk.
I presented this deployment flow during the February LaravelSF Meetup event at the AWS Loft in San Francisco. During my presentation I used my demo app, LaraSqrrl, to show the process, along with integration with S3 and SQS (upcoming posts).
As in my previous guide, there are some prerequisites before you jump into the guide:
- You should have an AWS account.
- You have git installed and a git repo initiated for your project.
- You should familiarize yourself with Elastic Beanstalk.
I like to keep my RDS instance separated from Elastic Beanstalk, as RDS instances created by and associated with an Elastic Beanstalk environment will be terminated when the environment is terminated. Separating the RDS instance from the environment allows you to keep the same database regardless of the environment.
Step 1: Choose your engine
Choose whichever database engine your app is set up for. I generally use MySQL with Laravel, so that’s what I’ll select.
Step 2. Production vs. Development
For production apps, Multi-AZ deployment is recommended. The advantage here is that if one server goes down for any reason, you’ll have a backup ready to go. And if you choose to change any options on your database (e.g., increase allocated storage), the servers will be updated one at a time, meaning your database never suffers any down-time and seamlessly switches to the updated server.
Step 3. Specify Database Details
Now you’re ready to set up the details of your database. The important bits here are:
- DB Instance Class – The size of the RDS instance. Choosing the right size is a difficult task, but db.t2.small is probably the smallest you should use for any production app. You can always upgrade the size as your app grows.
- Multi-AZ Deployment – Enable or disable deploying to multiple Availability Zones (keep in mind this means you’ll be paying for 2 instances of the size specified).
Step 4: Network, Security, Database Name, Backup, Monitoring, and Maintenance
This is a pretty big sections, but we’ll start with Network and Security options. I’d suggest leaving everything as the default, but we want to specify our VPC Security Group. There are 2 ways that I’ve set this up in the past (though I’m certain you can do it many other ways as well, so leave your experiences in the comments!):
- Use the default security group. I’ve done this when I plan to have one RDS instance host databases for several Elastic Beanstalk apps. I treat the default security group as the “database security group” and allow inbound connections for only my IP and the Elastic Beanstalk apps using the DB.
- Use the AWSEBSecuritygroup security group. I’ve done this when I will have only one Elastic Beanstalk instance access the RDS instance. Be sure to select the AWSEBSecuritygroup group (like in the screenshot below) and not the AWSEBLoadBalancer security group, as it’s the EC2 instances in theAWSEBSecuritygroup group accessing RDS and not the load balancer!
After that you have some additional database options. There is only really one field of note here, and that’s Database Name. This is the name of the database schema that will be created within the MySQL database. You can also change the port, though I tend to leave it at the default.
And finally we have Backup, Monitoring, and Maintenance. For Backup retention period, adjust this based on the data you’ll be storing. Generally only a few days is enough, and 7 days feels like overkill. If you don’t want any backup retention, just change this value to 0. When you do that, you’ll get this lovely warning:
Then adjust your Backup Window based on when you think your app will be used the least during the day.
Enhanced monitoring can give you insight into a lot of specific of your RDS instance, but you’ll have to pay for data transfer charges to CloudWatch Logs. Check out this AWS blog post about enhanced monitoring for more details.
Last but not least, is server maintenance. I would recommend leaving Auto Minor Version Upgrade as yes (to get bug fix updates), and change the Maintenance Window to a period of the day where your app will be used the least.
We’re ready to go now, so go ahead and click “Launch DB Instance”! It’ll take a bit for the instance to launch, but you can move on to the next section.
Security Credentials and Key Pair
If you followed my Elastic Beanstalk Dev Guide, then you should already have your AWS access key and a Key Pair so you can SSH into your EC2 instances. If not, visit that post and read those 2 sections (cmd+f or ctrl+f and search for “Generate Security Credentials”, since I don’t have in-page linking set up). Alternatively, skip the section on creating a Key Pair and create it during the Elastic Beanstalk setup process.
Keep these access keys in an easy-to-access location as you’ll need them in just a bit.
Install the new EB CLI
The easiest way to install the new EB CLI is via homebrew:
brew install awsebcli
And that’s it. If you don’t have homebrew or want to see the other ways to install the EB CLI, check out AWS’s EB CLI installation guide.
Initialize Your Elastic Beanstalk App
Open your command prompt/terminal and follow along to initialize Elastic Beanstalk for your app. Navigate to the root directory of your Laravel app and run the following command:
You’ll be prompted to set up a number of parameters for your app and account. First up, default service region:
Set this up in the region from which you think your app will be accessed the most. I usually default to US East.
Next you’ll have to enter your AWS credentials. These are the keys you either generated a few sections above or already had set up.
Now you’ll name your Elastic Beanstalk Application. Think of the application as the high-level collection for all the components (i.e., individual deployments, versions, and configs) for your app.
After your application name, the EB CLI will auto-detect the language you’re using, in this case PHP, and set the version of that language to use. The latest PHP version at the moment is 5.6. If you required PHP 7, you’ll need to configure your app to use a custom EC2 AMI (Amazon Machine Image). This is out of the scope of this post, but you can check out the Elastic Beanstalk documentation for Creating a Custom Amazon Machine Image (AMI).
Last up we have setting up SSH. This is an optional step, but I strong recommend you set this up so you can access your EC2 instances via SSH. This is where you’ll specify your Key Pair name if you’ve already generated one or generate a new one. If you generate a new Key Pair, you’ll need to optionally specify a passphrase which you’ll enter every time you use your Key Pair to SSH into an EC2 instance.
And that’s it! Your Elastic Beanstalk app is now set up. Head over to the Elastic Beanstalk Management Console and you’ll see your app with no environments yet.
If you check out your project directory, you’ll see a new
.elasticbeanstalk directory. This directory has also been added to your
.gitignore file. In the directory you’ll find a
config.yml file that specifies all the settings we just chose:
Create the Elastic Beanstalk Environment
Now we’re ready to set up our EB environment. If you followed my Elastic Beanstalk Dev Guide, then you remember being prompted to set the various EB config options before you launched anything, but the new EB CLI changes this process a bit. When you launch an EB environment, it uses some default configuration options, but you no longer have the ability to set those options through the CLI via prompts (e.g., single instance vs. load balanced environment) before you launch. You can create an environment and pass some options with the
eb create command, but some important settings are missing (e.g., composer install option and document root).
You have 2 routes to deal with this:
- If you’ve already created an EB environment and want to use the same environment configuration options, you can save that environment’s configuration and use it to create a new environment. If this is you, then start at Step 2
- If you’ve never created an EB environment, then you’ll need to create an environment first, download the saved configuration, modify the configuration options, save the configuration, then use it to create a new environment. For this, start at Step 1 below.
Step 1: Create the default environment
If you want to create an auto-scaling environment, run:
eb create environment-name -i t2.micro --scale 2
If you want to create a single-instance environment, run:
eb create environment-name -i t2.micro --single
You’ll want to input your own values for the various options, but here are what they mean:
-i value– instance type, check out the list of EC2 Instance Types to choose one that fits your needs.
--scale value– auto-scaling group starting size
--single– specify a single-instance environment
If you just type
eb create without any options, you’ll be prompted to specify some information (such as environment name). Here’s what that looks like:
This process can take some time. Whether you pass in options to the
eb create statement or not, once the environment is created you’ll see:
eb create method has several other parameters you can specify, so I encourage you check those out on the eb create reference page.
Step 2: Save the environment configuration locally, modify, and upload to S3
Next up we want to modify our environment to our needs for a Laravel app. First up, run
eb config save --cfg configName where configName is the name of your choice for this config file.
This downloads the config file being used for the current environment to your local environment. Open this file in your favorite editor. Here’s what my config file looks like for an auto-scaling group of 1 instance:
The most important part of the config file is the
aws:elasticbeanstalk:container:php:phpini: parameters, which specify the server to server from the
/public folder and to run composer with the
I would recommend that you do not copy my config file and overwrite yours, as some of the IDs and group names will be different. Only choose the pieces that you need.
To read more about these yaml config files, check out AWS’s documentation on the Environment Manifest
Alright, we have our config file modified, now we need to upload it to S3 so we can use the config to launch new environments. Run
eb config put configName where configName is what you used in the first download step.
These config files can be confusing, and I’d recommend you read AWS’s documentation to learn a bit more:
- AWS Environment Manifest
- Setting Configuration Options Before Environment Creation
- Setting Configuration Options During Environment Creation
- Setting Configuration Options After Environment Creation
Step 3: Create a new environment using the saved configuration
Now that you have a config file for Elastic Beanstalk set up for Laravel, you can either:
- Terminate your current environment and create a new environment using the configuration file. To do this, run:
eb terminate environment_name
eb create environment_name --cfg configName
- Modify the existing running environment by running
eb config --cfg configName
I’ve had mixed results with modifying the running environment, so I usually just start with a clean slate. I encourage you to try both methods and see which works best for you.
Elastic Beanstalk Config Files
Although we have the Elastic Beanstalk environment set up, we still need to create some configuration files that Elastic Beanstalk will run at deploy time. This includes setting up composer, cron, and environment variables.
When you first run eb init for your repo, the eb CLI creates a .ebextensions folder in the root directory of your repo. This is where you put configuration files to run at deploy time. For more information about these files, check out the Advanced Customization With Configuration Files (.ebextensions) documentation.
- The config files generally run in order of name, which is why I number my files to ensure specific order. There are some caveats, where certain types of commands are run first regardless, and that information can be found in the above documentation.
- Be sure to commit these config files to your repo, otherwise they won’t be part of the deploy to Elastic Beanstalk!
The setup.config is pretty simple:
COMPOSER_HOME, update Composer, and optimize Composer. I’ve also included the
composer_options parameters in here as well in case you’d prefer to set these options here instead of the environmen.cfg.yaml file.
.env file to your repo is bad practice, and can be dangerous for public repos. Committing your environment variables to Elastic Beanstalk
.config files is also bad practice. So where does that leave you? Well, my preferred method is to keep a production .env file in a private S3 bucket, and pull it in while deploying.
Here’s my config script for that:
What this script does is apply an S3 role on the instance to access the
app-env bucket where I have my production
.env file. Next is fetches the file from S3 and moves it to a temporary folder. Lastly, it moves the file to the the
/var/app/ondeck directory, which is where the currently deploying app is set up before being moved to current folder.
Before this script can do it’s magic, though, we need to set up S3!
Add or edit the bucket policy to the following:
The “AWS” parameter is the role ARN that will have access to this bucket. You can get your elastic beanstalk role ARN by going to the IAM console and clicking on roles.
Select the aws-elasticbeanstalk-ec2-role (or the custom EC2 role you created if you chose to do so), and copy the Role ARN. Use this ARM in your bucket policy above.
With all that set up, go ahead and upload your .env file to your S3 bucket.
When deploying your Laravel app for the first time, you will likely need to run a migration and maybe seed the database. This file contains those Artisan commands.
After the first deploy, comment out the
db:seed command and the
migrate command if it isn’t needed. You’ll notice these two commands have the
leader_only: true parameter, indicating that these commands should not run on each EC2 instance if you have more than 1 instance in your auto-scaling group.
The remaining commands clear the cache, optimize Artisan, and finally set the proper permissions on the Laravel folder.
This file will setup supervisor to monitor your queue workers. If you will have separate queue worker instances, then this file isn’t necessary for this environment.
Note: After the first deploy, remove this file fully, as re-running these commands will cause errors on deploy. I’m looking into ways to optionally do this when running updates to ensure when your app auto-scales, supervisor starts up on the new EC2 instances. If you have suggestions, please leave a comment!
This file set up the Artisan Scheduler cron job for your app. If your app won’t be using Scheduler, omit this file from your config files.
Deploy your Laravel app
Alright, we have our Elastic Beanstalk environment configured and our deploy config files set! We’re finally ready to deploy. Luckily, this part is easy. Just run:
eb deploy environment_name
That’s it! You’re up and running on Elastic Beanstalk! Navigate to the URL for your environment (e.g., environmentname-hdb582lbjd.elasticbeanstalk.com) to see your Laravel application live.
Deleting your Environment
As with the dev guide, 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 terminate environment_name
Did I miss something? Have questions about the process? Leave a comment!