VS Vikesh Singh

Courses PHP & Laravel Lesson 18

PHP & LARAVEL · LESSON 18 OF 18

Building and Deploying a Real Laravel App

· 5 min read

This is the final lesson of the course, and it is about the step every previous lesson was quietly building toward.

This is the final lesson of the course, and it is about the step every previous lesson was quietly building toward: taking a Laravel application from your own machine to a real server where real people can use it. Lessons 1 through 17 taught you how to write the application; this lesson is about what changes once it has to survive contact with the real world.

Environment Configuration

The first mental shift, and one experienced developers who are new to Laravel specifically sometimes still trip over, is environment configuration. Laravel reads settings, database credentials, API keys, debug mode, the application's URL, from a .env file, loaded through the config/ directory's files, never hardcoded directly into your PHP code:

APP_ENV=production
APP_DEBUG=false
APP_URL=https://indexhub.in

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=sp_production
DB_USERNAME=sp_user
DB_PASSWORD=supersecret

Two settings matter enormously and are a genuinely common mistake to get wrong: APP_ENV=production and APP_DEBUG=false. In local development, APP_DEBUG=true is wonderful, every error shows a full stack trace with file paths, variable values, and line numbers directly in the browser. In production, that exact same behavior is a serious information leak: a visitor who triggers any error sees your database credentials, file system paths, and internal code structure laid out in front of them. Every real Laravel deployment must run with APP_DEBUG=false, with errors instead logged quietly to a log file (or an error-tracking service) that only your team can see.

.env itself should never, under any circumstance, be committed to Git. It holds real secrets: database passwords, API keys, mail credentials. Laravel's default .gitignore already excludes it for exactly this reason; the file that actually gets committed is .env.example, a template with the same variable names but placeholder or blank values, so a new developer (or a fresh server) knows exactly which variables need real values filled in, without ever seeing anyone else's actual secrets.

The Deploy Steps

Deploying a code change, in the simplest and most common self-managed setup like the one this very project runs on, follows a predictable sequence, and this project's own CLAUDE.md spells out the exact version for this specific server, worth walking through here because every step has a reason:

git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache

composer install --no-dev skips installing development-only packages (like PHPUnit, from lesson 17) that production never needs to run, keeping the deployed codebase smaller and reducing the attack surface of code that exists on the server. migrate --force is needed because Laravel, by default, refuses to run migrations in a production environment without explicit confirmation, a safety rail against someone accidentally running a destructive schema change against live data by mistake, exactly the kind of mistake lesson 10 warned about with migrate:fresh.

The three cache commands at the end matter for performance, and also explain a confusing class of bug if you ever skip them inconsistently: Laravel can precompile your configuration files, your routes, and your Blade views into a single optimized file each, instead of parsing and merging many files on every single request. This is a meaningful speed boost at real traffic volumes. The trap: if you cache config and then change a .env value without re-running config:cache, your application keeps using the old cached config values, silently, because the cache file takes priority once it exists. The fix whenever a deployed .env change doesn't seem to take effect is always the same, clear and rebuild the cache: php artisan config:clear followed by php artisan config:cache again.

Restarting Background Workers

For a Laravel application running background workers, exactly as described back in lesson 15, deployment has one more mandatory step this project's CLAUDE.md calls out directly: restarting the queue workers, because a running worker process keeps the old, pre-deploy code loaded in memory indefinitely otherwise:

sudo supervisorctl restart sp-queue-backtests sp-strategy-run sp-queue-live

Skipping this step after a deploy is one of the most common and most confusing production bugs across the entire Laravel ecosystem, not specific to this project: the code on disk is fixed, tests pass, everything looks correct, and yet the bug keeps happening in production, because the actual running process never picked up the fix.

Easier Deployment Options

A fair question for a beginner finishing this course: is there an easier path than managing your own server, Supervisor, and manual deploy steps? Yes. Platforms like Laravel Cloud (mentioned in this project's own Laravel Boost guidelines) or other managed hosting handle most of this ceremony, server provisioning, zero-downtime deploys, queue worker supervision, automatically. They are a very reasonable choice for a new project, and worth knowing about even though this specific course's example project (and the real production site it's modeled on) runs on a traditional, self-managed server, which is also an extremely common real-world setup, especially for teams with existing infrastructure or specific compliance needs that managed platforms don't fit.

Logging And Monitoring

Logging and monitoring deserve a final mention, because "it deployed successfully" is not the same as "it is working correctly for real users." Laravel writes application errors to storage/logs/laravel.log by default, and production applications typically also wire up an error-tracking service (like Sentry or Flare) to get immediately notified of exceptions, rather than discovering a broken feature only when a user complains days later.

This closes the course. You started at "what PHP even is" in lesson 1, and you have now walked through the exact path that turns a PHP script into a deployed, tested, queue-backed, authenticated Laravel application running in production. The gap between a tutorial project and a real one is almost entirely in this final lesson's territory: configuration discipline, caching correctness, and remembering that running code in memory is not automatically the same as the code currently on disk.

Try it yourself, as a capstone for the whole course: take the Post model, migrations, controller, Form Request, and tests you built across lessons 9 through 17, and actually deploy them to a fresh, free-tier server or a platform like Laravel Cloud, end to end, from git push through a working, publicly reachable page. Completing that one real deployment will teach you more than rereading any lesson a second time.