6 mins read

Deployment Guide: RILT Stack, Nginx, PostgreSQL on Ubuntu

A practical production ready Laravel deployment guide using InertiaJS, React, TailwindCSS, PostgreSQL, Nginx on Ubuntu.

Deployment Guide: RILT Stack, Nginx, PostgreSQL on Ubuntu

Modern Laravel applications frequently combine a powerful backend framework with a reactive frontend interface. A common and practical stack today is Laravel + InertiaJS + React with TailwindCSS, backed by PostgreSQL and deployed using Nginx on an Ubuntu server.

This guide documents a practical production deployment workflow for this stack. The goal is to keep the setup simple, maintainable, and secure, which is especially important for small development teams or internal business systems.

The guide assumes:

  • Ubuntu 22.04 LTS or newer
  • A Laravel project using InertiaJS + React
  • Vite as the frontend build tool
  • Access to a domain name
  • A fresh server with sudo privileges

Architecture Overviewh2

A typical deployment architecture for this stack looks like the following:

User Browser
NGINX
PHP-FPM (Laravel)
PostgreSQL

Frontend assets are compiled during deployment:

React + Tailwind
Vite Build
Optimized Static Assets
Served by Nginx

This is a standard LEMP architectureh2

  • Nginx efficiently serves static assets and proxies PHP requests.
  • PHP-FPM executes Laravel application logic.
  • PostgreSQL handles reliable relational data storage.
  • Vite compiles and optimizes frontend assets.

This architecture is stable, well-supported, and easy to maintain.


Server Requirementsh2

Before deploying, the server should have the following components:

  • Ubuntu 22.04+
  • Nginx
  • PHP 8.4 (FPM)
  • PostgreSQL
  • Node.js
  • Composer
  • Git
  • Supervisor
  • Certbot

1. Initial Server Setuph2

First update the system to ensure all packages are current.

Terminal window
sudo apt update

This command refreshes the package list from Ubuntu repositories.

Next upgrade installed packages:

Terminal window
sudo apt upgrade -y

The -y flag automatically confirms installation prompts.

Tiph3

Running updates regularly helps avoid security vulnerabilities and dependency conflicts.


2. Install Basic Utilitiesh2

Install common tools required for deployment.

Terminal window
sudo apt install nginx git unzip curl software-properties-common -y

Explanation of each package:

PackagePurpose
nginxWeb server
gitPull project code from repository
unzipExtract archives
curlDownload remote files
software-properties-commonManage repositories

Verify nginx installation:

Terminal window
nginx -v

Running deployments as root is not recommended.

Create a new user:

Terminal window
sudo adduser deploy

This creates a dedicated deployment user.

Grant sudo privileges:

Terminal window
sudo usermod -aG sudo deploy
  • -aG appends the user to a group
  • sudo allows administrative privileges

Switch to the new user:

Terminal window
su - deploy

4. Install PHP 8.4h2

Ubuntu repositories may not always include the latest PHP version. We use the Ondřej Surý repository, which maintains modern PHP builds.

Add the repository:

Terminal window
sudo add-apt-repository ppa:ondrej/php

Update package lists:

Terminal window
sudo apt update

Install PHP 8.4 and Laravel extensions:

Terminal window
sudo apt install php8.4 php8.4-fpm php8.4-pgsql php8.4-mbstring php8.4-xml php8.4-bcmath php8.4-curl php8.4-zip php8.4-gd php8.4-cli -y

Explanation of Important Extensionsh3

ExtensionPurpose
php-fpmExecutes PHP scripts
pgsqlPostgreSQL database driver
mbstringMultibyte string handling
xmlXML parsing
bcmathArbitrary precision math
curlHTTP requests
zipArchive management

Verify installation:

Terminal window
php -v

PHP-FPM runs PHP as a service and handles concurrent requests.


5. Install Composerh2

Composer manages PHP dependencies for Laravel.

