
August 15, 2026
10 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Deploying Laravel on AWS EC2 with RDS requires separating your application logic from your data layer while maintaining strict network security between them. Many developers struggle with this architecture because it demands coordinating VPC peering, security groups, and PHP-FPM tuning across two distinct managed services rather than a single monolithic server. This guide provides the concrete configuration needed to ship a production-grade Laravel 12 application on AWS infrastructure in 2026.
Before writing any infrastructure code, verify your stack meets current 2026 baselines. I regularly audit Laravel development environments where outdated runtimes cause silent failures during deployment; for AWS production today, you need PHP 8.2 minimum (8.4 recommended), Laravel 11.x or 12.x, Composer 2.7+, and Node.js 22 LTS for asset compilation. Understanding these dependencies upfront prevents the most common integration issues when comparing cloud hosting options for business-critical applications.
How do you architect a secure VPC for deploying Laravel on AWS EC2 with RDS?
The foundation of any reliable AWS deployment is network isolation. Never place an RDS instance in a public subnet, and never allow SSH access directly to your application servers from the open internet without a bastion host or AWS Systems Manager Session Manager. In my experience working on production Laravel applications handling sensitive legal-tech data, a three-tier VPC architecture prevents accidental exposure and satisfies compliance requirements.
Subnet and security group configuration
Create three distinct subnets across at least two availability zones. The public subnet holds only your Application Load Balancer (ALB) and NAT Gateway. The private application subnet contains your EC2 instances running Laravel; these instances have no public IP addresses and route outbound traffic through the NAT Gateway for Composer updates and package installation. The isolated database subnet hosts RDS with "Publicly Accessible" explicitly disabled.
Security groups act as your primary firewall. Configure the RDS security group to accept inbound traffic on port 3306 (MySQL) or 5432 (PostgreSQL) only from the application security group ID, not from CIDR ranges. This ensures that even if another instance launches in the VPC, it cannot reach your database unless explicitly tagged. The application security group should accept HTTP/HTTPS only from the ALB security group and SSH only from your bastion or SSM endpoint. On real client projects, I have seen entire databases compromised because someone opened port 3306 to 0.0.0.0/0 during debugging and forgot to revert it; explicit security group references prevent this class of error entirely.
How do you provision and configure the EC2 instance for Laravel 12?
Once networking is established, provision your EC2 instance with Ubuntu 24.04 LTS. Avoid Amazon Linux for Laravel unless your team has specific expertise; Ubuntu’s PPA ecosystem makes managing multiple PHP versions significantly easier. Select an instance type based on workload: t3.medium works for low-traffic portals, but m6g.large or c7g.large provides better price-performance for compute-heavy Laravel applications processing queues or generating PDFs.
Installing PHP 8.4 and required extensions
Laravel 12 supports PHP 8.2 through 8.4. I standardize on PHP 8.4 for new deployments in 2026 to leverage performance improvements and typed class constants. Add the Ondřej Surý PPA and install the full extension set Laravel requires:
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php8.4-fpm php8.4-cli php8.4-common \
php8.4-mysql php8.4-pgsql php8.4-sqlite3 \
php8.4-xml php8.4-curl php8.4-mbstring \
php8.4-zip php8.4-bcmath php8.4-intl \
php8.4-readline php8.4-redis php8.4-gd \
nginx composer git unzip After installation, tune PHP-FPM for your instance size. Edit /etc/php/8.4/fpm/pool.d/www.conf. For a 2-vCPU instance with 4GB RAM, start with:
pm = dynamicpm.max_children = 20(approximately 50MB per worker × 20 = 1GB reserved)pm.start_servers = 4pm.min_spare_servers = 2pm.max_spare_servers = 6pm.max_requests = 500(recycle workers to prevent memory leaks)
These values assume each Laravel request consumes roughly 40–60MB. Monitor actual memory usage with htop or CloudWatch after load testing; over-provisioning children causes OOM kills, while under-provisioning leaves CPU idle during traffic spikes.
Nginx virtual host configuration
Create /etc/nginx/sites-available/laravel with proper FastCGI parameters pointing to the PHP 8.4 socket:
server {
listen 80;
server_name _;
root /var/www/html/current/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
location ~ /\.(?!well-known).* {
deny all;
}
} Enable the site and validate configuration before reloading:
sudo ln -s /etc/nginx/sites-available/laravel /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx php8.4-fpm How do you connect Laravel to RDS securely and optimize database performance?
With EC2 provisioned, configure Laravel’s environment to reach RDS through the private endpoint. Never use the public endpoint even if temporarily enabled for migration; DNS resolution can switch unexpectedly, and latency increases significantly outside the VPC.
Environment variables and connection pooling
In your .env file, set the database host to the RDS private endpoint exactly as shown in the AWS console:
DB_CONNECTION=mysql
DB_HOST=laravel-prod-db.cluster-abc123xyz.us-east-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=laravel_prod
DB_USERNAME=app_user
DB_PASSWORD="${SSM:/prod/laravel/db-password}"
DB_SSLMODE=require Note the DB_SSLMODE=require directive. RDS supports TLS encryption in transit by default, but Laravel does not enforce it unless explicitly configured. Without this flag, credentials travel unencrypted between EC2 and RDS within the VPC—a risk if traffic traverses peered networks or if future misconfigurations expose routes. Download the RDS CA bundle and reference it if your organization requires certificate verification beyond AWS’s default trust chain.
For high-traffic applications, consider enabling persistent connections or using an external pooler like PgBouncer (for PostgreSQL) or ProxySQL (for MySQL). Laravel’s native PDO connections are stateless per request; under heavy queue workloads or Octane deployments, connection churn becomes a bottleneck. On a legal-tech portal I built handling concurrent document generation, adding ProxySQL reduced database connection overhead by 40% and eliminated intermittent timeout errors during peak filing periods.
Running migrations safely against RDS
Never run php artisan migrate manually on production EC2 instances. Integrate migrations into your deployment pipeline with explicit safeguards:
- Run
php artisan migrate:status --pendingfirst to preview changes. - Execute migrations during low-traffic windows with maintenance mode enabled.
- Use
--forceflag only in automated scripts, never interactively. - Back up RDS snapshots before destructive schema changes.
- Test migrations against a staging RDS clone restored from production snapshot weekly.
If your application uses read replicas, ensure migrations target the writer endpoint exclusively. Laravel’s default configuration handles this correctly, but custom multi-connection setups sometimes accidentally route DDL statements to readers, causing replication lag or silent failures.
What deployment strategy minimizes downtime for Laravel on AWS?
Zero-downtime deployment is non-negotiable for production Laravel systems. I use Deployer 7 with symlinked releases on every AWS project because it integrates cleanly with GitLab CI, supports atomic rollbacks, and requires no additional infrastructure like CodeDeploy agents. For teams already invested in AWS-native tooling, CodeDeploy with Blue/Green or Rolling configurations achieves similar results, but adds operational complexity that rarely justifies itself for small-to-medium Laravel applications.
| Strategy | Downtime | Rollback Speed | Complexity | Best For |
|---|---|---|---|---|
| Deployer 7 (Symlink) | < 1 second | Instant (symlink swap) | Low | SMB, Legal-Tech, Agencies |
| AWS CodeDeploy (Rolling) | Partial during batch | Minutes (re-deploy) | Medium | Mid-size fleets, Compliance |
| Blue/Green (ALB Swap) | Zero | Seconds (listener rule) | High | Enterprise, High-Traffic SaaS |
| Docker/ECS Fargate | Zero (rolling) | Minutes (task revision) | Very High | Microservices, Polyglot stacks |
Deployer 7 configuration essentials
Your deploy.php should define shared files and writable directories explicitly. Missing entries here cause post-deploy cache misses or log permission errors that manifest hours after release:
<?php
namespace Deployer;
require 'recipe/laravel.php';
host('production')
->set('hostname', '10.0.2.15')
->set('remote_user', 'deploy')
->set('deploy_path', '/var/www/html');
set('shared_files', ['.env']);
set('shared_dirs', ['storage/app', 'storage/logs']);
set('writable_dirs', ['bootstrap/cache', 'storage/framework']);
after('deploy:succeeded', 'artisan:optimize:clear');
after('deploy:succeeded', 'artisan:migrate');
after('deploy:succeeded', 'artisan:queue:restart'); Critically, restart queue workers after every deploy. Laravel caches job classes in memory; without queue:restart, old code continues executing queued jobs even though web requests serve new code. This mismatch causes subtle bugs that are extremely difficult to diagnose. I have debugged payment processing failures on eCommerce platforms where the webhook handler ran updated logic but the queue worker still executed the previous version’s refund routine—always restart queues atomically with the symlink swap.
How do you monitor and maintain Laravel performance on AWS long-term?
Deployment is day one; operations determine whether your architecture survives month six. Configure CloudWatch Logs agent on EC2 to stream Laravel logs, PHP-FPM slow logs, and Nginx access/error logs to centralized storage. Set alarms on RDS CPU utilization exceeding 70%, free storage dropping below 20%, and replica lag surpassing 30 seconds. These thresholds catch problems before users report them.
Implement application-level health checks beyond simple HTTP 200 responses. Create a /health route that verifies database connectivity, Redis availability, and queue worker responsiveness. Configure your ALB target group to use this endpoint for health checks; when dependencies fail, the ALB stops routing traffic to unhealthy instances automatically. On production systems I maintain, this pattern has prevented cascading failures during RDS maintenance events and ElastiCache node replacements.
Schedule regular maintenance tasks: rotate OPcache every deploy, prune old log files weekly, analyze slow query logs monthly, and test disaster recovery quarterly. Backup verification matters more than backup creation; restore your RDS snapshot to a test instance monthly and run your application’s smoke tests against it. If restoration takes longer than your RTO allows, adjust your backup strategy or instance sizing now—not during an actual outage.
Cost optimization deserves ongoing attention. Right-size EC2 instances based on CloudWatch metrics after 30 days of production traffic. Reserve instances for predictable baseline load and use spot instances for queue workers that tolerate interruption. Enable RDS Performance Insights to identify expensive queries; often, adding a composite index reduces monthly RDS spend more effectively than vertical scaling. For Nepal-based clients billing in NPR, even modest AWS optimizations translate to meaningful savings when converted annually.
Next Steps for Your Laravel AWS Deployment
Deploying Laravel on AWS EC2 with RDS demands disciplined networking, precise runtime configuration, and automated deployment workflows—but the payoff is a resilient, scalable platform that grows with your business. Start with the VPC architecture outlined above, validate each layer independently before integrating, and invest early in monitoring and rollback capabilities. If you need hands-on assistance architecting or migrating your Laravel application to AWS, reach out to discuss your project requirements. Whether you are building a legal-tech portal, an eCommerce platform, or a SaaS product, getting the infrastructure right from day one prevents costly rework later.

