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

  1. Search bar → "EC2" → click EC2
  2. Verify region: ap-northeast-1 (Tokyo)
  3. 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

  1. EC2 → Instances → find laravel-web-01
  2. Wait for Running state
  3. 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

  1. EC2 → Instances → laravel-web-01 → Status checks: both ✅ passed
  2. Monitoring tab: CPU, Network, Disk I/O

Series Navigation:

Comments