Download Composer installer:

Terminal window
curl -sS https://getcomposer.org/installer | php

Move it globally:

Terminal window
sudo mv composer.phar /usr/local/bin/composer

Verify installation:

Terminal window
composer --version

Tip:

Using Composer globally allows running:

Terminal window
composer install

from any project directory.


6. Install Node.jsh2

Laravel uses Node.js to build frontend assets.

Install Node.js 20:

Terminal window
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -

Install Node:

Terminal window
sudo apt install nodejs -y

Verify installation:

Terminal window
node -v
npm -v

7. Install PostgreSQLh2

Install PostgreSQL server:

Terminal window
sudo apt install postgresql postgresql-contrib -y

Switch to postgres user:

Terminal window
sudo -u postgres psql

Create database:

CREATE DATABASE <app_database>;

change <app_database> to your database name

Create user:

CREATE USER <app_user> WITH PASSWORD <'strongpassword'>;

change <app_user> to your database user name and <‘strongpassword’> to your database password

Grant privileges:

GRANT ALL PRIVILEGES ON DATABASE <app_database> TO <app_user>;

Exit PostgreSQL:

Terminal window
\q

Tip:

Use strong passwords and avoid using the default postgres user for applications.


8. Deploy the Laravel Applicationh2

Move to web directory:

Terminal window
cd /var/www

Clone repository:

Terminal window
sudo git clone https://github.com/your-repository/project.git <app>

Enter project folder:

Terminal window
cd <app>

change <app> to your application name

Install PHP dependencies:

Terminal window
composer install --no-dev --optimize-autoloader

Explanation:

FlagPurpose
—no-devSkip development dependencies
—optimize-autoloaderImprove autoload performance

Create environment file:

Terminal window
cp .env.example .env

Generate application key:

Terminal window
php artisan key:generate

Configure database:

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_DATABASE=<app_database>
DB_USERNAME=<app_user>
DB_PASSWORD=<strongpassword>

Run database migrations:

Terminal window
php artisan migrate --force

--force allows migrations to run in production.


9. Install Frontend Dependenciesh2

Install npm packages:

Terminal window
npm install

Build production assets:

Terminal window
npm run build

This step compiles:

  • React components
  • TailwindCSS styles
  • JavaScript assets

Vite outputs optimized files to:

Terminal window
public/build

Tip:

Always run npm run build on production servers.

Never use npm run dev in production.


10. Set File Permissionsh2

Laravel requires write access to specific directories. Avoid assigning full ownership to www-data.

Set ownership:

Terminal window
sudo chown -R root:root /var/www/<app>
sudo chown -R www-data:www-data storage bootstrap/cache

Set permissions:

Terminal window
sudo chmod -R 775 storage bootstrap/cache

Explanation:

DirectoryPurpose
storagelogs, cache, sessions
bootstrap/cacheframework cache

11. Configure Nginxh2

Create site configuration:

Terminal window
sudo nano /etc/nginx/sites-available/<app>

This is vhost configuration so you can create multiple systems on the same server. Just repeat this step for each system.

Example configuration:

server {
listen 80;
server_name example.com www.example.com;
root /var/www/<app>/public;
index index.php index.html;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
# basic security restrictions
location ~ /\. { deny all; }
location ~* \.(env|log|sql)$ { deny all; }
}

Note: This is a basic configuration. You may need to adjust it based on your specific needs.

Enable vhost:

Terminal window
sudo ln -s /etc/nginx/sites-available/<app> /etc/nginx/sites-enabled/

Test configuration:

Terminal window
sudo nginx -t

Restart nginx:

Terminal window
sudo systemctl restart nginx

12. Configure Firewall (UFW)h2

Ubuntu comes with UFW (Uncomplicated Firewall), which is enough for most setups.

The idea is simple: only allow what is needed, block everything else.


Enable Required Portsh2

Allow SSH first (important, or you can lock yourself out):

