Configuration Guide
Last Updated: 2026-01-09 Status: Active Audience: Developers
This guide documents configuration patterns and environment management for the Client Portal application.
Table of Contents
- Overview
- Environment Variables
- Configuration Files
- Custom Configuration
- Accessing Configuration
- Environment-Specific Settings
- Secrets Management
Overview
Laravel configuration is managed through:
.envfiles for environment-specific valuesconfig/*.phpfiles for application settings- Environment variables for sensitive data
Configuration Priority
- Environment variables (
.env) - Config file defaults
- Package defaults
Environment Variables
.env File Structure
#--------------------------------------------------------------------
# Application
#--------------------------------------------------------------------
APP_NAME="Client Portal"
APP_ENV=local
APP_KEY=base64:...
APP_DEBUG=true
APP_URL=http://localhost:8000
#--------------------------------------------------------------------
# Database
#--------------------------------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=client_portal
DB_USERNAME=root
DB_PASSWORD=
#--------------------------------------------------------------------
# Cache & Session
#--------------------------------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=database
SESSION_LIFETIME=120
QUEUE_CONNECTION=sync
#--------------------------------------------------------------------
# Mail
#--------------------------------------------------------------------
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@clientportal.test"
MAIL_FROM_NAME="${APP_NAME}"
#--------------------------------------------------------------------
# Application-Specific
#--------------------------------------------------------------------
CLIENT_PORTAL_MAX_FILE_SIZE=10485760
CLIENT_PORTAL_INVOICE_DUE_DAYS=30
.env.example Template
Always maintain .env.example with all variables (without sensitive values):
APP_NAME="Client Portal"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=client_portal
DB_USERNAME=root
DB_PASSWORD=
# Add comments for non-obvious variables
# Maximum file upload size in bytes (default: 10MB)
CLIENT_PORTAL_MAX_FILE_SIZE=10485760
Naming Conventions
| Pattern | Example |
|---|---|
| Laravel core | APP_NAME, DB_HOST, MAIL_MAILER |
| Custom app | CLIENT_PORTAL_* prefix |
| Services | STRIPE_SECRET, AWS_ACCESS_KEY_ID |
Configuration Files
Core Configuration Files
config/
├── app.php # Application settings
├── auth.php # Authentication guards, providers
├── cache.php # Cache drivers
├── database.php # Database connections
├── filesystems.php # Storage disks
├── logging.php # Log channels
├── mail.php # Mail configuration
├── queue.php # Queue connections
├── services.php # Third-party services
└── session.php # Session configuration
Configuration File Structure
<?php
// config/app.php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application, which will be used when the
| framework needs to place the application's name in a notification or
| other UI elements where an application name needs to be displayed.
|
*/
'name' => env('APP_NAME', 'Laravel'),
'env' => env('APP_ENV', 'production'),
'debug' => (bool) env('APP_DEBUG', false),
'url' => env('APP_URL', 'http://localhost'),
// ...
];
Custom Configuration
Creating Custom Config Files
<?php
// config/client_portal.php
return [
/*
|--------------------------------------------------------------------------
| File Upload Settings
|--------------------------------------------------------------------------
*/
'files' => [
// Maximum file size in bytes (10MB default)
'max_size' => (int) env('CLIENT_PORTAL_MAX_FILE_SIZE', 10 * 1024 * 1024),
// Allowed MIME types
'allowed_types' => [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'image/jpeg',
'image/png',
],
// Storage disk for project files
'disk' => env('CLIENT_PORTAL_FILES_DISK', 'local'),
],
/*
|--------------------------------------------------------------------------
| Invoice Settings
|--------------------------------------------------------------------------
*/
'invoices' => [
// Default days until invoice is due
'due_days' => (int) env('CLIENT_PORTAL_INVOICE_DUE_DAYS', 30),
// Days before due date to send reminder
'reminder_days' => [7, 3, 1],
// Tax rate (decimal)
'tax_rate' => (float) env('CLIENT_PORTAL_TAX_RATE', 0.10),
// Invoice number prefix
'number_prefix' => env('CLIENT_PORTAL_INVOICE_PREFIX', 'INV-'),
],
/*
|--------------------------------------------------------------------------
| Notification Settings
|--------------------------------------------------------------------------
*/
'notifications' => [
// Send email notifications
'email_enabled' => (bool) env('CLIENT_PORTAL_EMAIL_NOTIFICATIONS', true),
// Admin email for system notifications
'admin_email' => env('CLIENT_PORTAL_ADMIN_EMAIL'),
],
/*
|--------------------------------------------------------------------------
| Pagination
|--------------------------------------------------------------------------
*/
'pagination' => [
'per_page' => 15,
'max_per_page' => 100,
],
];
Organizing Complex Configuration
<?php
// config/client_portal.php - using separate files
return [
'files' => require __DIR__ . '/client_portal/files.php',
'invoices' => require __DIR__ . '/client_portal/invoices.php',
'notifications' => require __DIR__ . '/client_portal/notifications.php',
];
Accessing Configuration
Using the config() Helper
// Get single value
$maxSize = config('client_portal.files.max_size');
// Get with default
$taxRate = config('client_portal.invoices.tax_rate', 0.10);
// Get entire section
$fileConfig = config('client_portal.files');
// Set value at runtime (not recommended)
config(['client_portal.files.max_size' => 5 * 1024 * 1024]);
Using the Config Facade
use Illuminate\Support\Facades\Config;
$maxSize = Config::get('client_portal.files.max_size');
$fileConfig = Config::get('client_portal.files');
In Service Classes
class InvoiceService
{
private int $dueDays;
private float $taxRate;
public function __construct()
{
$this->dueDays = config('client_portal.invoices.due_days');
$this->taxRate = config('client_portal.invoices.tax_rate');
}
public function calculateDueDate(): Carbon
{
return now()->addDays($this->dueDays);
}
}
Injecting Configuration
// For testability, inject config values
class InvoiceService
{
public function __construct(
private readonly int $dueDays,
private readonly float $taxRate
) {}
}
// In AppServiceProvider
$this->app->when(InvoiceService::class)
->needs('$dueDays')
->give(fn () => config('client_portal.invoices.due_days'));
$this->app->when(InvoiceService::class)
->needs('$taxRate')
->give(fn () => config('client_portal.invoices.tax_rate'));
Environment-Specific Settings
Local Development
# .env (local)
APP_ENV=local
APP_DEBUG=true
# Use log driver for mail (see emails in logs)
MAIL_MAILER=log
# Use sync queue for immediate processing
QUEUE_CONNECTION=sync
# SQLite for simplicity (optional)
DB_CONNECTION=sqlite
Production
# .env (production)
APP_ENV=production
APP_DEBUG=false
# Real mail provider
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@mg.example.com
MAIL_PASSWORD=your-password
# Database queue for reliability
QUEUE_CONNECTION=database
# Redis for cache and sessions
CACHE_DRIVER=redis
SESSION_DRIVER=redis
Testing
// phpunit.xml
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="MAIL_MAILER" value="array"/>
</php>
Checking Environment
// Check current environment
if (app()->environment('production')) {
// Production-only code
}
if (app()->environment(['local', 'staging'])) {
// Local or staging
}
// Check debug mode
if (config('app.debug')) {
// Debug mode enabled
}
// In Blade
@production
<script src="analytics.js"></script>
@endproduction
@env('local')
<div class="debug-bar">...</div>
@endenv
Secrets Management
Never Commit Secrets
# .gitignore
.env
.env.local
.env.*.local
.env.production
.env.staging
Required vs Optional Variables
// config/client_portal.php
// Required - will throw error if missing
'stripe_secret' => env('STRIPE_SECRET') ?? throw new \RuntimeException(
'STRIPE_SECRET environment variable is required'
),
// Optional with sensible default
'tax_rate' => (float) env('CLIENT_PORTAL_TAX_RATE', 0.10),
Validating Configuration
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if (app()->environment('production')) {
$this->validateProductionConfig();
}
}
private function validateProductionConfig(): void
{
$required = [
'APP_KEY',
'DB_HOST',
'DB_DATABASE',
'MAIL_HOST',
];
foreach ($required as $key) {
if (empty(env($key))) {
throw new \RuntimeException("Missing required environment variable: {$key}");
}
}
if (config('app.debug')) {
throw new \RuntimeException('APP_DEBUG must be false in production');
}
}
Production Secrets
For production, consider:
# AWS Parameter Store / Secrets Manager
# Set environment variables in your hosting platform
# Laravel Forge
# Configure via Forge dashboard
# Docker
# Use Docker secrets or environment files
# Kubernetes
# Use ConfigMaps and Secrets
Configuration Caching
Cache Configuration for Production
# Cache all configuration
php artisan config:cache
# Clear configuration cache
php artisan config:clear
Important Notes
When configuration is cached:
.envfile is NOT readenv()calls in config files workenv()calls elsewhere returnnull
// Good - env() in config file
// config/services.php
'stripe' => [
'secret' => env('STRIPE_SECRET'),
],
// Then use config()
$secret = config('services.stripe.secret');
// Bad - env() outside config file
// app/Services/PaymentService.php
$secret = env('STRIPE_SECRET'); // Returns null when cached!
Best Practices
Do
- Use
.envfor environment-specific values - Provide sensible defaults in config files
- Document all custom environment variables
- Use
config()helper, notenv()in application code - Cache configuration in production
- Validate required configuration on boot
Don't
- Commit
.envfiles - Use
env()outside of config files - Hard-code environment-specific values
- Store sensitive data in config files
- Forget to update
.env.example
Config File Checklist
- All values use
env()where appropriate - Sensible defaults provided
- Comments explain non-obvious settings
- Values are cast to correct types
-
.env.exampleis updated - Validated in production if critical
Related Documentation
- Laravel Configuration Documentation
- SECURITY.md - Secrets management
- CONTRIBUTING.md - Development setup