Laravel Multi-Environment Secrets

Laravel Multi-Environment Secrets Strategy

A practical, real-world guide to managing Laravel configuration and secrets across local, staging, and production—without leaks or drift.

Real-world Laravel apps live in at least three environments: local, staging, and production. Same code, different risk levels, different secrets. A sloppy approach leaks credentials, breaks deploys, and slows teams down. A disciplined approach turns environments into a predictable workflow with clear ownership, safe rotations, and reliable rollbacks.

That discipline mirrors the 12-factor “Config” principle, which keeps code portable by injecting environment-specific settings rather than baking them into the repo.


Introduction

Multi-environment planning is about more than three .env files. It means:

  • Separating code from configuration so the same build can run anywhere.
  • Reducing leak paths (Git history, logs, backups, screenshots, support tickets).
  • Keeping environments aligned while still allowing safe experimentation in dev.
  • Making deploys repeatable and auditable with minimal manual steps.

Laravel environment basics

  • APP_ENV tells Laravel which environment you are in (local, staging, production, etc.). Hosting platforms often inject this for you.
  • .env is loaded at runtime and must not be committed. .env.example documents expected keys without secrets.
  • For production, prefer injected environment variables or a secret manager instead of plaintext files on disk.
  • Keep env() calls inside config files; use config() in application code so config caching behaves.

Need a template to start from? Use the pattern in our Laravel .env.example guide so every environment begins with the same documented baseline.


Typical multi-environment layout

A simple but effective layout:

Environment Purpose Secrets / Config handling
Local Developer work, rapid iteration .env copied from .env.example; personal credentials only
Staging Pre-production testing, QA, demos .env.staging or injected variables; secrets kept outside Git
Production Live traffic, compliance, high trust Injected environment variables or secret manager; avoid plaintext files on disk

How to manage multiple environments

Option A: separate .env-style files

# On staging or production during deploy
cp .env.production .env
php artisan config:cache
php artisan migrate --force
  • Pros: simple to grasp, low tooling overhead.
  • Cons: risk of commits or leaks, no access control, poor audit trail, harder rotation.

Option B: injected environment variables / secret manager

  • Use platform- or OS-level environment variables, container runtime variables, or a dedicated secret manager.
  • Keep secrets encrypted at rest, scoped by environment, and traceable with audit logs.
  • Rotate credentials without shipping new code or artifacts.

Teams typically reach for HashiCorp Vault, AWS Parameter Store, or platform-specific secret stores to make this flow repeatable.

Recommended: templates + injected secrets

Commit templates (.env.example, optionally .env.staging.example) for documentation, but inject real values from a secret manager during deploy. This keeps expectations visible while secrets stay out of Git and build outputs.


Best practices & security

  • Never commit real secrets. Only commit templates and obvious placeholders.
  • Use uppercase, underscored variable names; avoid spaces or punctuation.
  • Validate required keys early (bootstrap scripts or service providers) to fail fast on missing configuration.
  • Cache config in staging/production (php artisan config:cache), but remember to warm it after updates.
  • Back up secret stores securely and track who can read or rotate credentials.
  • Document ownership: who updates staging vs. production values, and how rotations are approved.

Migration strategy: single env to multi-env + secret manager

  1. Audit every environment variable your app relies on; note which are sensitive.
  2. Create environment-specific templates (.env.staging.example, .env.production.example) and refresh .env.example.
  3. Choose a secret manager or hosting-level env store; populate it per environment.
  4. Update deploy scripts/CI to inject variables for staging and production, then run config:cache and any migrations.
  5. Refactor code to read via config() so caching is safe.
  6. Document the workflow: who adds keys, how to rotate, how to onboard new teammates.
  7. Test in staging first. Verify expected keys, log redaction, and that secrets never touch artifacts.

When to use which approach

  • Local development: .env from .env.example; developer-owned credentials.
  • Shared staging: injected env vars or a secrets manager; optional .env.staging if tightly controlled.
  • Production: always injected env vars or secret manager; avoid plaintext files; enforce access controls and audit logs.
  • Frequent rotations or multiple services: secret manager + environment-aware config, with validation to prevent drift.

How Ghostable helps

  • Environment templates: keep .env.example accurate and in sync with staging/production expectations.
  • Validation: catch missing or extra keys before deploy; enforce parity across environments.
  • Secure sharing: share secrets without passing around files; audit who accessed what, and when.
  • Rotation and drift control: rotate credentials centrally and push updates without leaking into Git or artifacts.

Want these guardrails without gluing tools together? Ghostable ships environment templates, validation, and audited sharing out of the box.


Conclusion

Treat environments as first-class citizens. Document expected keys, keep secrets out of Git, inject values from a secure store, and validate at deploy time. With a clean workflow, local development stays fast, staging stays trustworthy, and production stays locked down.

FAQ

Only commit templates (e.g., .env.example, .env.staging.example). Real secrets belong in a secret manager or injected environment variables on the server.
Keep env() calls inside config files and read via config() elsewhere. This avoids surprises with config caching and keeps configuration centralized.
Generate a unique APP_KEY per environment with php artisan key:generate. Never commit real keys, and rotate if a key is exposed.
Store them in your CI/CD secret store and inject as environment variables during deploy. Avoid baking secrets into build artifacts.
Use a single source of truth for expected keys (.env.example or config validation), validate during deploy, and keep changes synchronized through PRs and a secret manager.

Want product news and updates?

Sign up for our newsletter.

Email Address

We care about your data. Read our privacy policy.