Terminal window
sudo ufw allow OpenSSH

Allow HTTP and HTTPS:

Terminal window
sudo ufw allow 'Nginx Full'

Explanation:

  • OpenSSH → allows remote server access (port 22)
  • Nginx Full → allows port 80 (HTTP) and 443 (HTTPS)

Enable Firewallh2

Terminal window
sudo ufw enable

This activates the firewall.

Check status:

Terminal window
sudo ufw status

Expected output should show:

  • OpenSSH → ALLOW
  • Nginx Full → ALLOW

Instead of allowing SSH from anywhere:

Terminal window
sudo ufw allow from YOUR_IP_ADDRESS to any port 22

Then remove the default open rule:

Terminal window
sudo ufw delete allow OpenSSH

This limits SSH access to a specific IP.


Notesh2

  • Do not enable UFW without allowing SSH first
  • Always double-check rules before enabling
  • Firewall helps reduce attack surface, especially for public servers

For internal/on-prem servers, this is still useful to prevent unnecessary access between networks.


12. Point Domain to Serverh2

Add an A Record in your DNS provider.

Example:

TypeNameValue
A@YOUR_PUBLIC_IP

Optional:

TypeNameValue
AwwwYOUR_PUBLIC_IP

DNS propagation usually takes 5 minutes to several hours.


13. Install HTTPS (Let’s Encrypt)h2

Install certbot:

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

Generate SSL certificate:

Terminal window
sudo certbot --nginx -d example.com -d www.example.com

Certbot automatically:

  • requests certificates
  • updates nginx configuration
  • enables HTTPS redirect

Test renewal:

Terminal window
sudo certbot renew --dry-run

Certificates renew automatically every 90 days.


14. Configure Laravel Schedulerh2

Open crontab:

Terminal window
crontab -e

Add scheduler entry:

* * * * * cd /var/www/<app> && php artisan schedule:run >> /dev/null 2>&1

This executes scheduled tasks every minute.

Laravel internally determines which tasks should run.


15. Configure Queue Workers with Supervisorh2

Install Supervisor:

Terminal window
sudo apt install supervisor -y

Create worker config:

Terminal window
sudo nano /etc/supervisor/conf.d/<app>-worker.conf

Example configuration:

[program:<app>-worker]
command=php /var/www/<app>/artisan queue:work --sleep=3 --tries=3 --timeout=90
process_name=%(program_name)s_%(process_num)02d
numprocs=2
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/<app>/storage/logs/worker.log

Save and exit.

Reload supervisor:

Terminal window
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start <app>-worker:*

Make sure you replace <app> with your application name.

Check status:

Terminal window
sudo supervisorctl status

Basic Security Notesh2

  • restrict write access to storage and bootstrap/cache only
  • deny access to .env, logs, and hidden files
  • validate file uploads in application level

Optional (php.ini):

disable_functions = exec,passthru,shell_exec,system

Deployment Checklisth2

Before marking deployment complete, verify:

  • Environment variables configured
  • Database connected
  • Migrations executed
  • Assets built
  • Permissions set
  • Nginx working
  • HTTPS enabled
  • Cron scheduler running
  • Queue workers active

Final Thoughtsh2

Deploying a Laravel + InertiaJS + React application on Ubuntu does not require complex infrastructure. With Nginx, PHP-FPM, PostgreSQL, and a few supporting services, it is possible to run reliable production systems using a setup that is straightforward and proven.

This setup is not complex, but it is reliable.

For internal systems and small teams, this approach works well because it is:

  • predictable
  • easy to debug
  • easy to maintain

Most environments, especially on-premise or VM-based, do not need overly engineered solutions. A clean LEMP setup with proper configuration is already enough to handle real workloads when the application is built correctly.

The goal is not to build the most advanced infrastructure, but to have a system that runs consistently, is easy to understand, and can be maintained without unnecessary complexity.