DEV Community

Cover image for Secrets Management for Laravel: .env, Encrypted Config, and Deploynix
Deploynix
Deploynix

Posted on • Originally published at deploynix.io

Secrets Management for Laravel: .env, Encrypted Config, and Deploynix

Every Laravel application has secrets. Database passwords, API keys, encryption keys, third-party service credentials, payment gateway tokens. These secrets are the keys to your kingdom, and mishandling them is one of the most common security mistakes in web development.

The default approach of storing everything in a .env file works fine during development. But as your application grows, your team expands, and your deployment pipeline becomes more sophisticated, the humble .env file starts showing its limitations. This article explores why .env alone isn't enough, what alternatives exist, and how Deploynix's credential management fits into a mature secrets management strategy.

The .env File: Simple but Limited

Laravel's .env file is elegant in its simplicity. It's a flat file of key-value pairs that sits in your project root, ignored by version control, and loaded into PHP's environment at boot time via the vlucas/phpdotenv package.

What it does well:

  • Separates configuration from code.
  • Different values per environment (local, staging, production).
  • Easy to understand and edit.
  • Supported by virtually every hosting environment.

Where it falls short:

  • No encryption at rest. The .env file is plaintext on disk. Anyone with filesystem access can read every secret.
  • No access control. You can't give a developer access to some secrets but not others. It's all or nothing.
  • No audit trail. When someone changes a value, there's no record of who changed what, when, or why.
  • No versioning. If someone accidentally deletes a key or introduces a typo, there's no way to roll back to a previous version.
  • Distribution headaches. Getting the right .env file to the right server requires manual copying, SSH access, or a deployment tool. Mistakes are easy to make.
  • Accidental exposure. Despite .gitignore, .env files get committed to repositories, uploaded to public S3 buckets, and included in Docker images more often than anyone cares to admit.

These limitations don't mean you should stop using .env files. They mean you need to augment them with additional practices and tools.

Laravel's Encrypted Environment Files

Starting with Laravel 9, the framework introduced built-in support for encrypted environment files. This feature lets you commit encrypted .env files to version control, solving the distribution problem while keeping secrets secure.

How it works:

Generate an encrypted environment file:

php artisan env:encrypt --env=production
Enter fullscreen mode Exit fullscreen mode

This creates .env.production.encrypted in your project root, encrypted with a key that's output to your terminal. The original .env.production file should then be deleted (or kept only in a secure vault).

To decrypt during deployment:

php artisan env:decrypt --env=production --key=your-encryption-key
Enter fullscreen mode Exit fullscreen mode

Or set the LARAVEL_ENV_ENCRYPTION_KEY environment variable, and Laravel will decrypt automatically.

Advantages:

  • Encrypted files can be committed to version control safely.
  • Each environment gets its own encrypted file with its own key.
  • Only one secret (the encryption key) needs to be managed outside of the file.
  • Changes to environment variables are tracked in your Git history.

Limitations:

  • You still need to manage the encryption key securely.
  • The decrypted file is still plaintext on disk during runtime.
  • No granular access control; anyone with the key can decrypt everything.
  • Rotating secrets requires re-encrypting the file and redeploying.

Best practices with encrypted env files:

  • Store the encryption key in a secure vault or your CI/CD system's secret storage.
  • Use different encryption keys for each environment.
  • Automate the decrypt step in your deployment pipeline.
  • Rotate the encryption key periodically by re-encrypting with a new key.

Per-Environment Secrets Strategy

As your application moves through environments (local development, CI/CD, staging, production), each environment should have its own isolated set of secrets with appropriate access controls.

Local development:

Use a plain .env file with development-only credentials. These should be throwaway credentials that don't work in production. Use local services or Docker containers for databases, caches, and mail catchers.

CI/CD:

Store secrets in your CI/CD platform's native secret management (GitHub Actions secrets, GitLab CI variables, etc.). These are encrypted at rest, access-controlled, and masked in logs. Inject them as environment variables during the test run.

Staging:

Use a dedicated set of credentials that mirror production's structure but point to staging resources. Consider using Laravel's encrypted env files for staging, with the encryption key stored in your deployment platform.

Production:

This is where security matters most. Options include:

  • Encrypted env files with the key stored in your deployment platform.
  • A secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) that injects secrets at deploy time.
  • Your deployment platform's built-in credential management (like Deploynix's environment variable management).

Common Secrets Management Mistakes

Understanding what goes wrong helps you avoid the same pitfalls.

Hardcoding secrets in source code. This seems obvious, but it happens constantly. API keys embedded in JavaScript files, database passwords in config files, tokens in migration scripts. Use environment variables for everything.

Using the same secrets across environments. If your staging and production databases share credentials, a compromise in staging is a compromise in production. Always use unique credentials per environment.

Overly broad access to production secrets. Not every developer needs access to production database credentials. Apply the principle of least privilege. Junior developers working on the frontend have no business seeing the Stripe secret key.

