Skip to content

Instantly share code, notes, and snippets.

@abubaker417
Last active January 29, 2026 19:42
Show Gist options
  • Select an option

  • Save abubaker417/ad5a08f165c3449f65c2916a415994ce to your computer and use it in GitHub Desktop.

Select an option

Save abubaker417/ad5a08f165c3449f65c2916a415994ce to your computer and use it in GitHub Desktop.

AWS Production Deployment Guide for Laravel with Docker 🚀

Complete Step-by-Step Setup

This guide will help you deploy your Laravel project with Docker on AWS using production-level security practices.


Overview - What We'll Build

┌──────────────────────────────────────────────────────────────┐
│                  CUSTOM VPC: 10.0.0.0/16                     │
│                                                              │
│  ┌────────────────────┐         ┌────────────────────┐     │
│  │  PUBLIC SUBNET     │         │  PRIVATE SUBNET    │     │
│  │  10.0.1.0/24      │         │  10.0.11.0/24     │     │
│  │                    │         │                    │     │
│  │  [IGW]            │         │  [EC2 Instance]   │     │
│  │  [NAT Gateway]    │         │  Your Laravel App  │     │
│  │  [ALB]            │────────>│  with Docker       │     │
│  │                    │         │  No Public IP      │     │
│  │  [Instance Connect│         │                    │     │
│  │   Endpoint]       │────────>│  SSH Access        │     │
│  │                    │         │                    │     │
│  └────────────────────┘         └────────┬───────────┘     │
│         ↑                                │                  │
│         │                                │                  │
│         │                                ↓                  │
│         │                      ┌────────────────────┐      │
│         │                      │  PRIVATE SUBNET    │      │
│         │                      │  10.0.12.0/24     │      │
│         │                      │                    │      │
│         │                      │  [RDS MySQL]       │      │
│         │                      │  No Public IP      │      │
│         │                      │                    │      │
│         │                      └────────────────────┘      │
│         │                                                   │
└─────────┼───────────────────────────────────────────────────┘
          │
    INTERNET (Users)

Phase 1: Create VPC Infrastructure

