Install Laravel 13 on a Debian 13 server with nginx
5 chapters · 1 hr 15 · Intermediate · Composer, Debian · verified on 7 September 2026
On Debian 13, PHP 8.4 comes from the official repositories: no third-party repository is needed. Install PHP and its extensions, then Composer (checking its signature), create the project with composer create-project laravel/laravel, give write access to storage/ and bootstrap/cache/, then point the nginx root at public/. Check the result: the home page must answer 200 and /.env must answer 403.
This guide runs through the full installation of a Laravel 13 application on a Debian 13 server, from a bare system to a page served by nginx. Every step was executed on a clean Debian 13 “trixie” on 2 September 2026, and the end result checked: HTTP 200 on the home page, and the .env file unreachable from the web.
Part of the Web development learning path. Five of the steps have a detailed chapter of their own, listed in the sidebar. What follows is the complete sequence, with just enough of each step to reach the end.
What you need before you start
- A Debian 13 “trixie” server reachable over SSH. Debian 12 works too, with PHP 8.2.
- A non-root user with
sudo. Never install or deploy as root. - A development machine on your side. On macOS, the usual blocker is the
laravelcommand not being found. - A domain name pointing at the server, if you plan on HTTPS.
Create the user that will own the application, if you have not already:
sudo adduser deploy
sudo usermod -aG sudo deploy1. PHP and its extensions
Debian 13 ships PHP 8.4, which saves you the third-party Sury repository:
sudo apt update
sudo apt install -y php-cli php-fpm \
php-mbstring php-xml php-curl php-zip \
php-bcmath php-intl php-mysql php-sqlite3 \
unzip curl ca-certificates
php -v # PHP 8.4.24The full list of required extensions, the php.ini settings and the steps for installing another PHP version are in the chapter devoted to PHP.
2. Composer
Install it while checking the hash of the installer, as the official documentation requires:
EXPECTED="$(curl -sS https://composer.github.io/installer.sig)"
curl -sS https://getcomposer.org/installer -o composer-setup.php
ACTUAL="$(php -r 'echo hash_file("sha384", "composer-setup.php");')"
if [ "$EXPECTED" = "$ACTUAL" ]; then
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
fi
rm composer-setup.php
composer --version # Composer version 2.10.33. Create the Laravel project
As the deploy user, never as root:
su - deploy
composer create-project laravel/laravel /home/deploy/app --no-interaction
cd /home/deploy/app
php artisan --version # Laravel Framework 13.30.1create-project writes the .env file, generates the application key and runs the migrations against the default SQLite database. For MySQL or MariaDB, whose installation has a chapter of its own, fill in the connection then run the migrations again:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=un_mot_de_passe_solidephp artisan migrate --force4. File permissions
This is the step people skip, and it produces the blank page, or a 500 error. The folders concerned are storage_path() and bootstrap/cache, described in the application paths, plus database/ as long as the application uses the SQLite database created at install time: the session driver writes to it on every visit. The web server must be able to write to these three folders, and only these:
sudo chown -R deploy:www-data /home/deploy/app/storage /home/deploy/app/bootstrap/cache /home/deploy/app/database
sudo chmod -R 775 /home/deploy/app/storage /home/deploy/app/bootstrap/cache /home/deploy/app/databaseIf the project lives in a user’s home directory, nginx has to be able to traverse it:
sudo chmod o+x /home/deployIt is the most widespread piece of advice and the most dangerous: it makes the application writable by every account on the server. 775 with the right group is enough, and that is what was used for the verification.
5. nginx and PHP-FPM
This section installs nginx. If you would rather use Apache, more common on shared hosting, follow the chapter devoted to Apache instead: the rest of the guide still holds.
sudo apt install -y nginx php-fpmNote the exact socket path, it carries the PHP version number:
grep -h '^listen' /etc/php/8.4/fpm/pool.d/www.conf
# listen = /run/php/php8.4-fpm.sockCreate the site file. The root points at public/, never at the project root:
server {
listen 80;
server_name exemple.com;
root /home/deploy/app/public;
index index.php;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ /\.(?!well-known).* {
deny all;
}
}Enable the site, disable the default one, check the syntax:
sudo ln -s /etc/nginx/sites-available/laravel /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful6. Check the installation
Two checks are enough to tell whether the installation holds up:
curl -s -o /dev/null -w "page d'accueil : %{http_code}\n" http://127.0.0.1/
curl -s -o /dev/null -w ".env : %{http_code}\n" http://127.0.0.1/.envpage d'accueil : 200
.env : 403The second one matters most. A 200 on /.env would mean your database credentials and your APP_KEY are publicly readable. The location ~ /\.(?!well-known).* block is what prevents it, and it is why the site root has to be public/.
7. Move to production
The development configuration leaks information. Three lines in the .env:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://exemple.comThen the caches, to be regenerated after every deployment:
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan about --only=environment # Debug Mode must show OFFThat setting is covered in detail in the article on debug mode.
Add HTTPS with Certbot, which edits the nginx configuration itself:
If the application exposes public forms, plan for protection against automated submissions as well.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d exemple.comFinally, the task scheduler, if the application uses one:
sudo crontab -u deploy -e
# * * * * * cd /home/deploy/app && php artisan schedule:run >> /dev/null 2>&1For cache, sessions and queues on Redis, carry on with the chapter devoted to Redis.
Common errors
chmod o+x /home/deploy, otherwise nginx returns a permission error.