laravel/laravel
Deployment
The skeleton ships no opinionated deployment config — no Dockerfile, no Procfile, no Kubernetes manifests, no Forge or Vapor metadata. The README points at Laravel's hosting documentation for the canonical guidance. This page distills the deployment-related defaults and config that are in the skeleton, so you know what to override per environment.
What deployment looks like in this repo
A working production deploy of the skeleton is roughly:
- Clone the repo (or rsync a built artifact).
composer install --optimize-autoloader --no-dev(no Pint, PHPUnit, Pail, etc.).npm ci && npm run build(writespublic/build/manifest.json).- Set
.envwith production values (DB, cache, queue, mail, storage). php artisan key:generate(only ifAPP_KEYis not set).php artisan migrate --force.php artisan optimize(or, individually:config:cache,route:cache,view:cache,event:cache).- Point the web server's document root at
public/.
Steps 7 and 8 are where most production-related configuration lives.
Environment-aware defaults
Several settings change behaviour based on APP_ENV. The skeleton sets APP_ENV=local in .env.example — production deploys must set it to production:
| Setting | local (default) |
production recommended |
|---|---|---|
APP_ENV |
local |
production |
APP_DEBUG |
true |
false |
LOG_LEVEL |
debug |
warning or error |
LOG_DEPRECATIONS_CHANNEL |
null |
A real channel if you want deprecations logged |
BCRYPT_ROUNDS |
12 |
12 (default is fine; raise only if your CPU agrees) |
DB_CONNECTION |
sqlite |
mysql / pgsql / mariadb / sqlsrv |
SESSION_SECURE_COOKIE |
(unset) | true for HTTPS-only cookies |
SESSION_DOMAIN |
null |
Your top-level domain |
APP_DEBUG=true in production exposes stack traces and environment to anyone who triggers an exception. Treat it as a security incident if it ships.
Caches to warm before traffic
Run these as part of the deploy script:
php artisan config:cache # writes bootstrap/cache/config.php
php artisan route:cache # writes bootstrap/cache/routes-v7.php
php artisan view:cache # pre-compiles every Blade view
php artisan event:cache # writes bootstrap/cache/events.phpOr, equivalently:
php artisan optimizeAfter config:cache, the framework no longer reads .env at runtime — it reads from the cached array. If you change .env, you must run php artisan config:clear (or re-run optimize) for the change to take effect.
Migrations
php artisan migrate --forceThe --force flag is mandatory in non-interactive environments (the framework prompts for confirmation otherwise). The three baseline migrations create users, password reset tokens, sessions, cache, cache_locks, jobs, job_batches, and failed_jobs.
If you've added migrations and they need to roll out before the new code goes live, run migrate --force before swapping the application code over — most migrations are backward-compatible, and putting schema before code prevents downtime.
Queue workers
The default queue connection is database, which means any deploy that uses queues needs at least one worker process. The framework provides php artisan queue:work (long-running) and php artisan queue:listen (restarts after each job, used in dev). Production deployments typically run queue:work under Supervisor or systemd.
After every deploy:
php artisan queue:restartThis signals all running workers to gracefully exit at their next cycle, so the next request to come in is served by a worker that has the new code loaded.
Storage symlink
The storage:link command creates public/storage → storage/app/public:
php artisan storage:linkconfig/filesystems.php declares this mapping under the links key. Run once per server. Without it, Storage::disk('public')->url($path) produces broken URLs.
Maintenance mode
php artisan down --refresh=15 --retry=60 --secret=<secret-token>
# do the deploy
php artisan updown writes storage/framework/maintenance.php, and public/index.php checks for it on every request. The --secret flag lets you keep accessing the app via https://example.com/<secret> while everyone else gets a 503.
For zero-downtime deploys you typically don't use maintenance mode — instead, deploy to a new release directory and atomically symlink it. Forge, Vapor, and Envoyer all do this.
Health check
bootstrap/app.php registers GET /up as a health check via:
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)The framework returns 200 from this endpoint as long as it can boot. Use it for load balancer health checks, container readiness probes, and uptime monitors. The endpoint runs the framework's request lifecycle, so it does verify config, providers, and routes load — but it does not hit the database. Add your own health controller if you need DB/queue/cache reachability checks.
Web server document root
Apache: the bundled public/.htaccess handles rewrites; just point DocumentRoot at public/.
nginx: there's no bundled config. The Laravel docs and Forge templates use:
root /var/www/laravel/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}PHP built-in (php artisan serve): for local dev only. Don't run it in production.
File permissions
The web server user must be able to write to:
storage/and every subdirectorybootstrap/cache/
Standard recipe:
chown -R www-data:www-data /var/www/laravel
find /var/www/laravel -type d -exec chmod 755 {} +
find /var/www/laravel -type f -exec chmod 644 {} +
chmod -R ug+rwx /var/www/laravel/storage /var/www/laravel/bootstrap/cacheSecrets
APP_KEY (32 bytes, base64-encoded) decrypts cookies, signed URLs, encrypted columns, and queued jobs. Rotating it invalidates all existing encrypted state.
The skeleton's config/app.php supports key rotation:
'previous_keys' => [
...array_filter(
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
),
],Set APP_PREVIOUS_KEYS=<old1>,<old2> to keep decrypting old payloads while encrypting new ones with the current APP_KEY. After enough time has passed for all old data to be re-encrypted (or expired), drop them.
Other secrets — DB passwords, AWS keys, third-party API tokens — should be sourced from your deployment platform's secret manager (Forge env vars, Vapor secrets, AWS Secrets Manager, etc.). The .env.example contains placeholders; never check real values into source control.
See Security for the security-relevant defaults that interact with deployment.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.