Environments — .env templates
One .env file per stage. They share the same shape, but a handful of keys flip as you move from your laptop to a live server — get those wrong and you either leak secrets or send real emails from a test box. The tabs below give you all three full files; the table first shows the keys that actually differ.
What changes between stages
Section titled “What changes between stages”These are the keys to double-check every time. Everything else is broadly the same across files.
| Key | Local | Staging | Production |
|---|---|---|---|
APP_ENV | local | staging | production |
APP_DEBUG | true | false | false |
APP_URL | http://your-project.test | https://staging.your-domain.com | https://your-domain.com |
LOG_LEVEL | debug | debug | error |
LOG_STACK | single | daily | daily |
MAIL_MAILER | log (no real send) | smtp (Mailtrap) | smtp (live provider) |
SESSION_ENCRYPT | false | false | true |
SESSION_SECURE_COOKIE | — | true | true |
| Payment keys | — | pk_test_ / sk_test_ | pk_live_ / sk_live_ |
TELESCOPE_ENABLED | true | true | false |
DEBUGBAR_ENABLED | true | false | false |
A second gotcha shows up only once the file lives on a real server, where passwords and tokens contain special characters.
The three files
Section titled “The three files”Each one carries a self-check at the bottom. Search for your- (local/staging) or the <...> markers (production) and replace.
# ============================================================================# 📋 LARAVEL LOCAL DEVELOPMENT ENVIRONMENT TEMPLATE# ============================================================================# Purpose: Local development environment configuration# Server: Laravel Herd / Valet / Docker / MAMP / XAMPP# Usage: Copy to project root as ".env"# ============================================================================## 🎯 LOCAL DEV CHARACTERISTICS:# - APP_DEBUG=true (show detailed errors)# - LOG_LEVEL=debug (verbose logging)# - MAIL_MAILER=log (no real emails sent)# - QUEUE_CONNECTION=sync (immediate execution)# - Cache/Session use file driver (simple, no Redis needed)## 💡 CUSTOMIZE: Search for "your-" and replace with your values# ============================================================================
# App IdentityAPP_NAME="Your App Name"# Domain: your-project.test (Herd) or localhost:8000 (artisan serve)APP_URL=http://your-project.test
# Environment SettingsAPP_ENV=localAPP_DEBUG=trueAPP_TIMEZONE=UTC
# Security Key (generated via: php artisan key:generate)APP_KEY=
# LocalizationAPP_LOCALE=enAPP_FALLBACK_LOCALE=enAPP_FAKER_LOCALE=en_US
# Logging — Local: show everything for debuggingLOG_CHANNEL=stackLOG_STACK=singleLOG_DEPRECATIONS_CHANNEL=nullLOG_LEVEL=debug
# Database — Local MySQL (Herd/MAMP): root with no password on localhostDB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=your_project_localDB_USERNAME=rootDB_PASSWORD=
# OPTIONAL: SQLite for simple local dev# DB_CONNECTION=sqlite# DB_DATABASE=/absolute/path/to/database.sqlite
# Cache & Session — Local: use 'file' driver (no extra services)CACHE_STORE=fileCACHE_PREFIX=
SESSION_DRIVER=fileSESSION_LIFETIME=120SESSION_ENCRYPT=falseSESSION_PATH=/SESSION_DOMAIN=null
# Queue & Broadcasting — Local: 'sync' runs jobs immediatelyQUEUE_CONNECTION=syncBROADCAST_CONNECTION=logFILESYSTEM_DISK=local
# Redis (Optional for Local)REDIS_CLIENT=phpredisREDIS_HOST=127.0.0.1REDIS_PASSWORD=nullREDIS_PORT=6379
# Mail — Local: 'log' driver writes emails to storage/logs instead of sendingMAIL_MAILER=logMAIL_HOST=127.0.0.1MAIL_PORT=2525MAIL_USERNAME=nullMAIL_PASSWORD=nullMAIL_ENCRYPTION=nullMAIL_FROM_ADDRESS="hello@your-project.test"MAIL_FROM_NAME="${APP_NAME}"
# AWS / S3 (Optional) — use local storage for dev, S3 only in staging/productionAWS_ACCESS_KEY_ID=AWS_SECRET_ACCESS_KEY=AWS_DEFAULT_REGION=us-east-1AWS_BUCKET=AWS_USE_PATH_STYLE_ENDPOINT=false
# Pusher / Websockets (Optional)PUSHER_APP_ID=PUSHER_APP_KEY=PUSHER_APP_SECRET=PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME=httpsPUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"VITE_PUSHER_HOST="${PUSHER_HOST}"VITE_PUSHER_PORT="${PUSHER_PORT}"VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Vite / Frontend buildVITE_APP_NAME="${APP_NAME}"
# Debug tooling (local only)TELESCOPE_ENABLED=trueDEBUGBAR_ENABLED=true
# ============================================================================# ✅ SETUP CHECKLIST# ============================================================================# [ ] 1. Copy this file to project root as ".env"# [ ] 2. Run: php artisan key:generate# [ ] 3. Create database: mysql -u root -e "CREATE DATABASE your_project_local"# [ ] 4. Run: php artisan migrate# [ ] 5. Update APP_NAME, APP_URL, DB_DATABASE with your project values# [ ] 6. Test: php artisan serve OR access via Herd (your-project.test)# ============================================================================# ============================================================================# 📋 LARAVEL STAGING ENVIRONMENT TEMPLATE# ============================================================================# Purpose: Pre-production testing environment# Domain: staging.your-domain.com (or subdomain)# Usage: Copy to staging server as ".env"# ============================================================================## 🎯 STAGING CHARACTERISTICS:# - APP_DEBUG=true (see errors, but consider false for client demos)# - LOG_LEVEL=debug (capture everything for debugging)# - Real email provider OR Mailtrap (no log driver!)# - Tests payment integrations in sandbox/test mode## ⚠️ ALWAYS wrap ALL values in double quotes!# Characters like #, $, &, ^, [, +, *, ;, spaces break .env parsing without quotes.## 💡 CUSTOMIZE: Search for "your-" and replace with your values# ============================================================================
# App Identity — clearly mark as stagingAPP_NAME="Your App - Staging"APP_URL=https://staging.your-domain.com
# Environment SettingsAPP_ENV=stagingAPP_DEBUG=trueAPP_TIMEZONE=UTC
# Security Key (generate unique key for staging!)# Run: php artisan key:generate --showAPP_KEY=
# LocalizationAPP_LOCALE=enAPP_FALLBACK_LOCALE=enAPP_FAKER_LOCALE=en_US
# Logging — Staging: full debugging; 'daily' rotates logs automaticallyLOG_CHANNEL=stackLOG_STACK=dailyLOG_DEPRECATIONS_CHANNEL=nullLOG_LEVEL=debug
# Database — use a DEDICATED staging database, NEVER share with productionDB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=your_staging_databaseDB_USERNAME=your_staging_db_userDB_PASSWORD=<STAGING_DB_PASSWORD>
# Cache & Session — file-based works on all shared hosting# ⚠️ FIRST INSTALL: keep 'file' — database tables don't exist yetCACHE_STORE=fileCACHE_PREFIX=staging_
SESSION_DRIVER=fileSESSION_LIFETIME=120SESSION_ENCRYPT=falseSESSION_PATH=/SESSION_DOMAIN=null
# Queue & Broadcasting — 'sync' until migrations create the jobs tableQUEUE_CONNECTION=syncBROADCAST_CONNECTION=logFILESYSTEM_DISK=public
# Redis (Optional — most shared hosting doesn't have it; leave commented)# REDIS_CLIENT=phpredis# REDIS_HOST=127.0.0.1# REDIS_PASSWORD=null# REDIS_PORT=6379# When Redis IS available, switch CACHE_STORE / SESSION_DRIVER / QUEUE_CONNECTION to redis
# Mail — Staging: Mailtrap RECOMMENDED (emails don't reach real users)MAIL_MAILER=smtpMAIL_HOST=smtp.mailtrap.ioMAIL_PORT=2525MAIL_USERNAME=your-mailtrap-usernameMAIL_PASSWORD=your-mailtrap-passwordMAIL_ENCRYPTION=tlsMAIL_FROM_ADDRESS="staging@your-domain.com"MAIL_FROM_NAME="${APP_NAME}"
# AWS / S3 — dedicated staging bucket; never use production credentialsAWS_ACCESS_KEY_ID=AWS_SECRET_ACCESS_KEY=AWS_DEFAULT_REGION=us-east-1AWS_BUCKET=your-project-stagingAWS_USE_PATH_STYLE_ENDPOINT=false
# Pusher / WebsocketsPUSHER_APP_ID=PUSHER_APP_KEY=PUSHER_APP_SECRET=PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME=httpsPUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"VITE_PUSHER_HOST="${PUSHER_HOST}"VITE_PUSHER_PORT="${PUSHER_PORT}"VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
VITE_APP_NAME="${APP_NAME}"
# Security — staging should mirror productionSESSION_SECURE_COOKIE=trueSESSION_HTTP_ONLY=trueSESSION_SAME_SITE=lax
# Integrations — ⚠️ use TEST/SANDBOX keys, NOT production keys!SENTRY_LARAVEL_DSN=SENTRY_TRACES_SAMPLE_RATE=1.0SENTRY_ENVIRONMENT=staging
# Stripe TEST keys start with sk_test_ and pk_test_STRIPE_KEY=pk_test_xxxxxxxxxxxxSTRIPE_SECRET=sk_test_xxxxxxxxxxxxSTRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx
# Debug tooling (disable Debugbar for client demos)TELESCOPE_ENABLED=trueDEBUGBAR_ENABLED=false
# ============================================================================# ✅ STAGING SETUP CHECKLIST# ============================================================================# [ ] 1. Create staging database on server# [ ] 2. Copy this file to staging server as ".env"# [ ] 3. Generate unique APP_KEY: php artisan key:generate# [ ] 4. Update all "your-" placeholders# [ ] 5. Verify HTTPS is working# [ ] 6. Set up Mailtrap for email testing# [ ] 7. Use TEST API keys for payment providers# [ ] 8. Run: php artisan migrate# [ ] 9. Test critical flows before production deploy# ============================================================================# ============================================================================# 📋 LARAVEL PRODUCTION ENVIRONMENT TEMPLATE# ============================================================================# Purpose: Live production environment configuration# Domain: your-domain.com (primary production site)# Usage: Copy to production server as ".env"# ============================================================================## 🚨 PRODUCTION CRITICAL SETTINGS:# - APP_DEBUG=false (NEVER true in production - exposes secrets!)# - APP_ENV=production (enables production optimizations)# - LOG_LEVEL=error (only log errors, not debug info)# - All secrets must be strong, unique passwords# - HTTPS enforced via SESSION_SECURE_COOKIE=true## ⚠️ ALWAYS wrap ALL values in double quotes!# Example: DB_PASSWORD=p@ss#w&rd$1 silently truncates to p@ss# Correct: DB_PASSWORD="p@ss#w&rd$1"## ⚠️ SECURITY WARNINGS:# - NEVER commit this file to Git with real secrets# - Rotate passwords/API keys quarterly# - Use separate database user with limited privileges## 💡 CUSTOMIZE: Search for "<" and ">" placeholders# ============================================================================
# App IdentityAPP_NAME="Your App Name"APP_URL=https://your-domain.com
# Environment Settings — ⚠️ APP_DEBUG must be FALSE!APP_ENV=productionAPP_DEBUG=falseAPP_TIMEZONE=UTC
# Security Key (generate unique for production!)# Run: php artisan key:generate --show — NEVER share or commit this keyAPP_KEY=
# LocalizationAPP_LOCALE=enAPP_FALLBACK_LOCALE=enAPP_FAKER_LOCALE=en_US
# Maintenance mode driverAPP_MAINTENANCE_DRIVER=fileAPP_MAINTENANCE_SECRET=<random-secret-for-maintenance-bypass>
# Logging — Production: only errors and aboveLOG_CHANNEL=stackLOG_STACK=dailyLOG_DEPRECATIONS_CHANNEL=nullLOG_LEVEL=error
# Database — use a dedicated user (not root!), strong 32+ char passwordDB_CONNECTION=mysqlDB_HOST=localhostDB_PORT=3306DB_DATABASE=<production_database_name>DB_USERNAME=<production_database_user>DB_PASSWORD=<STRONG_PRODUCTION_DB_PASSWORD>
# Cache & Session# ⚠️ FIRST INSTALL: keep 'file' — tables don't exist yet. Upgrade later.# Performance tiers (best→worst): redis > database > fileCACHE_STORE=fileCACHE_PREFIX=prod_
SESSION_DRIVER=fileSESSION_LIFETIME=120SESSION_ENCRYPT=trueSESSION_PATH=/SESSION_DOMAIN=null
# Queue & Broadcasting — 'sync' until migrations create the jobs tableQUEUE_CONNECTION=syncBROADCAST_CONNECTION=logFILESYSTEM_DISK=public
# Redis (Optional — enable when available)# REDIS_CLIENT=phpredis# REDIS_HOST=127.0.0.1# REDIS_PASSWORD=<REDIS_PASSWORD_IF_SET># REDIS_PORT=6379# When Redis IS available, switch CACHE_STORE / SESSION_DRIVER / QUEUE_CONNECTION to redis
# Mail — Production: reliable transactional provider; ⚠️ NEVER 'log' driverMAIL_MAILER=smtpMAIL_HOST=[SMTP_HOST]MAIL_PORT=[SMTP_PORT]MAIL_USERNAME=[MAIL_USERNAME]MAIL_PASSWORD=[MAIL_PASSWORD]MAIL_ENCRYPTION=[ssl_or_tls]MAIL_FROM_ADDRESS="noreply@[DOMAIN]"MAIL_FROM_NAME="${APP_NAME}"# Providers: Titan smtp.titan.email:465 ssl · SendGrid smtp.sendgrid.net:587 tls · Mailgun smtp.mailgun.org:587 tls
# AWS / S3 — IAM user with minimal permissionsAWS_ACCESS_KEY_ID=<your-aws-access-key>AWS_SECRET_ACCESS_KEY=<your-aws-secret-key>AWS_DEFAULT_REGION=us-east-1AWS_BUCKET=<your-production-bucket>AWS_USE_PATH_STYLE_ENDPOINT=false
# Pusher / WebsocketsPUSHER_APP_ID=<your-pusher-app-id>PUSHER_APP_KEY=<your-pusher-app-key>PUSHER_APP_SECRET=<your-pusher-app-secret>PUSHER_HOST=PUSHER_PORT=443PUSHER_SCHEME=httpsPUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"VITE_PUSHER_HOST="${PUSHER_HOST}"VITE_PUSHER_PORT="${PUSHER_PORT}"VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
VITE_APP_NAME="${APP_NAME}"
# Security — ALL of these should be enabled in production!SESSION_SECURE_COOKIE=trueSESSION_HTTP_ONLY=trueSESSION_SAME_SITE=laxBCRYPT_ROUNDS=12
# Integrations — ⚠️ use LIVE/PRODUCTION keys only!SENTRY_LARAVEL_DSN=https://xxx@xxx.ingest.sentry.io/xxxSENTRY_TRACES_SAMPLE_RATE=0.1SENTRY_ENVIRONMENT=production
# Stripe LIVE keys start with sk_live_ and pk_live_STRIPE_KEY=pk_live_xxxxxxxxxxxxSTRIPE_SECRET=sk_live_xxxxxxxxxxxxSTRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx
GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX
# Production-only settings — disable debug tooling, enable optimizationsTELESCOPE_ENABLED=falseDEBUGBAR_ENABLED=falseOPCACHE_ENABLED=trueRATE_LIMIT_ENABLED=true
# ============================================================================# ✅ PRODUCTION DEPLOYMENT CHECKLIST# ============================================================================# [ ] 1. APP_DEBUG is FALSE (⚠️ CRITICAL!)# [ ] 2. APP_ENV is 'production'# [ ] 3. Unique APP_KEY generated for production# [ ] 4. Strong database password (32+ characters)# [ ] 5. HTTPS enabled and working# [ ] 6. SESSION_SECURE_COOKIE is TRUE# [ ] 7. Using LIVE API keys (not test/sandbox)# [ ] 8. Error tracking (Sentry) configured# [ ] 9. Email delivery tested# [ ] 10. Backups configured and tested# [ ] 11. php artisan config:cache (after ALL changes)# [ ] 12. php artisan route:cache# [ ] 13. php artisan view:cache# [ ] 14. Queue workers running via Supervisor# ============================================================================Once all three files are in place and the first migrate has run, the web-server layer is what locks everything down.