Step 1: Create VPC

  1. Go to VPC DashboardYour VPCsCreate VPC

  2. Settings:

    Name: laravel-prod-vpc
    IPv4 CIDR: 10.0.0.0/16
    IPv6 CIDR: No IPv6
    Tenancy: Default
    
  3. Click Create VPC

  4. Note the VPC ID: vpc-xxxxx (you'll need this!)


Step 2: Create Subnets

Public Subnet (for ALB, NAT, Instance Connect Endpoint)

  1. Go to SubnetsCreate subnet

  2. Settings:

    VPC: laravel-prod-vpc
    Subnet name: public-subnet
    Availability Zone: us-east-1a (or your preferred AZ)
    IPv4 CIDR: 10.0.1.0/24
    
  3. Click Create subnet

  4. Select the subnet → ActionsEdit subnet settings

    • ✅ Enable Auto-assign public IPv4 address
    • Click Save

Private Subnet 1 (for EC2 Instance)

  1. Create subnet

  2. Settings:

    VPC: laravel-prod-vpc
    Subnet name: private-subnet-app
    Availability Zone: us-east-1a
    IPv4 CIDR: 10.0.11.0/24
    
  3. Click Create subnet

  4. DO NOT enable auto-assign public IP

Private Subnet 2 (for RDS)

  1. Create subnet

  2. Settings:

    VPC: laravel-prod-vpc
    Subnet name: private-subnet-db
    Availability Zone: us-east-1b (different AZ for RDS!)
    IPv4 CIDR: 10.0.12.0/24
    
  3. Click Create subnet

Why 2 AZs for RDS? RDS requires subnets in at least 2 different Availability Zones for high availability.


Step 3: Create Internet Gateway

  1. Go to Internet GatewaysCreate internet gateway

  2. Settings:

    Name: laravel-igw
    
  3. Click Create internet gateway

  4. Select it → ActionsAttach to VPC

    • Select: laravel-prod-vpc
    • Click Attach internet gateway

Step 4: Create NAT Gateway

  1. Go to NAT GatewaysCreate NAT gateway

  2. Settings:

    Name: laravel-nat
    Subnet: public-subnet (MUST be public!)
    Connectivity type: Public
    Elastic IP allocation ID: Click "Allocate Elastic IP"
    
  3. Click Create NAT gateway

  4. Wait 2-3 minutes for it to become "Available"

Cost Note: NAT Gateway costs ~$32/month + data transfer charges


Step 5: Create Route Tables

Public Route Table

  1. Go to Route TablesCreate route table

  2. Settings:

    Name: public-rt
    VPC: laravel-prod-vpc
    
  3. Click Create route table

  4. Select it → Routes tab → Edit routes

    • Click Add route

      Destination: 0.0.0.0/0
      Target: Internet Gateway → laravel-igw
      
    • Click Save changes

  5. Subnet Associations tab → Edit subnet associations

    • ✅ Select public-subnet
    • Click Save associations

Private Route Table

  1. Create route table

  2. Settings:

    Name: private-rt
    VPC: laravel-prod-vpc
    
  3. Click Create route table

  4. Select it → Routes tab → Edit routes

    • Click Add route

      Destination: 0.0.0.0/0
      Target: NAT Gateway → laravel-nat
      
    • Click Save changes

  5. Subnet Associations tab → Edit subnet associations

    • ✅ Select private-subnet-app
    • ✅ Select private-subnet-db
    • Click Save associations

Phase 2: Create Security Groups

Step 1: ALB Security Group

  1. Go to EC2Security GroupsCreate security group

  2. Settings:

    Name: alb-sg
    Description: Security group for Application Load Balancer
    VPC: laravel-prod-vpc
    
  3. Inbound Rules:

    Rule 1:
    Type: HTTP
    Port: 80
    Source: 0.0.0.0/0
    Description: Allow HTTP from internet
    
    Rule 2:
    Type: HTTPS
    Port: 443
    Source: 0.0.0.0/0
    Description: Allow HTTPS from internet
    
  4. Outbound Rules: (default - allow all)

  5. Click Create security group

  6. Note the Security Group ID: sg-alb-xxxxx


Step 2: EC2 Instance Security Group

  1. Create security group

  2. Settings:

    Name: ec2-sg
    Description: Security group for Laravel EC2 instance
    VPC: laravel-prod-vpc
    
  3. Inbound Rules:

    Rule 1:
    Type: HTTP
    Port: 80
    Source: Custom → sg-alb-xxxxx (ALB security group)
    Description: Allow HTTP from ALB only
    
    Rule 2:
    Type: Custom TCP
    Port: 443
    Source: Custom → sg-alb-xxxxx
    Description: Allow HTTPS from ALB only
    
  4. Outbound Rules: (default - allow all)

    • This allows EC2 to:
      • Connect to internet via NAT (for downloading packages)
      • Connect to RDS database
  5. Click Create security group

  6. Note the Security Group ID: sg-ec2-xxxxx


Step 3: Instance Connect Endpoint Security Group

  1. Create security group

  2. Settings:

    Name: endpoint-sg
    Description: Security group for EC2 Instance Connect Endpoint
    VPC: laravel-prod-vpc
    
  3. Inbound Rules:

    Rule 1:
    Type: SSH
    Port: 22
    Source: My IP (or your specific IP: x.x.x.x/32)
    Description: Allow SSH from my IP only
    
  4. Outbound Rules:

    Rule 1:
    Type: SSH
    Port: 22
    Destination: Custom → sg-ec2-xxxxx (EC2 security group)
    Description: Allow SSH to EC2 instances
    
  5. Click Create security group


Step 4: Update EC2 Security Group (Add SSH Rule)

  1. Go back to ec2-sg security group

  2. Edit inbound rulesAdd rule

    Type: SSH
    Port: 22
    Source: Custom → sg-endpoint-xxxxx (Endpoint security group)
    Description: Allow SSH from Instance Connect Endpoint
    
  3. Click Save rules


Step 5: RDS Security Group

  1. Create security group

  2. Settings:

    Name: rds-sg
    Description: Security group for RDS MySQL database
    VPC: laravel-prod-vpc
    
  3. Inbound Rules:

    Rule 1:
    Type: MYSQL/Aurora
    Port: 3306
    Source: Custom → sg-ec2-xxxxx (EC2 security group)
    Description: Allow MySQL from EC2 only
    
  4. Outbound Rules: (not needed)

  5. Click Create security group


Phase 3: Create EC2 Instance Connect Endpoint

  1. Go to VPCEndpointsCreate endpoint

  2. Settings:

    Name: laravel-connect-endpoint
    Service category: EC2 Instance Connect Endpoint
    VPC: laravel-prod-vpc
    Security groups: endpoint-sg
    Subnet: public-subnet
    
  3. Click Create endpoint

  4. Wait for status to become "Available"


Phase 4: Create RDS Database

Step 1: Create DB Subnet Group

  1. Go to RDSSubnet groupsCreate DB subnet group

  2. Settings:

    Name: laravel-db-subnet-group
    Description: Subnet group for Laravel RDS
    VPC: laravel-prod-vpc
    
    Availability Zones:
    - us-east-1a
    - us-east-1b
    
    Subnets:
    - private-subnet-app (10.0.11.0/24)
    - private-subnet-db (10.0.12.0/24)
    
  3. Click Create


Step 2: Create RDS MySQL Instance

  1. Go to RDSDatabasesCreate database

  2. Settings:

    Engine: MySQL
    Version: 8.0.x (latest)
    Templates: Production (or Dev/Test for lower cost)
    
    DB instance identifier: laravel-prod-db
    Master username: admin
    Master password: [Create strong password]
    
    Instance configuration:
    - Burstable classes (for lower cost)
    - db.t3.micro (free tier) or db.t3.small
    
    Storage:
    - Storage type: General Purpose (SSD)
    - Allocated storage: 20 GB
    - Storage autoscaling: Enable (max 100 GB)
    
    Connectivity:
    - VPC: laravel-prod-vpc
    - DB subnet group: laravel-db-subnet-group
    - Public access: NO ❌
    - VPC security group: Choose existing → rds-sg
    
    Database authentication:
    - Password authentication
    
    Additional configuration:
    - Initial database name: laravel
    - Backup retention: 7 days
    - Delete protection: Enable (for production)
    
  3. Click Create database

  4. Wait 5-10 minutes for creation

  5. Once available, note:

    • Endpoint: laravel-prod-db.xxxxx.us-east-1.rds.amazonaws.com
    • Port: 3306

Phase 5: Launch EC2 Instance

Step 1: Launch Instance

  1. Go to EC2InstancesLaunch instances

  2. Settings:

    Name: laravel-prod-app
    
    AMI: Ubuntu Server 24.04 LTS (or Amazon Linux 2023)
    
    Instance type: t3.small (recommended minimum for Laravel)
    
    Key pair: Create new key pair
    - Name: laravel-prod-key
    - Type: RSA
    - Format: .pem
    - Download and save securely!
    
    Network settings:
    - VPC: laravel-prod-vpc
    - Subnet: private-subnet-app
    - Auto-assign public IP: DISABLE ❌
    - Security group: ec2-sg
    
    Storage: 20 GB gp3 (or 30 GB if you need more space)
    
  3. Advanced detailsUser data (optional - for basic setup):

    #!/bin/bash
    apt update -y
    apt upgrade -y
    
    # Install basic tools
    apt install -y git curl wget nano
  4. Click Launch instance

  5. Note the Instance ID: i-xxxxx


Phase 6: Create Application Load Balancer

Step 1: Create Target Group

  1. Go to EC2Target GroupsCreate target group

  2. Settings:

    Target type: Instances
    Target group name: laravel-tg
    Protocol: HTTP
    Port: 80
    VPC: laravel-prod-vpc
    
    Health check:
    - Protocol: HTTP
    - Path: / (or /health if you have a health endpoint)
    - Healthy threshold: 2
    - Unhealthy threshold: 2
    - Timeout: 5 seconds
    - Interval: 30 seconds
    
  3. Click Next

  4. Register targets:

    • Select your laravel-prod-app instance
    • Port: 80
    • Click Include as pending below
  5. Click Create target group


Step 2: Create Application Load Balancer

  1. Go to EC2Load BalancersCreate load balancer

  2. Select Application Load Balancer

  3. Settings:

    Name: laravel-alb
    Scheme: Internet-facing
    IP address type: IPv4
    
    Network mapping:
    - VPC: laravel-prod-vpc
    - Mappings:
      ✅ us-east-1a → public-subnet
      ✅ us-east-1b → (you need another public subnet in different AZ)
    
    Security groups: alb-sg
    
    Listeners:
    - Protocol: HTTP
    - Port: 80
    - Default action: Forward to → laravel-tg
    
  4. Click Create load balancer

  5. Wait 2-3 minutes for provisioning

  6. Once active, note:

    • DNS name: laravel-alb-xxxxx.us-east-1.elb.amazonaws.com

Important: ALB requires subnets in at least 2 Availability Zones. You'll need to create another public subnet in a different AZ (e.g., us-east-1b).


Step 2b: Create Second Public Subnet (if needed)

  1. Go to VPCSubnetsCreate subnet

  2. Settings:

    VPC: laravel-prod-vpc
    Subnet name: public-subnet-2
    Availability Zone: us-east-1b
    IPv4 CIDR: 10.0.2.0/24
    
  3. Enable Auto-assign public IPv4 address

  4. Associate with public-rt route table

  5. Go back to ALB settings and add this subnet


Phase 7: Connect and Setup Laravel

Step 1: Connect via Instance Connect Endpoint

Option A: AWS Console (Easiest)

  1. Go to EC2Instances
  2. Select laravel-prod-app
  3. Click Connect
  4. Select EC2 Instance Connect Endpoint
  5. Choose your endpoint
  6. Click Connect

Option B: AWS CLI

# Install AWS CLI if not already installed
# Configure AWS credentials
aws configure

# Connect to instance
aws ec2-instance-connect ssh \
  --instance-id i-xxxxx \
  --connection-type eice

Step 2: Install Docker and Docker Compose

Once connected to your instance:

# Update system
sudo apt update && sudo apt upgrade -y

# Install Docker
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker

# Add ubuntu user to docker group
sudo usermod -aG docker $USER

# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Verify installations
docker --version
docker-compose --version

# Log out and back in for group changes to take effect
exit

Then reconnect to the instance.


Step 3: Install Git and Clone Your Project

# Install Git
sudo apt install -y git

# Configure Git (optional)
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

# Create project directory
mkdir -p /home/ubuntu/laravel-app
cd /home/ubuntu/laravel-app

# Clone your repository
# Option 1: HTTPS (will need credentials)
git clone https://github.com/yourusername/your-laravel-project.git .

# Option 2: SSH (recommended - set up SSH key first)
# Generate SSH key on EC2
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub
# Copy the output and add to GitHub: Settings → SSH Keys

# Then clone
git clone [email protected]:yourusername/your-laravel-project.git .

Step 4: Configure Environment Variables

# Copy example env file
cp .env.example .env

# Edit .env file
nano .env

Update these values:

APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://yourdomain.com

DB_CONNECTION=mysql
DB_HOST=laravel-prod-db.xxxxx.us-east-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=admin
DB_PASSWORD=your_rds_password

CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

Save and exit (Ctrl+X, then Y, then Enter)


If you want Directly nginx configuration on Server then use this

server {
    listen 80;
    server_name devopslab.info;
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Step 5: Verify Docker Compose Configuration

Example docker-compose.yml for Laravel:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: laravel-app
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - .:/var/www
      - ./storage:/var/www/storage
      - ./bootstrap/cache:/var/www/bootstrap/cache
    networks:
      - laravel
    depends_on:
      - redis

  nginx:
    image: nginx:alpine
    container_name: laravel-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - .:/var/www
      - ./docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    networks:
      - laravel
    depends_on:
      - app

  redis:
    image: redis:alpine
    container_name: laravel-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

Make sure your nginx configuration forwards to your app correctly!


Step 6: Build and Start Docker Containers

# Make sure you're in the project directory
cd /home/ubuntu/laravel-app

# Build and start containers
docker-compose up -d --build

# Check running containers
docker-compose ps

# View logs
docker-compose logs -f

# If everything looks good, continue

Step 7: Install Laravel Dependencies and Setup

# Access app container
docker-compose exec app bash

# Inside the container:

# Install Composer dependencies
composer install --optimize-autoloader --no-dev

# Generate application key
php artisan key:generate

# Run migrations
php artisan migrate --force

# Seed database (if needed)
php artisan db:seed --force

# Clear and cache config
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Set proper permissions
chown -R www-data:www-data /var/www/storage
chown -R www-data:www-data /var/www/bootstrap/cache
chmod -R 775 /var/www/storage
chmod -R 775 /var/www/bootstrap/cache

# Exit container
exit

Step 8: Test Application Locally

# Test if app is responding on port 80
curl http://localhost

# You should see your Laravel app HTML response

Phase 8: Configure Domain

Step 1: Point Domain to ALB

  1. Go to your domain provider (GoDaddy, Namecheap, Route53, etc.)

  2. Add CNAME Record:

    Type: CNAME
    Name: www (or @ if supported)
    Value: laravel-alb-xxxxx.us-east-1.elb.amazonaws.com
    TTL: 300 (5 minutes)
    
  3. If using Route53:

    • Create ALIAS record instead (no CNAME needed for apex domain)
    • Type: A Record
    • Name: yourdomain.com
    • Alias: Yes
    • Alias Target: Select your ALB
  4. Wait 5-10 minutes for DNS propagation

  5. Test: http://yourdomain.com


Step 2: Add SSL Certificate (HTTPS)

  1. Go to AWS Certificate ManagerRequest certificate

  2. Settings:

    Certificate type: Public certificate
    Domain names:
    - yourdomain.com
    - *.yourdomain.com (wildcard for subdomains)
    
    Validation: DNS validation
    
  3. Click Request

  4. Add DNS records for validation:

    • Certificate Manager will show CNAME records
    • Add these to your domain provider's DNS
    • Wait for validation (5-30 minutes)
  5. Once validated, go to EC2Load Balancers → Select your ALB

  6. Listeners tab → Add listener

    Protocol: HTTPS
    Port: 443
    Default action: Forward to → laravel-tg
    Security policy: ELBSecurityPolicy-TLS13-1-2-2021-06 (recommended)
    SSL certificate: Select your certificate
    
  7. Click Save

  8. Update HTTP listener to redirect to HTTPS:

    • Edit HTTP:80 listener
    • Change action to: Redirect to HTTPS:443
    • Status code: 301 (permanent)
  9. Test: https://yourdomain.com


Phase 9: Setup CI/CD (Optional)

GitHub Actions Deployment

Create .github/workflows/deploy.yml in your repository:

name: Deploy to AWS

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      
      - name: Deploy to EC2
        env:
          INSTANCE_ID: ${{ secrets.INSTANCE_ID }}
        run: |
          # Connect via Instance Connect and deploy
          aws ec2-instance-connect send-ssh-public-key \
            --instance-id $INSTANCE_ID \
            --instance-os-user ubuntu \
            --ssh-public-key file://~/.ssh/id_rsa.pub
          
          ssh ubuntu@$INSTANCE_ID << 'EOF'
            cd /home/ubuntu/laravel-app
            git pull origin main
            docker-compose down
            docker-compose up -d --build
            docker-compose exec -T app composer install --optimize-autoloader --no-dev
            docker-compose exec -T app php artisan migrate --force
            docker-compose exec -T app php artisan config:cache
            docker-compose exec -T app php artisan route:cache
            docker-compose exec -T app php artisan view:cache
          EOF

GitHub Secrets to add:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • INSTANCE_ID (your EC2 instance ID)

Security Checklist ✅

VPC Security

  • ✅ Custom VPC created
  • ✅ Public and private subnets separated
  • ✅ Internet Gateway for public access
  • ✅ NAT Gateway for private instances
  • ✅ Route tables properly configured

EC2 Security

  • ✅ Instance in private subnet
  • ✅ No public IP assigned
  • ✅ Security group allows only ALB traffic
  • ✅ SSH only via Instance Connect Endpoint
  • ✅ Instance Connect Endpoint only from your IP

RDS Security

  • ✅ Database in private subnet
  • ✅ No public access
  • ✅ Security group allows only EC2
  • ✅ Strong master password
  • ✅ Automated backups enabled
  • ✅ Delete protection enabled

Application Security

  • .env file not in Git
  • ✅ APP_DEBUG=false in production
  • ✅ Strong APP_KEY generated
  • ✅ Database credentials secured
  • ✅ HTTPS enforced via ALB
  • ✅ Security headers configured

ALB Security

  • ✅ SSL certificate configured
  • ✅ HTTP redirects to HTTPS
  • ✅ Security group allows only HTTP/HTTPS
  • ✅ Health checks configured

Cost Estimate (Monthly)

Service Configuration Cost
EC2 t3.small On-Demand, 24/7 ~$15
RDS db.t3.micro 20GB storage ~$13
NAT Gateway + data transfer ~$32
ALB + LCU hours ~$16
Data Transfer ~100GB ~$9
EBS Storage 20GB gp3 ~$2
Total ~$87/month

Ways to reduce costs:

  • Use Savings Plans for EC2/RDS (save 30-50%)
  • Stop instances during off-hours (dev only)
  • Use Reserved Instances for production
  • Optimize NAT Gateway usage
  • Consider using VPC endpoints for AWS services

Troubleshooting Common Issues

Can't connect via Instance Connect Endpoint

Check:

  • Endpoint security group allows SSH from your IP
  • Endpoint is in public subnet
  • Endpoint outbound allows SSH to EC2 security group
  • EC2 security group allows SSH from endpoint security group

Fix:

# Verify endpoint exists
aws ec2 describe-instance-connect-endpoints

# Check your IP
curl ifconfig.me

Application not accessible via ALB

Check:

  • Target group health checks passing
  • EC2 instance docker containers running
  • Nginx listening on port 80
  • Security group allows ALB → EC2 traffic

Fix:

# Check target health
# AWS Console: EC2 → Target Groups → laravel-tg → Targets tab

# On EC2, check if port 80 is listening
sudo netstat -tlnp | grep :80

# Check docker containers
docker-compose ps

# Check nginx logs
docker-compose logs nginx

# Test locally
curl http://localhost

Can't connect to RDS from EC2

Check:

  • RDS security group allows EC2 security group
  • RDS endpoint is correct in .env
  • Database credentials are correct
  • RDS is in available state

Fix:

# Test connection from EC2
mysql -h laravel-prod-db.xxxxx.us-east-1.rds.amazonaws.com \
      -P 3306 \
      -u admin \
      -p

# If mysql command not found:
sudo apt install mysql-client -y

# Check .env file
cat .env | grep DB_

Docker containers keep restarting

Check:

# View container logs
docker-compose logs app
docker-compose logs nginx

# Check container status
docker-compose ps

# Restart containers
docker-compose down
docker-compose up -d

# Rebuild if needed
docker-compose up -d --build

Maintenance Tasks

Daily

  • Monitor CloudWatch metrics
  • Check application logs
  • Review security group rules

Weekly

  • Review RDS backups
  • Check disk space usage
  • Update Docker images
  • Review access logs

Monthly

  • Update system packages
  • Review AWS costs
  • Rotate credentials
  • Test backup restoration
  • Review security patches

Useful Commands

EC2 Instance Commands

# Connect via Instance Connect
aws ec2-instance-connect ssh --instance-id i-xxxxx

# Check system resources
htop
df -h
free -h

# View docker containers
docker-compose ps
docker-compose logs -f

# Restart application
docker-compose restart

# Update application
cd /home/ubuntu/laravel-app
git pull
docker-compose down
docker-compose up -d --build

Laravel Commands

# Access app container
docker-compose exec app bash

# Clear caches
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Run migrations
php artisan migrate

# Check queue workers
php artisan queue:work --daemon

# View logs
tail -f storage/logs/laravel.log

Next Steps

  1. Setup Monitoring:

    • Enable CloudWatch logs
    • Setup alerts for CPU/memory
    • Configure application monitoring
  2. Setup Backups:

    • Configure automated RDS snapshots
    • Setup EBS snapshots for EC2
    • Test backup restoration
  3. Optimize Performance:

    • Enable Redis for caching
    • Configure queue workers
    • Setup CDN (CloudFront)
    • Enable PHP OPcache
  4. Enhance Security:

    • Setup WAF (Web Application Firewall)
    • Configure AWS Shield
    • Enable GuardDuty
    • Setup AWS Config
  5. Setup Monitoring & Logging:

    • CloudWatch Logs for application logs
    • CloudWatch Metrics for performance
    • SNS alerts for critical events

Summary

You've successfully deployed a production-level Laravel application on AWS with:

Secure VPC with public/private subnets ✅ Private EC2 instance (no direct internet access) ✅ RDS MySQL in private subnet ✅ Application Load Balancer for public access ✅ Instance Connect Endpoint for secure SSH ✅ Docker containerization ✅ HTTPS with SSL certificate ✅ Proper security groups for all components

Your application is now:

  • Secure (private instances, proper security groups)
  • Scalable (can add more EC2 instances to ALB)
  • Highly available (multi-AZ RDS, ALB health checks)
  • Production-ready (HTTPS, proper monitoring)

🎉 Congratulations on your production deployment!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment