Skip to main content

Command Palette

Search for a command to run...

I Deployed React + Laravel + MySQL on One Server

Updated
4 min read

I recently deployed a full project:

  • React (frontend)

  • Laravel (API)

  • MySQL (database)

All on a single AWS server.

If you search online, everything looks clean and easy. In reality, it’s not. Small mistakes waste hours.

So instead of pretending it’s smooth, I’m just going to walk through what actually matters — setup, mistakes, and what you should do differently.


What I Wanted

Simple goal:

  • Open domain → React loads

  • Call /api → Laravel responds

  • Laravel → talks to MySQL

That’s it.


Basic Setup (Server)

I used an EC2 instance from Amazon Web Services.

First thing I installed:

sudo apt update
sudo apt install nginx git unzip curl -y

Nothing fancy here.


Node Setup (Don’t Use apt)

I first installed Node using apt.

Bad idea.

Version was outdated and caused weird issues in React.

So I switched to Node Version Manager:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install node

After that, React build worked properly.


React Setup

cd /var/www/html
git clone <your-react-repo> react-project
cd react-project

npm install
npm run build

This gives a dist/ folder — that’s what Nginx will serve.


Laravel Setup

cd /var/www/html
git clone <your-laravel-repo> laravel-api
cd laravel-api

composer install
cp .env.example .env
php artisan key:generate

At this point, Laravel exists but doesn’t fully work yet.


MySQL Setup

Install MySQL:

sudo apt install mysql-server -y

Secure it:

sudo mysql_secure_installation

Then create DB + user:

CREATE DATABASE laravel_api;

CREATE USER 'madin'@'localhost' IDENTIFIED BY 'StrongPassword123!';

GRANT ALL PRIVILEGES ON laravel_api.* TO 'madin'@'localhost';

FLUSH PRIVILEGES;

Connect Laravel to Database

Edit .env:

DB_DATABASE=laravel_api
DB_USERNAME=madin
DB_PASSWORD=StrongPassword123!

Run:

php artisan migrate

If this fails, don’t guess — check credentials.


The Biggest Confusion — Laravel “Working” But Not Really

I ran:

php artisan serve

API worked.

So I assumed backend is done.

Wrong.

That command is just for local testing. Nginx doesn’t use it.

Real fix was installing PHP-FPM:

php -v
sudo apt install php8.3-fpm

Without this, Laravel won’t work with Nginx at all.


Nginx Setup (Where Most Bugs Come From)

Created config:

server {
    listen 80;
    server_name madinapp.duckdns.org;

    root /var/www/html/react-project/dist;
    index index.html;

    location / {
        try_files $uri /index.html;
    }

    location ^~ /api/ {
        root /var/www/html/laravel-api/public;
        index index.php;
        try_files \(uri \)uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        root /var/www/html/laravel-api/public;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME \(document_root\)fastcgi_script_name;
    }
}

This part took the most time to get right.


Mistakes That Cost Me Time

1. API returning React instead of JSON

Cause: wrong Nginx routing Fix: use ^~ /api/


2. Laravel not responding

Cause: PHP-FPM not installed Fix: install correct version


3. Node issues

Cause: installed via apt Fix: use NVM


4. Domain not opening

Cause: DNS not pointing correctly Used DuckDNS


5. “Connection refused”

Cause: port 80 not open Fix: allow in AWS security group


SSL Setup

Used Let's Encrypt:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx

Also opened:

  • port 80

  • port 443


Final Setup Flow (Simple View)

Browser
   ↓
Nginx
   ↓
Laravel (PHP-FPM)
   ↓
MySQL

One Thing I Got Wrong Initially

I thought since both apps are on same server, API calls are internal.

They’re not.

Browser still sends request → server → response So yes, it uses bandwidth.


Final Thoughts

This setup works well for:

  • learning

  • small projects

  • personal apps

But the tricky part is not React or Laravel.

It’s understanding how everything connects.

If you skip that and just copy configs, you’ll get stuck.


That’s it.

No perfect tutorial — just what actually worked after fixing mistakes.