Skip to content
FreeNo sign-up

.env linter

Paste a .env file to find lines that loaders read differently, values that run as commands if you source the file, duplicates, and values that look like real keys. The report names lines and variables, never values, so you can share it.

Runs in your browser. Nothing you type is sent anywhere.

The tool is code on this page. It makes no network requests while you use it, stores nothing in cookies or local storage, and keeps working if you go offline after the page loads.

.env linter

Runs in your browser

Paste the file or type into the box. Checks run as you type.

Findings

Paste a .env file to see findings.

Example values use Kiteline, a fictional company.

How to use it

  1. Paste a .env file into the box, or load the example to see every kind of finding.
  2. Read the findings in line order. Each one names the line and the variable, and says which loader reads it which way.
  3. Fix the file in your editor: the linter only reads. Most fixes are quoting a value or removing a space around =.
  4. Copy the report into a ticket or a pull request comment if someone else owns the file. It contains names and line numbers, never values.

The same without a browser

Two quick screens in a terminal. Neither replaces a parser, but neither runs anything in the file.

Syntax check without running the file (catches unclosed quotes)
bash -n .env
Lines with command substitution, backticks, ${…} or an unquoted space
grep -nE '\$\(|`|\$\{|^[A-Za-z_][A-Za-z0-9_]*=[^"'"'"'#[:space:]]+[[:space:]]+[^#[:space:]]' .env

What it checks

Errors break loading somewhere. Warnings mean two tools would read the line differently, or that a value would run as code. Notes are worth a look but often intended.

LevelCheck
ErrorNot a KEY=VALUE lineA line without = is skipped by dotenv loaders, rejected by some platforms, and run as a command by a shell that sources the file.
ErrorName won't work everywherePortable names use letters, digits and underscores and don't start with a digit. Node's dotenv accepts dots and dashes, but a shell can't export them and some platforms reject them.
ErrorSpace around =dotenv trims the spaces, but in a shell KEY = value runs a program called KEY, and KEY= value runs value with an empty KEY. Docker's --env-file rejects a name with a space.
ErrorQuote not closedLoaders either fail or treat the rest of the file as part of the value.
ErrorText after the closing quoteA shell joins text that touches the quote to the value and runs text after a space as a command; dotenv loaders drop it or reject the line.
WarningRuns code when sourcedset -a; source .env runs the file as a shell script: $(…) and backticks execute, $VAR expands, and a space starts a new command. Values are safe to source when they're plain, in single quotes, or in double quotes without $, backticks or backslashes.
Warning# inside an unquoted valueA password like abc#def works in a sourced shell and in Compose, and is silently cut to abc by a loader that follows the dotenv rule. Quote the value.
WarningVariable set twiceDuplicates can come from merging files. Keep one line per variable.
WarningSpace at the end of the valueAn invisible trailing space can make an API key that works locally fail in a container.
WarningLooks like a real keyThe check recognizes the formats of common providers' keys and long random values under names like SECRET or TOKEN. Values marked as examples, such as AKIAIOSFODNN7EXAMPLE, are ignored.
WarningWindows line endingsNode's dotenv strips the carriage return, but a sourced shell doesn't, so a password or URL silently gains an invisible character. Save the file with Unix line endings.
WarningQuote probably not closedMulti-line values are allowed in quotes, but when the lines inside look like assignments, a closing quote is usually missing.
NoteEmpty valueFine in an example file. In a real one, an empty value can mean a variable someone forgot to fill in, and an app may treat empty and missing differently.
Noteexport prefixexport makes the file work with source, but docker run --env-file reads export KEY as a name with a space and fails.
NoteValue spans several linesCertificates and private keys are often multi-line. dotenv and systemd read them inside quotes; Docker's --env-file reads one line per variable.
NoteByte order markSome Windows editors add an invisible U+FEFF at the start of the file. Docker removes it; a shell doesn't.

How Crunu handles this

Checked before a file is loaded

In Crunu, a file marked to load as environment variables is checked when you add it: Crunu flags a file whose values could run as shell commands if it's sourced. crunu run then passes variables straight into the program's environment, decrypted in memory, so no shell parses them and nothing is written to disk.

Read about crunu run
Example. Kiteline is a fictional company.

Questions about this tool

Found a case the tool gets wrong? Write to support@crunu.com

Which .env rules does the linter follow?

The common dotenv rules: KEY=VALUE, an optional export, # comments at the start of a line or after a space, and values in single, double or backtick quotes, which may span lines. Where Node's dotenv, a shell, Docker and Docker Compose disagree, the linter says so instead of picking one.

Why is a value with a space or a $ flagged?

Because some setups load .env with set -a; source .env, which runs the file as a shell script. Unquoted, Hello World runs a program called World, and $(…) runs a command. Single quotes make any value literal for a shell.

Does the linter fix the file?

No. It only reads what you paste and lists findings with line numbers. Fix the file in your editor, then paste it again to confirm.

Is it safe to paste a file with real keys?

The check runs in your browser and nothing is sent, so the values don't leave your machine. If your rules forbid pasting production secrets anywhere, lint a copy with the values replaced: every check except the real-key detector works the same.

When the values need a home

Crunu is a vault for your team's environment variables and keys. Values are encrypted on your devices, so the server stores them only as ciphertext. Free during early access.