skip to content

Config Files & Environment

Laravel reads .env values into config/*.php files that you query with config(); calling env() elsewhere breaks once config is cached. A favourite trap question for mid-level candidates.

on this pageshow

explore

questions

6

In a Laravel project, what is the difference between .env and .env.example, and which one belongs in version control?

level: juniorimportance: must knowfreq 68%

answer

  1. one is a template, one is real
  2. skeleton .gitignore lists .env
  3. copied on install if missing
  4. placeholders document required variables
  5. server variables beat .env values

basics

~10 s

.env holds this machine's real settings and secrets and is git-ignored; .env.example is the committed template listing every variable the app needs, with placeholder values. Laravel copies .env.example to .env on install.

solid answer

~40 s

Laravel reads per-machine settings from `.env` through the phpdotenv library, and the config files pull them in with `env()`. `.env` contains real values (database password, payment keys), differs per developer and server, and the skeleton's `.gitignore` excludes it. `.env.example` is the committed template: every variable the app expects, with safe placeholders, so a new teammate can see what to fill in. The skeleton's Composer scripts copy `.env.example` to `.env` when no `.env` exists (on `create-project` and in the `setup` script). When you add a variable, add it to `.env.example` in the same commit. Values are strings except the reserved words `true`, `false`, `null` and `empty`, and a variable already set in the real server environment wins over the same key in `.env`.

code

ini · 7 lines
ini
# .env.example (committed)
APP_NAME="Gym Pro"
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=sqlite
PAYMENT_API_KEY=
PAYMENT_WEBHOOK_SECRET=

go deeper

for a junior

Know which file is committed (.env.example) and which is git-ignored (.env), and that adding a new variable means updating the example file in the same change.

for a middle

Explain the Composer script that copies the template, the immutable repository that lets real environment variables win, and the reserved values env() converts to booleans and null.

for a senior

Treat an .env that reached Git history as a leaked set of credentials, and design deploys so production values arrive as real environment variables or through Laravel's encrypted environment files.

for a principal

Set a team rule that every new setting ships with its .env.example entry and a config file mapping, so onboarding and new environments never depend on someone's private notes.

## Two files with two jobs A Laravel app keeps anything that differs between machines out of its code and out of `config/*.php`. Those values live in a plain text file at the project root, parsed by the **phpdotenv** library at boot. | | `.env` | `.env.example` | |---|---|---| | Contains | real values for this machine | every variable name, with placeholders | | Secrets | yes (database password, payment key) | never | | In Git | no, the skeleton's `.gitignore` lists it | yes, committed and reviewed | | Read by Laravel at boot | yes, unless config is cached | no, it is only a template | | Who edits it | each developer or server | whoever adds or removes a setting | For a gym-membership app, `.env` on a laptop might point `DB_CONNECTION` at SQLite and hold a sandbox `PAYMENT_API_KEY`, while production has a different database and the live key. `.env.example` lists both variable names with dummy values. ## How a new clone gets its `.env` The laravel/laravel skeleton wires the copy into Composer: 1. `composer create-project` runs the `post-root-package-install` script, which copies `.env.example` to `.env` if no `.env` exists. 2. A teammate cloning an existing repo runs `composer run setup`; its steps include the same copy, then `key:generate` and the migrations. 3. The developer then edits `.env` with their own values. Because the copy only happens when `.env` is missing, it never overwrites someone's existing file. ## The format - One `KEY=value` per line; lines starting with `#` are comments. - Values containing spaces go in double quotes: `APP_NAME="Gym Pro"`. - Everything is a **string**, except reserved values that `env()` converts: `true`/`(true)` become boolean `true`, `false`/`(false)` boolean `false`, `null`/`(null)` become `null`, and `empty`/`(empty)` an empty string. - The config files decide the final type. The skeleton's `config/app.php` still wraps the debug flag in `(bool)` to be safe. ## Who wins when a variable is set twice Laravel builds phpdotenv's repository as **immutable**: loading `.env` never overwrites a variable that already exists in the process environment. So a `PAYMENT_API_KEY` set by the server, container or shell takes precedence over the line in `.env`. That lets a deployment platform inject secrets as real environment variables while developers keep using `.env` locally. ## Habits interviewers check - **Never commit `.env`.** If it was committed once, removing the file does not remove it from history; the values in it must be treated as exposed. - **Keep `.env.example` in step with the code.** A pull request that adds `env('PAYMENT_WEBHOOK_SECRET')` to a config file should add `PAYMENT_WEBHOOK_SECRET=` to `.env.example`; otherwise the next clone fails in a confusing way. - **Placeholders, not real values**, in the example file, even for "harmless" sandbox keys. - **Code reads `config()`, not `env()`.** The `.env` file feeds config files; application code reads the config keys. - **Environment-specific files exist too.** A `.env.testing` or `.env.staging` file can replace `.env` for that environment; which one loads depends on `APP_ENV` and the `--env` option. ## When a variable is missing If a variable is absent from both `.env` and the real environment, `env()` returns the default given in the config file, or `null` when there is none. That is why the config files carry sensible defaults for most values (`env('APP_ENV', 'production')`, `env('DB_CONNECTION', 'sqlite')`) and no default for secrets. A secret with no default fails as `null`, which is easier to spot than a wrong-but-plausible fallback. When the gym app adds a payment webhook, its config entry should read `env('PAYMENT_WEBHOOK_SECRET')` with no default, and `.env.example` should list the name with an empty value. A few more format details trip people up: - Spaces around `=` are not needed; write `KEY=value`. - Values containing spaces or a `#` are safest in double quotes, so the parser cannot mistake part of them for a comment. - Variables can reference earlier ones: `APP_NAME="Gym Pro"` then `MAIL_FROM_NAME="${APP_NAME}"`, as the skeleton's `.env.example` does. ## A small `.env.example` ```ini APP_NAME="Gym Pro" APP_ENV=local APP_DEBUG=true DB_CONNECTION=sqlite PAYMENT_API_KEY= PAYMENT_WEBHOOK_SECRET= ``` The empty values are deliberate: the names document the requirement, and each developer fills their own `.env`.

  • The server sets PAYMENT_API_KEY as an environment variable and .env also contains it. Which value does Laravel use?
    The server's. Laravel builds phpdotenv's repository as immutable, so loading `.env` never overwrites a variable already present in the process environment. The line in `.env` is simply ignored for that key. This is what lets a hosting platform inject production secrets while `.env` still works on laptops.
  • What does env() return for a .env line such as FEATURE_TRIALS=false?
    Boolean `false`, not the string "false". Laravel's `Env` class converts the reserved words `true`, `false`, `null` and `empty` (and their parenthesised forms) to real types. Any other value, including `0` or `yes`, comes back as a string, so cast it in the config file if you need a number.

saying these in an interview costs you the question

  • Commit .env so every developer shares the same settings
  • .env.example is loaded as a fallback when .env is missing
  • Values in .env always override server environment variables
  • FEATURE_TRIALS=false in .env returns the string 'false' from env()
  • Put real sandbox keys in .env.example for convenience
open as a page

In a Laravel gym-membership app, why does env('PAYMENT_API_KEY') in a controller return null in production while it works locally?

level: middleimportance: must knowfreq 78%

basics

~20 s

Production runs with cached configuration, and once config is cached Laravel skips loading .env, so env() sees only real server environment variables. Map the key in a config file and read it with config() instead.

open as a page

In Laravel, where does app()->environment() get its value, and how does an externally set APP_ENV choose which .env file loads?

level: middleimportance: should knowfreq 48%

basics

~10 s

app()->environment() returns the app.env config value, which config/app.php takes from APP_ENV (default 'production'). An APP_ENV set outside the file makes Laravel load .env.{APP_ENV} instead of .env when that file exists.

open as a page

In Laravel 13, why does config('view.paths') return a value when config/view.php does not exist, and how do you change it?

level: middleimportance: should knowfreq 36%

basics

~20 s

Laravel 13 loads the framework's own config files as a base and merges the app's files over them, so view settings exist without a local file. Run php artisan config:publish view to copy it into config/ for editing.

open as a page

How do Laravel's env:encrypt and env:decrypt commands let a team commit its production environment file, and where must the key live?

level: seniorimportance: should knowfreq 26%

basics

~10 s

env:encrypt writes an encrypted copy such as .env.production.encrypted that can be committed; env:decrypt recreates the plain file during deployment. The key must stay outside the repository, typically in LARAVEL_ENV_ENCRYPTION_KEY on the deploy host.

open as a page

In a Laravel queue worker serving several gym branches, why can Config::set('services.payment.key', ...) inside one job charge the next job with the wrong key?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Config::set only changes the in-memory config repository, which lives as long as the PHP process. A queue worker handles many jobs in one process and does not reset config between them, so a key set for one branch leaks into later jobs.

open as a page