Adriano Cataluddi
November 2, 2022
Adriano’s ability to see the big picture comes from almost 30 years in IT, managing the widest range of scenarios. He has created and led sophisticated enterprise-level SaaS solutions for household names, and is frequently called on to translate underlying technical complexity for executive boards.
Laravel is a web application framework that allows you to build apps using PHP, HTML and JavaScript. It can be used to build just about anything—Laravel provides the tools you need to build complete browser-based applications, as well as APIs and backends for mobile apps.
Laravel’s extensive feature set makes it an excellent choice for rapid prototyping, while also providing a stable foundation for production applications.
Amazon Web Services (AWS) provides all of the cloud hosting tools required to host Laravel applications—from hosting the PHP code to storing data in databases, caching content and storing files.
This article will detail the best practices for optimizing, hosting and scaling Laravel applications on AWS.
Horizontal versus vertical scaling
There are two ways to scale the infrastructure that hosts an application. Vertical scaling is the simplest method and involves simply increasing the performance of the single server hosting your code by adding more RAM or a faster CPU. Vertical scaling, however, has limits—there’s only so much processing power and memory that can be added to a single server.
Horizontal scaling does not suffer this limitation. Rather than relying on a single highly-specced server, the application is hosted on any number of servers all serving the same code with traffic being spread across them. If capacity is reached, more servers can be added.
While Laravel is designed to horizontally scale, this requires appropriate infrastructure so data that is expected to persist between requests can be shared between servers.
Optimizing Laravel in AWS
It is vital that you optimize your Laravel application before you consider scaling—optimizing will help you get the most out of your existing infrastructure, ensuring that when you do scale, no capacity is wasted.
Optimizing your hosting environment
The hosted services provided by AWS are flexible and can be tailored to your specific requirements to ensure cost-effectiveness while ensuring your users get the best experience possible.
If you are already hosting your Laravel code on AWS Elastic Compute Cloud (EC2) instances, the first thing you should do is switch to Graviton-based instances. Graviton processors are designed for delivering high-performance web applications, and are often more cost-effective to run.
You should also make sure you are building your applications using the latest version of Laravel and using the latest version of PHP, as recent versions of PHP have seen large increases in performance compared to older versions.
Caching and sessions
By default, Laravel stores its cache and user sessions in the filesystem. This is incredibly inefficient, and is also unsuitable for future scaling as the cache and session data will not be shared between the multiple servers delivering your application.
While it is possible to store these in the application’s database, SQL databases are not designed for this, and frequent caching requests during high-traffic periods will quickly slow down your database’s response time.
For best performance, configure a purpose-built caching service such as Redis or Memcached as your application cache and session store. AWS makes both Redis and Memcached servers available through Amazon ElastiCache.
Your application’s configuration, routes and views are usually all compiled and delivered to the user on demand. By caching these, performance is greatly increased. After publishing your application to your production environment, ensure that your configuration, routes and views are all cached by running:
php artisan view:cache php artisan config:cache php artisan routes:cache
Application architecture
Your Laravel application code should be optimized to consume as few resources as possible. Reducing the number of database queries your application makes will speed up your application.
Each database server your application connects to supports a limited number of concurrent connections, with subsequent queries being queued. Query your database only when required, and cache values wherever possible.
You should also use eager loading and lazy loading appropriately. Eager-loading models related to an item you are working with can reduce the overall number of queries being made. However, in some situations, lazy loading is faster—for example, when accessing distant relationships through other models where the intermediate data is not required.
Analyze your database queries and ensure that you are loading your data in a way that results in as few queries as possible.
If your application performs tasks that are time-consuming, and do not require further user interaction (for example, compiling a report and emailing it to a user), consider moving these tasks into Laravel queues. Queued jobs run asynchronously in the background, so you can deliver a web page to the user without waiting for the associated task to complete.
To further increase the responsiveness of your application, limit the number of packages you are using when developing your app. Each package will load its code each time your application receives a request—the more packages, the slower your application will respond.
When deploying to production, make use of the available composer options to skip installing development dependencies for each package, to reduce the size of your application, and optimize the auto-loader by running:
composer install --prefer-dist --no-dev -o
Laravel packages are loaded for each request through service providers that bootstrap the routes, middleware and bindings for each package. Laravel comes with a number of providers enabled by default, but you may not be using all of them. These default providers can be disabled by commenting them out in the config/app.php file.
Optimizing for delivery
Make sure that any images and media assets used in your application are optimized to make them as small in size as possible. Consider the screen size your users will be viewing your application on—there is no need to deliver billboard-sized images to a palm-sized device.
Minify your JavaScript and CSS to reduce the time it takes to load your web pages, and remove any unnecessary JavaScript libraries and analytics code from your views. Keep your front end lean so that it loads quickly and doesn’t slow down your user experience.
A content delivery network (CDN) will also improve the performance of your application. Static assets and user content can be hosted on Amazon CloudFront to make sure that the files are delivered to user devices from a server geographically close to them.
Laravel Octane
Laravel Octane allows you to host Laravel entirely in memory. Your entire application is loaded and kept running, rather than being executed once for each request. This greatly improves performance, but requires some additional configuration and considerations to your application’s structure. Laravel Octane can be hosted on and horizontally scaled in AWS the same way as standard Laravel deployments.
Optimize your AWS cloud environment with InfrAssure.
Horizontally scaling Laravel in AWS
Horizontally scaling Laravel in AWS is a relatively straightforward process—Laravel is out-of-the-box ready to scale, with support for AWS features like Amazon S3 (Simple Storage Service) storage, ElastiCache Redis/Memcached servers, Amazon SES (Simple Email Service), and Amazon RDS (Relational Database Service)/Aurora database hosting.
Preparing Laravel for horizontal scaling
The common functionality of web applications usually relies on a few core components: the database, caching, notifications (email, SMS, Slack, etc.), file storage and, of course, the application itself—the code that generates the content that your users view on their devices.
When horizontally scaling your application and delivering it from multiple servers, one request may not be served by the same server as the previous one.
This means that all of the servers hosting your application should store their data in shared external database and file storage systems (rather than on their own filesystems, which are inaccessible to each other), including their caches and session data. AWS makes doing this easy, providing dedicated hosting for all of the services Laravel relies on.
To prepare your Laravel application for horizontal scaling in AWS, configure it to use Amazon RDS or Amazon Aurora MySQL-compatible databases. These services are themselves able to scale, with RDS allowing up to five replicated database hosts and Aurora supporting even larger deployments.
ElastiCache should be set up to provide caching and queueing for your Laravel app using either Redis or Memcached. Session data should also be configured to be stored in either Redis or Memcached to ensure that sessions are maintained between requests.
Application storage in Laravel also uses the filesystem by default. Amazon S3 provides fast, affordable bulk file storage with no storage limits, making it perfect for storing user-uploaded content like photos and videos.
Laravel supports S3 as a storage provider natively—all you need to do is create an AWS S3 bucket and change the relevant Laravel configuration options. Data stored on S3 can also be delivered through Amazon’s CloudFront CDN to further increase performance.
Logs, again, are stored on the filesystem by default by Laravel. Configure them to use S3 storage, or consider using an external logging service like Sentry to monitor your application and watch for errors and bugs.
Sending email is not allowed from Amazon EC2 instances, and must be done through the SES email service, which supports sending email via SMTP. Again, this can be set up in Laravel by updating the email settings in your app’s configuration.
Most well-designed third-party Laravel packages will either use the configured storage and database services from your application, or provide options to use external services through their own configuration parameters.
Hosting scalable Laravel apps—EC2 instances and Kubernetes
Laravel can be deployed to AWS for scaling using either EC2 instances or Kubernetes. To deploy to EC2, you need to deploy an EC2 instance, install and configure your application on it (with the above scaling considerations implemented), and then create an AMI (Amazon Machine Image) of the configured instance.
This image can then be used to create further identical instances, all serving the same code and connecting to the same backend services.
If you would like a solution that lends itself to automation, you can create a deployable Docker image of your Laravel application and deploy it to Amazon’s Elastic Kubernetes Service (EKS).
The end result of both of the above is the same—your Laravel application is served from multiple concurrent hosts, ready for traffic to be spread over them using a load balancer. The number of hosts can be increased or decreased according to demand.
Load balancing and auto-scaling
Once you have your application code deploying to multiple servers, a load balancer needs to be configured to send traffic to them. If you are deploying to EC2 instances, the Elastic Load Balancer can be used. You can also configure AWS Auto Scaling to dynamically adapt the number of running hosts based on demand.
If you are deploying to the Elastic Kubernetes Service, much of this work is done automatically for you. EKS will automatically scale the number of running nodes based on conditions you define and can also direct traffic to nodes running in different regions, so that your users are served by infrastructure that’s close to them.
When deploying to multiple servers or a Kubernetes cluster, continuous integration/continuous deployment (CI/CD) tools will save your team time by automating repetitive testing and deployment tasks—pushing your code, creating and deploying images and deprovisioning outdated instances as required.
Many CI/CD solutions will also allow you to control AWS services directly through the AWS command line tools.
Fast, secure AWS deployments need a great cross-functional MSP
Hiring a managed service provider (MSP) to manage your AWS infrastructure is a single step that will vastly improve your productivity. Your team can focus on developing great Laravel applications, without being distracted by managing the numerous AWS services that your application relies on.
Your developers will have a single point of contact who investigates and resolves hosting and deployment issues and who provides up-to-date advice and information on AWS services.
Logicata is an Amazon Web Services MSP with experience in both developing and delivering high-performance applications on AWS. We understand what development teams need and offer a holistic approach to building and managing AWS infrastructure that considers both the technical and cost implications. We can even take a look at your code to see what AWS-specific optimizations are possible.
We’ve provided AWS managed services to businesses just like yours. If you are looking to deploy high-performance, scalable web applications or have any questions about deploying Laravel applications to AWS, contact Logicata to find out how we can help you.