STEP 01Prerequisites
Before starting, make sure you have:
- An AWS account with access to the EC2 dashboard
- A domain name pointed to your server (optional, but needed for SSL)
- Your Quasar + Laravel project pushed to a Git repository (GitHub, GitLab, etc.)
- A local SSH key pair or the ability to generate one
STEP 02Launch EC2 Instance
2.1 Choose Your Instance
Go to the EC2 Dashboard → Launch Instance:
- AMI: Ubuntu Server 22.04 LTS (HVM)
-
Instance type:
t2.small(minimum for Laravel + MySQL) ort3.mediumfor comfort - Key pair: Create or select an existing one
- VPC: Use the default VPC
2.2 Security Group Rules
| Type | Port | Source | Purpose |
|---|---|---|---|
| SSH | 22 | Your IP | Server access |
| HTTP | 80 | 0.0.0.0/0 | Web traffic |
| HTTPS | 443 | 0.0.0.0/0 | Secure traffic |
2.3 Connect to Your Instance
# Set permissions on your key chmod 400 your-key.pem # SSH into the instance ssh -i "your-key.pem" ubuntu@<your-ec2-public-ip>
STEP 03Install Dependencies
Update the system and install everything we need in one go:
# Update system packages sudo apt update && sudo apt upgrade -y # Install Nginx, Git, Curl, Unzip sudo apt install -y nginx git curl unzip software-properties-common # Add PHP 8.2 repository sudo add-apt-repository ppa:ondrej/php -y sudo apt update # Install PHP 8.2 + extensions for Laravel sudo apt install -y php8.2-fpm php8.2-mysql php8.2-mbstring \ php8.2-xml php8.2-curl php8.2-zip php8.2-bcmath php8.2-gd # Install Composer curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer # Install Node.js 20 (for building Quasar) curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - sudo apt install -y nodejs
php -v,
composer --version, node -v,
nginx -v
STEP 04Set Up MySQL
# Install MySQL 8 sudo apt install -y mysql-server # Secure the installation sudo mysql_secure_installation # Create database and user sudo mysql CREATE DATABASE myapp_db; CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPassword123!'; GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost'; FLUSH PRIVILEGES; EXIT;
StrongPassword123! with an actual secure
password. Never use this in production!
STEP 05Deploy Laravel Backend
# Clone your project cd /var/www sudo git clone https://github.com/you/your-project.git myapp cd myapp/backend # or wherever your Laravel code lives # Set ownership sudo chown -R www-data:www-data /var/www/myapp sudo chmod -R 775 storage bootstrap/cache # Install dependencies composer install --no-dev --optimize-autoloader # Configure environment cp .env.example .env nano .env
5.1 Configure .env
APP_ENV=production APP_DEBUG=false APP_URL=https://yourdomain.com DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=myapp_db DB_USERNAME=myapp_user DB_PASSWORD=StrongPassword123!
# Generate app key and run migrations php artisan key:generate php artisan migrate --force php artisan config:cache php artisan route:cache php artisan view:cache
STEP 06Deploy Quasar Frontend
You can either build locally and upload, or build on the server:
# Navigate to frontend directory cd /var/www/myapp/frontend # Install dependencies npm install # Build for production (SPA mode) npx quasar build # The build output goes to dist/spa/ # We'll point Nginx here
/api (relative) so Nginx can proxy it to Laravel. Update
quasar.config.js or your axios config accordingly.
STEP 07Configure Nginx
This is the crucial part — Nginx serves the Quasar SPA and proxies API requests to Laravel via PHP-FPM:
server { listen 80; server_name yourdomain.com; # Quasar SPA (frontend) root /var/www/myapp/frontend/dist/spa; index index.html; # SPA fallback — all routes go to index.html location / { try_files $uri $uri/ /index.html; } # Laravel API — proxy /api requests to PHP-FPM location /api { alias /var/www/myapp/backend/public; try_files $uri $uri/ @laravel; location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $request_filename; } } location @laravel { rewrite /api/(.*)$ /api/index.php?/$1 last; } # Security headers add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; # Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript; }
# Enable the site sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ # Remove default site sudo rm /etc/nginx/sites-enabled/default # Test config and reload sudo nginx -t sudo systemctl reload nginx
STEP 08SSL with Let's Encrypt
# Install Certbot sudo apt install -y certbot python3-certbot-nginx # Get SSL certificate sudo certbot --nginx -d yourdomain.com # Auto-renewal is set up automatically # Test it with: sudo certbot renew --dry-run
STEP 09Troubleshooting
Common Issues
500 Internal Server Error
Check Laravel logs:
tail -f /var/www/myapp/backend/storage/logs/laravel.log
Usually caused by missing .env, wrong permissions, or
missing PHP extensions.
403 Forbidden
Fix ownership:
sudo chown -R www-data:www-data /var/www/myapp
API returns 404
Check Nginx config — make sure the /api location block is
correct and nginx -t passes.
Quasar routes show blank page
Ensure the try_files directive falls back to
/index.html for SPA routing.
Database connection refused
Verify MySQL is running: sudo systemctl status mysql.
Check .env credentials match what you created in Step 4.
/api/* should hit Laravel.