In a Laravel project, what is the difference between .env and .env.example, and which one belongs in version control?
answer
- one is a template, one is real
- skeleton .gitignore lists .env
- copied on install if missing
- placeholders document required variables
- 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 sLaravel 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# .env.example (committed)
APP_NAME="Gym Pro"
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=sqlite
PAYMENT_API_KEY=
PAYMENT_WEBHOOK_SECRET=go deeper
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.
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.
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.
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