Deploy Laravel to AWS via Console (Part 2): EC2 with Amazon Linux 2023 — Nginx + PHP-FPM from Scratch
·
5 min read
In Part 1, we created the VPC, subnets, and security groups via Console. Now let's launch a server and run Laravel on it.
Note: Launching EC2 is done via Console, but after SSH-ing in, software installation (Nginx, PHP, Composer...) requires terminal commands. This is the nature of server management.
Step 1: Launch EC2 Instance
1a. Open EC2 Console
- Search bar → "EC2" → click EC2
- Verify region: ap-northeast-1 (Tokyo)
- Click "Launch instance"
1b–1h. Configuration
| Setting | Value |
|---|---|
| Name | laravel-web-01 |
| AMI | Amazon Linux 2023 AMI (Free tier eligible) |
| Instance type | t3.small (2 vCPU, 2GB RAM) |
| Key pair | laravel-production |
| VPC | laravel-production-vpc |
| Subnet | Public subnet 1 (ap-northeast-1a) |
| Auto-assign public IP | Enable |
| Security group | laravel-ec2-sg |
| Storage | 20 GB gp3 |
| IAM instance profile (Advanced details) | laravel-ec2-role |
Click "Launch instance". Wait 30-60 seconds.
1i. Get Public IP
- EC2 → Instances → find
laravel-web-01 - Wait for Running state
- Note the Public IPv4 address
Step 2: SSH into Server
ssh -i ~/.ssh/laravel-production.pem ec2-user@54.178.xxx.xxx
Step 3: System Setup
sudo dnf update -y
sudo timedatectl set-timezone Asia/Tokyo
sudo dnf install -y git vim htop unzip tar wget jq
Step 4: Install Nginx
sudo dnf install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Step 5: Install PHP 8.4
sudo dnf install -y \
php8.4-fpm php8.4-cli php8.4-common php8.4-mbstring \
php8.4-xml php8.4-curl php8.4-zip php8.4-gd php8.4-intl \
php8.4-bcmath php8.4-mysqlnd php8.4-pdo php8.4-opcache \
php8.4-redis php8.4-sodium
If PHP 8.4 isn't in default repos, use Remi:
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm sudo dnf module reset php -y && sudo dnf module enable php:remi-8.4 -y sudo dnf install -y php-fpm php-cli php-common php-mbstring php-xml \ php-curl php-zip php-gd php-intl php-bcmath php-mysqlnd php-pdo \ php-opcache php-redis php-sodium
Configure PHP-FPM
sudo vim /etc/php-fpm.d/www.conf
user = nginx
group = nginx
listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500
Production php.ini
memory_limit = 256M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 60
expose_php = Off
display_errors = Off
log_errors = On
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
opcache.validate_timestamps = 0
sudo systemctl enable php-fpm && sudo systemctl start php-fpm
Step 6: Install Composer & Node.js
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install -y nodejs
Step 7: Nginx Configuration for Laravel
sudo rm -f /etc/nginx/conf.d/default.conf
sudo vim /etc/nginx/conf.d/laravel.conf
server {
listen 80;
server_name your-domain.com;
root /var/www/laravel/current/public;
index index.php index.html;
charset utf-8;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
gzip on;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml text/javascript image/svg+xml;
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
sudo nginx -t && sudo systemctl reload nginx
Step 8: Deploy Directory Structure
sudo mkdir -p /var/www/laravel/{releases,shared}
sudo mkdir -p /var/www/laravel/shared/{storage,node_modules}
sudo mkdir -p /var/www/laravel/shared/storage/{app,framework,logs}
sudo mkdir -p /var/www/laravel/shared/storage/framework/{cache,sessions,views}
sudo chown -R ec2-user:nginx /var/www/laravel
sudo chmod -R 775 /var/www/laravel/shared/storage
Step 9: First Manual Deploy
cd /var/www/laravel
RELEASE=$(date +%Y%m%d_%H%M%S)
git clone git@github.com:your/repo.git releases/$RELEASE
cd releases/$RELEASE
composer install --no-dev --optimize-autoloader --no-interaction
cp .env.example /var/www/laravel/shared/.env
vim /var/www/laravel/shared/.env
rm -rf storage
ln -s /var/www/laravel/shared/storage storage
ln -s /var/www/laravel/shared/.env .env
npm ci && npm run build && rm -rf node_modules
php artisan key:generate
php artisan config:cache
php artisan route:cache
php artisan view:cache
cd /var/www/laravel
ln -sfn releases/$RELEASE current
sudo systemctl reload php-fpm
Step 10: Supervisor for Queue Workers
sudo dnf install -y supervisor
sudo systemctl enable supervisord && sudo systemctl start supervisord
sudo vim /etc/supervisord.d/laravel-worker.ini
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/laravel/current/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=ec2-user
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log
sudo supervisorctl reread && sudo supervisorctl update
Step 11: Laravel Scheduler
crontab -e
* * * * * cd /var/www/laravel/current && php artisan schedule:run >> /dev/null 2>&1
Verify via Console
- EC2 → Instances → laravel-web-01 → Status checks: both ✅ passed
- Monitoring tab: CPU, Network, Disk I/O
Series Navigation:
- ← Part 0: Prerequisites
- ← Part 1: Architecture & VPC
- Part 2: EC2 & Amazon Linux 2023 (You are here)
- Part 3: RDS, S3 & ElastiCache →
- Part 4: ALB, CloudFront & SSL →
- Part 5: CI/CD & Zero-Downtime Deploy →