How to install Laravel on a Linux VPS (Debian 13 / Ubuntu 24.04)
Estimated Time: 30-45 minutes
Difficulty: Intermediate ⭐⭐
System: Debian 13 (Trixie) / Ubuntu 24.04 LTS
📋 Introduction
This guide explains how to install Laravel 12 (latest version) on a Linux VPS with Debian 13 or Ubuntu 24.04. By the end of this tutorial, you will have a production environment ready with:
- ✅ PHP 8.3 (required for Laravel 12)
- ✅ Composer (PHP dependency manager)
- ✅ Nginx (high-performance web server)
- ✅ MariaDB (database)
- ✅ Laravel 12 (PHP framework)
Prerequisites
| Prerequisite | Description |
|---|---|
| 🖥️ Linux VPS | Debian 13 or Ubuntu 24.04 with root access |
| 🔑 SSH Access | Root or sudo user connection |
| 🌐 Domain Name | (Optional) To access via URL |
| 💾 RAM | Minimum 1 GB recommended |
| 💿 Storage | Minimum 10 GB |
Required Configuration for Laravel 12
| Component | Minimum Version |
|---|---|
| PHP | 8.2 (8.3 or 8.4 recommended) |
| Composer | 2.x |
| Database | MySQL 5.7+ / MariaDB 10.3+ / PostgreSQL 10+ / SQLite 3.26+ |
🔄 Step 1: Update the System
Before any installation, update your system.
SSH Connection
ssh root@your-server-ip
Update Packages
# Update package list
apt update
# Upgrade installed packages
apt upgrade -y
✅ Expected Outcome: All packages are up to date.
🐘 Step 2: Install PHP 8.3
Laravel 12 requires a minimum of PHP 8.2. We will install PHP 8.3 for better performance.
For Debian 13
Debian 13 (Trixie) includes PHP 8.4 by default. To install PHP 8.3, add the Sury repository:
# Install dependencies
apt install -y apt-transport-https lsb-release ca-certificates curl gnupg
# Download the GPG key for the Sury repository
curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/sury-php.gpg
# Add the Sury repository
echo "deb [signed-by=/usr/share/keyrings/sury-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/sury-php.list
# Update package list
apt update
For Ubuntu 24.04
Ubuntu 24.04 includes PHP 8.3 by default. If you want a newer version, add the Ondřej PPA:
# Install dependencies
apt install -y software-properties-common
# Add PHP PPA (optional for PHP 8.4)
add-apt-repository ppa:ondrej/php -y
# Update package list
apt update
Install PHP 8.3 and required extensions
apt install -y php8.3 php8.3-fpm php8.3-cli php8.3-common \
php8.3-mysql php8.3-pgsql php8.3-sqlite3 \
php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml \
php8.3-zip php8.3-bcmath php8.3-intl php8.3-readline \
php8.3-tokenizer php8.3-opcache
| Extension | Purpose |
|---|---|
php8.3-fpm | FastCGI Process Manager (for Nginx) |
php8.3-mysql | MySQL/MariaDB connection |
php8.3-curl | HTTP requests |
php8.3-gd | Image manipulation |
php8.3-mbstring | Multibyte strings (UTF-8) |
php8.3-xml | XML processing |
php8.3-zip | ZIP compression |
php8.3-bcmath | Precision calculations |
php8.3-intl | Internationalization |
Check the Installation
php -v
Expected Outcome:
PHP 8.3.x (cli) (built: ...)
Copyright (c) The PHP Group
Zend Engine v4.3.x, Copyright (c) Zend Technologies
with Zend OPcache v8.3.x, Copyright (c), by Zend Technologies 📦 Step 3: Install Composer
Composer is the PHP dependency manager essential for Laravel.
Download and Install Composer
# Download the installer
curl -sS https://getcomposer.org/installer -o composer-setup.php
# Install Composer globally
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
# Remove the installer
rm composer-setup.php
Check the Installation
composer --version
Expected Outcome:
Composer version 2.x.x 2024-xx-xx xx:xx:xx 🌐 Step 4: Install Nginx
Nginx is a high-performance web server, ideal for Laravel.
Install Nginx
apt install -y nginx
Start and Enable Nginx
# Start Nginx
systemctl start nginx# Enable on boot
systemctl enable nginx# Check status
systemctl status nginx
Expected Outcome:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; ...)
Active: active (running) since ...
Test Nginx
Open your browser and access http://your-server-ip. You should see the default Nginx page.
🗄️ Step 5: Install MariaDB
MariaDB is a MySQL-compatible, high-performance, open-source database.
Install MariaDB
apt install -y mariadb-server mariadb-client
Start and Enable MariaDB
# Start MariaDB
systemctl start mariadb# Enable on boot
systemctl enable mariadb# Check status
systemctl status mariadb
Secure the Installation
mysql_secure_installation
Answer the questions:
| Question | Recommended Answer |
|---|---|
| Enter current password for root | Press Enter (empty) |
| Switch to unix_socket authentication | n |
| Change the root password | Y then enter a strong password |
| Remove anonymous users | Y |
| Disallow root login remotely | Y |
| Remove test database | Y |
| Reload privilege tables | Y |
Create a Database for Laravel
# Connect to MariaDB
mysql -u root -p
-- Create the database
CREATE DATABASE laravel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create a user
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'YourSecurePassword123!';
-- Grant permissions
GRANT ALL PRIVILEGES ON laravel.* TO 'laravel_user'@'localhost';
-- Apply changes
FLUSH PRIVILEGES;
-- Exit
EXIT;
📝 Note: Keep this information for Laravel configuration:
- Database:
laravel- User:
laravel_user- Password:
YourSecurePassword123!
🚀 Step 6: Install Laravel 12
Create the project directory
# Create the folder for websites
mkdir -p /var/www
# Go to the directory
cd /var/www
Install Laravel via Composer
composer create-project laravel/laravel monsite
💡 Replace
monsitewith your project name.
Expected result:
Creating a "laravel/laravel" project at "./monsite"
Installing laravel/laravel (v12.x.x)
- Downloading laravel/laravel (v12.x.x)
- Installing laravel/laravel (v12.x.x): Extracting archive
...
Application ready! Build something amazing. Laravel project structure
📁 /var/www/monsite/
├── 📁 app/ ← Application code
├── 📁 bootstrap/ ← Bootstrap files
├── 📁 config/ ← Configuration
├── 📁 database/ ← Migrations and seeds
├── 📁 public/ ← Web entry point (index.php)
├── 📁 resources/ ← Views, CSS, JS
├── 📁 routes/ ← Application routes
├── 📁 storage/ ← Generated files, logs, cache
├── 📁 tests/ ← Unit tests
├── 📁 vendor/ ← Composer dependencies
├── 📄 .env ← Environment variables
├── 📄 artisan ← Laravel CLI
└── 📄 composer.json ← Project dependencies
⚙️ Step 7: Configure Laravel
Configure the .env file
cd /var/www/monsite
nano .env
Modify the following lines:
APP_NAME="My Laravel Site"
APP_ENV=production
APP_DEBUG=false
APP_URL=http://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel_user
DB_PASSWORD=YourSecurePassword123!
⚠️ Important: In production, set
APP_DEBUG=falseto avoid exposing sensitive information.
Generate the application key
php artisan key:generate
Expected result:
INFO Application key set successfully.
Run migrations
php artisan migrate
Expected result:
INFO Running migrations.
2024_01_01_000000_create_users_table ............... 45ms DONE
2024_01_01_000001_create_password_reset_tokens_table 12ms DONE
2024_01_01_000002_create_sessions_table ............ 18ms DONE
...
📁 Step 8: Configure Permissions
Laravel requires certain directories to be writable.
Set the owner
# Change the owner to the web user
chown -R www-data:www-data /var/www/monsite
Set permissions
# Permissions for directories
find /var/www/monsite -type d -exec chmod 755 {} \;
# Permissions for files
find /var/www/monsite -type f -exec chmod 644 {} \;
# Special permissions for storage and bootstrap/cache
chmod -R 775 /var/www/monsite/storage
chmod -R 775 /var/www/monsite/bootstrap/cache
🔧 Step 9: Configure Nginx for Laravel
Create the site configuration
nano /etc/nginx/sites-available/monsite
Paste the following configuration:
server {
listen 80;
listen [::]:80;
# Replace with your domain or IP
server_name your-domain.com www.your-domain.com;
# Site root (Laravel public folder)
root /var/www/monsite/public;
# Index files
index index.php index.html index.htm;
# Encoding
charset utf-8;
# Logs
access_log /var/log/nginx/monsite.access.log;
error_log /var/log/nginx/monsite.error.log;
# Request handling
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Static files
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
# 404 error → Laravel
error_page 404 /index.php;
# PHP processing via PHP-FPM
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
# Block access to hidden files
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable the site
# Create a symbolic link
ln -s /etc/nginx/sites-available/monsite /etc/nginx/sites-enabled/
# Disable the default site (optional)
rm /etc/nginx/sites-enabled/default
# Test the configuration
nginx -t
Expected result:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Restart Nginx
systemctl restart nginx
✅ Step 10: Test the Installation
Access the site
Open your browser and go to:
http://your-server-ip(if no domain)http://your-domain.com(if domain configured)
You should see the Laravel homepage:
Check the application health
Laravel 12 includes a health check route:
curl http://localhost/up
Expected result: HTTP code 200 (OK)
🔒 Step 11: Secure with HTTPS (Let's Encrypt)
Install Certbot
apt install -y certbot python3-certbot-nginx
Obtain an SSL certificate
certbot --nginx -d your-domain.com -d www.your-domain.com
Follow the instructions:
- Enter your email
- Accept the terms
- Choose to redirect HTTP to HTTPS (recommended)
Check automatic renewal
certbot renew --dry-run
Expected result:
Congratulations, all simulated renewals succeeded
✅ Your site is now accessible via
https://your-domain.com
🛠️ Useful Laravel Commands
Artisan (Laravel CLI)
# Go to the project folder
cd /var/www/mywebsite
# See all available commands
php artisan list
# Clear cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Optimize for production
php artisan optimize
# Create a controller
php artisan make:controller NameController
# Create a model with migration
php artisan make:model ModelName -m
# Run migrations
php artisan migrate
# Rollback
php artisan migrate:rollback
# Maintenance mode
php artisan down
php artisan up
📊 Production Optimization
Optimize Laravel
cd /var/www/mywebsite
# Cache configuration
php artisan config:cache
# Cache routes
php artisan route:cache
# Cache views
php artisan view:cache
# Global optimization
php artisan optimize
Configure OPcache
nano /etc/php/8.3/fpm/conf.d/10-opcache.ini
Add or modify:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.save_comments=1
Restart PHP-FPM:
systemctl restart php8.3-fpm
🔧 Troubleshooting
Error 500 - Internal Server Error
| ❌ Cause | ✅ Solution |
|---|---|
| Incorrect permissions | chown -R www-data:www-data /var/www/mywebsite |
| Inaccessible storage | chmod -R 775 /var/www/mywebsite/storage |
| Missing APP_KEY | php artisan key:generate |
| Incorrect .env configuration | Check database settings |
# View Laravel errors
tail -f /var/www/mywebsite/storage/logs/laravel.log
Error 502 Bad Gateway
| ❌ Cause | ✅ Solution |
|---|---|
| PHP-FPM not started | systemctl start php8.3-fpm |
| Incorrect PHP socket | Check /var/run/php/php8.3-fpm.sock |
| Incorrect PHP version | update-alternatives --config php |
Database Connection Error
| ❌ Cause | ✅ Solution |
|---|---|
| MariaDB not started | systemctl start mariadb |
| Incorrect credentials | Check .env (DB_DATABASE, DB_USERNAME, DB_PASSWORD) |
| Database not created | Create the database using mysql -u root -p |
# Test connection
php artisan tinker
>>> DB::connection()->getPdo();
White Page
# Temporarily enable debug
nano /var/www/mywebsite/.env
# Change APP_DEBUG=true
# Clear cache
php artisan cache:clear
php artisan config:clear
# Check logs
tail -50 /var/www/mywebsite/storage/logs/laravel.log
📝 Command Summary
# === SYSTEM UPDATE ===
apt update && apt upgrade -y
# === PHP 8.3 INSTALLATION (Debian 13) ===
apt install -y apt-transport-https lsb-release ca-certificates curl gnupg
curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/sury-php.gpg
echo "deb [signed-by=/usr/share/keyrings/sury-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/sury-php.list
apt update
apt install -y php8.3 php8.3-fpm php8.3-cli php8.3-common php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath php8.3-intl
# === COMPOSER INSTALLATION ===
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# === NGINX INSTALLATION ===
apt install -y nginx
systemctl enable nginx && systemctl start nginx
# === MARIADB INSTALLATION ===
apt install -y mariadb-server mariadb-client
systemctl enable mariadb && systemctl start mariadb
mysql_secure_installation
# === DATABASE CREATION ===
mysql -u root -p -e "CREATE DATABASE laravel; CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'Password'; GRANT ALL ON laravel.* TO 'laravel_user'@'localhost'; FLUSH PRIVILEGES;"
# === LARAVEL INSTALLATION ===
cd /var/www
composer create-project laravel/laravel mywebsite
cd mywebsite
php artisan key:generate
php artisan migrate
# === PERMISSIONS ===
chown -R www-data:www-data /var/www/mywebsite
chmod -R 775 /var/www/mywebsite/storage /var/www/mywebsite/bootstrap/cache
# === HTTPS (Let's Encrypt) ===
apt install -y certbot python3-certbot-nginx
certbot --nginx -d your-domain.com
🎉 Conclusion
Congratulations! You have successfully installed:
- ✅ PHP 8.3 with all Laravel extensions
- ✅ Composer 2.x for dependency management
- ✅ Nginx configured for Laravel
- ✅ MariaDB with a dedicated database
- ✅ Laravel 12 ready for production
- ✅ HTTPS with Let's Encrypt (optional)
Next Steps
- 📖 Check the Laravel documentation
- 🎨 Install a starter kit:
php artisan breeze:installorphp artisan jetstream:install - 📊 Configure queues with Redis or Supervisor
- 📧 Set up email sending (SMTP, Mailgun, etc.)