Not rotating secrets after team changes. When a team member leaves, rotate every secret they had access to. This is tedious but critical. Automate it if possible.

Logging secrets accidentally. Laravel's logger can inadvertently capture request data containing API keys or tokens. Configure your logging to redact sensitive fields. Use $hidden on models and $except on middleware to prevent sensitive data from appearing in logs.

Storing secrets in Docker images. Building secrets into a Docker image means anyone with access to the image registry can extract them. Use Docker secrets, environment variables, or mounted volumes instead.

How Deploynix Handles Credential Protection

Deploynix provides a built-in solution for managing production secrets that addresses many of the limitations of plain .env files.

Dashboard-based management:

You can view and edit environment variables directly from the Deploynix dashboard. This eliminates the need to SSH into servers to modify .env files, reducing the risk of leaving sensitive data in terminal history or accidentally breaking the file format.

Secure storage:

Environment variables are stored securely within Deploynix and pushed to your server during deployment. This means your secrets don't need to live in your Git repository (even encrypted) if you prefer to manage them through the platform.

Deployment integration:

When you deploy through Deploynix, the environment file is written to your server with appropriate file permissions (readable only by the application user). The Nginx configuration blocks direct web access to .env and other dotfiles, preventing accidental exposure through misconfigured web roots.

Team access control:

Deploynix supports organizational roles: Owner, Admin, Manager, Developer, and Viewer. You can control which team members have access to server management features. Viewers have read-only access and cannot see or modify environment variables. For stricter credential isolation, limit production server access to Owners and Admins only.

Comparison with external secrets managers:

For most Laravel applications, Deploynix's built-in credential management provides sufficient security. You don't need the complexity of HashiCorp Vault or AWS Secrets Manager unless your compliance requirements or organizational scale demand it.

However, if your application integrates with dozens of third-party services and you need features like automatic secret rotation, dynamic database credentials, or PKI certificate management, a dedicated secrets manager might be worth the additional complexity.

A Practical Secrets Management Workflow

Here's a workflow that balances security with practicality for a typical Laravel application deployed on Deploynix:

1. Local development: Plain .env file with development-only credentials. Never use production values locally.

2. Version control: Commit .env.example with placeholder values documenting every required variable. This serves as documentation for new team members.

3. CI/CD: Store test credentials in your CI platform's secret management. Use SQLite for tests when possible to avoid needing real database credentials.

4. Staging: Manage environment variables through Deploynix. Use staging-specific credentials for all services.

5. Production: Manage environment variables through Deploynix. Restrict dashboard access to Owners and Admins.

6. Secret rotation: Schedule quarterly reviews of all production credentials. Rotate API keys and database passwords. Update through the Deploynix dashboard and redeploy.

7. Incident response: If you suspect a credential has been compromised, rotate it immediately through the Deploynix dashboard and redeploy. Review access logs to understand the scope of the compromise.

Advanced: Environment Variable Validation

Laravel provides a mechanism to validate environment variables at boot time, catching misconfiguration before it causes runtime errors:

// bootstrap/app.php or a service provider
use Illuminate\Support\Env;

if (app()->isProduction()) {
    $required = [
        'APP_KEY',
        'DB_HOST',
        'DB_DATABASE',
        'DB_USERNAME',
        'DB_PASSWORD',
        'MAIL_MAILER',
        'AWS_ACCESS_KEY_ID',
        'AWS_SECRET_ACCESS_KEY',
    ];

    foreach ($required as $key) {
        if (empty(env($key))) {
            throw new RuntimeException("Missing required environment variable: {$key}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This catches deployment errors where a required variable is missing or empty. It's far better to fail loudly at boot than to discover a missing API key when a user tries to check out.

Handling the APP_KEY

The APP_KEY deserves special attention. It's used to encrypt cookies, session data, and anything you encrypt with Laravel's Crypt facade or encrypt() helper.

Critical rules for APP_KEY:

  • Never share the production APP_KEY with anyone who doesn't absolutely need it.
  • Never use the same APP_KEY across environments.
  • If you rotate the APP_KEY, all existing encrypted data becomes unreadable. Plan accordingly.
  • Back up the APP_KEY in a secure location outside your server. If you lose it, you lose access to all encrypted data.

Conclusion

The .env file is a great starting point, but production secrets management requires more thoughtfulness. Use encrypted environment files for version-controlled secret distribution. Implement per-environment isolation with unique credentials. Restrict access based on role and necessity.

Deploynix simplifies the operational side of secrets management by providing dashboard-based credential editing, secure storage, role-based access control, and automatic file permission management during deployment. For most Laravel teams, this eliminates the need for complex external secrets managers while providing meaningful security improvements over manual .env file management.

The best secrets management strategy is one your team will actually follow consistently. Start with the practices that match your current scale, and add complexity only when your security requirements demand it.

Top comments (0)