Original config
Changed config

Config Diff: Compare INI, TOML, .env Files Online

Paste two configuration files side-by-side and see exactly which keys, values, and comments changed. Works for .env, TOML, INI, .properties, and .conf.

What is the config diff tool?

A free, in-browser tool for diffing configuration files. Paste a staging .env on the left, the production one on the right, and the missing keys, changed values, and stale comments light up immediately. Same workflow for a pyproject.toml before and after a Poetry update, an nginx.conf you are about to reload, or a Java application.properties moving between environments.

The diff is character-level. There is no parser, no schema, no opinion about which dialect you are pasting. That is on purpose. Configuration files do not share a single grammar. INI has no formal spec and every parser disagrees about quoting and escapes. TOML 1.0 has a strict grammar but adds typed values. .env files have no spec at all, just whatever dotenv or your shell happens to do.

If you came here looking for plain prose, our text diff tool is the right pick. For YAML configuration (Kubernetes manifests, Helm values, Ansible playbooks), use YAML diff. For config that ships as JSON (VS Code settings, npm package.json, Terraform tfstate), JSON diff handles object reordering more cleanly than a text diff can.

How the diff actually works

The diff runs at the character level, then a semantic post-processing pass shifts the highlights so they land on whole keys, whole values, and whole comment lines rather than mid-token noise. Insertions show in green on the right pane, deletions in red on the left. The change counter in each header tells you how many distinct edits the algorithm found.

Configuration formats vary enough that one parser cannot serve all of them, so this tool deliberately stays format-agnostic. .env is the loosest of the lot: most loaders treat the whole right-hand side as a string, but some expand $VAR and some do not, some honour export, and quoting around values with spaces is parser-specific. .properties files (the Java standard, documented in the java.util.Properties API) accept either = or : as a separator and require Unicode escapes like \u00e9 for non-ASCII characters. INI files in the wild range from the Windows convention (sections in square brackets, semicolons for comments) to the slightly different dialect Python's configparser module accepts.

Three real-world gotchas worth knowing before you trust a clean diff. First, trailing whitespace. A value like API_KEY=abc with a stray space at the end of the line is a different string to most loaders, and the diff will flag it but humans rarely see the offending space. Second, line endings. A file checked in on Windows with CRLF and one written on Linux with LF will diff as identical content but show every line as changed; format both sides or normalise endings first. Third, the UTF-8 byte order mark. Notepad on Windows still happily writes a BOM at the start of a saved .env, which most loaders trip over and which presents as a phantom one-character diff at line 1.

How to compare config files in three steps

Two text panes, one diff. Nothing is uploaded. Nothing is logged.

  1. 1

    Paste or upload your config

    Paste the old config on the left, the new one on the right. Or click Upload on either side to load a .env, .toml, .ini, .properties, or .conf file directly. The Sample button fills both panes with a small .env example so you can see how value changes, new keys, and removed entries render before you paste your own.

  2. 2

    Format and normalise

    Click Format on each pane to trim trailing whitespace and apply consistent line endings. This kills the most common false-positive class: two files that look identical but diff as completely different because one was saved with CRLF and the other with LF. The diff focuses on real key and value changes once both sides agree on whitespace.

  3. 3

    Read the diff

    Deletions appear with a red highlight on the left, insertions with a green highlight on the right. Scroll either side and the other follows. Comments and commented-out keys are diffed as ordinary text, so a key you removed by prefixing with # shows up as the comment line being added rather than the key being deleted.

When config diff is the right tool

Diffing .env files between local and staging

You spin up a feature branch locally, it works fine, then it crashes on staging because STRIPE_WEBHOOK_SECRET is missing from staging's .env. Paste the working local file against the staging one and the missing key, the changed DATABASE_URL host, and the stale LOG_LEVEL=debug all surface in one pass. Faster than walking through both files line by line, and a lot safer than running a script that prints both sets of variables to a terminal.

Comparing pyproject.toml after a dependency upgrade

Poetry or uv just rewrote your pyproject.toml after poetry update. Diff the previous committed version against the working tree to confirm the actual upgrades: fastapi moved from 0.110.0 to 0.115.4, a new [tool.ruff.lint] table appeared, and the build-system.requires pin shifted. The TOML 1.0 spec is strict enough that the diff is normally clean and easy to read.

Reviewing nginx.conf changes before reloading

Reloading nginx with a broken config takes a production site down faster than almost anything else on a server. Before the nginx -s reload, paste the running nginx.conf against the proposed one and confirm the change is exactly what you intended: a new upstream block, an updated proxy_pass target, a TLS cipher suite swap. The diff catches the accidental missing semicolon every time.

Auditing a systemd unit or supervisord config

A service is restarting in a loop after a deploy. Diff the working foo.service against the new one and you usually find the cause inside thirty seconds: a changed ExecStart, a dropped Environment= line, a tightened NoNewPrivileges. Same workflow for supervisord .conf blocks, runit run-scripts, and the various .ini-flavoured init configs you still see on older boxes.

Comparing infrastructure-as-code config files

Terraform variables in terraform.tfvars, Pulumi stack configs, Ansible group_vars, Helm values for Kubernetes ConfigMaps. Each environment has its own copy and the only safe way to merge them is to diff. Paste prod against staging and the drift jumps out: a region pinned to us-east-1 in one and us-west-2 in the other, an instance type that someone bumped during an incident and never reverted, an IAM role ARN that points at the wrong account.

Checking a CI environment file matches the documented template

A new engineer joins, copies the .env.example from the repo, and the build still fails on their first PR because the template has drifted from what the CI pipeline actually expects. Diff the committed .env.example against a known-good .env from someone whose builds pass and the missing keys show up as red on the left or green on the right. Same trick works for Taskfile env blocks, GitHub Actions env: sections, and direnv .envrc files.

Config format quick reference

A short cheat sheet across the formats this tool sees most often. Configuration is messier than its reputation suggests, and the differences below are where most diffs go sideways.

TopicWhat this tool does
Format dialectsINI (no formal spec, dialects per-parser), TOML 1.0 (strict, typed), .env (no spec, parser-defined), .properties (Java, see java.util.Properties), .conf (per-app: nginx, Apache, sshd_config all differ).
Comment charactersINI: ; or #. TOML: # only. .env: # only, and only at start of line in most loaders. .properties: # or !. nginx .conf: # only.
Section headersINI and TOML use [section]. TOML adds nested tables via [a.b.c] and arrays of tables via [[a]]. .env and .properties are flat, no sections. nginx uses { ... } blocks instead of headers.
Value typesTOML has typed values: strings, ints, floats, bools, dates, arrays, tables. INI, .env, and .properties are string-only; the application decides how to parse true or 42.
Interpolation and expansionMost dotenv loaders do not expand $VAR by default. Some (dotenv-expand, direnv, shell sourcing via source .env) do. TOML never expands. INI rarely expands; Python configparser supports ${section:key} if you opt in.
Trailing whitespaceA real bug source. FOO=bar with a trailing space sets FOO to "bar " in most loaders, which fails string equality checks downstream. Always trim or quote.
Line endingsCRLF (Windows) vs LF (Unix). Two identical files saved on different OSes diff as every-line-changed. Use the Format button or normalise endings (dos2unix, git config core.autocrlf) before diffing.
Encoding and BOMTOML mandates UTF-8. .properties historically required ISO-8859-1 with \uXXXX escapes, but modern Java accepts UTF-8 via Properties.load(Reader). A UTF-8 BOM at the start of a .env file is a classic phantom-diff cause; Notepad on Windows still writes one by default.

Config diff: frequently asked questions

Does the tool understand value types?

No. It is a text diff. TOML distinguishes strings, integers, floats, booleans, dates, and arrays, but the diff treats every line as plain text and shows you which characters changed. That is usually fine because configuration changes are character changes anyway. If you genuinely need a typed compare, parse both files in your language of choice (Python's tomllib, Rust's toml crate, Go's pelletier/go-toml) and compare the resulting maps. For the 95% case, the text diff catches what you need to catch.

How do I handle commented-out lines that "differ" but are equivalent?

You usually do not, and you should be glad the tool flagged it. A line that goes from API_KEY=abc to #API_KEY=abc is semantically meaningful: the key is now disabled. The diff highlights the comment-prefix change, which is exactly what a human reviewer should see during a config review. If you really want to ignore comment churn, format both sides to drop comment-only lines before pasting, but that is rarely the right move for a security-relevant file.

How do I check if a key is missing on one side?

Paste both files, run the diff, and any key present on one side and absent on the other shows up as a red deletion or a green insertion of the whole line. The diff does not try to align keys structurally, so a key reordered to a different position will appear once as a deletion and once as an insertion. For a fully order-insensitive compare, sort both files by key first (sort .env on Unix) and then diff. The format-agnostic approach catches missing keys without needing a parser.

Is it safe to paste .env files with real secrets?

Yes, your input never leaves your browser. The diff runs entirely on the client. There is no server upload, no telemetry on the contents of either pane, no analytics that capture what you typed. That is intentional because .env diffs are one of the highest-trust workflows on this site. If you are paranoid (and you should be a little paranoid), open browser devtools and confirm the network tab stays empty as you paste. For organisations that prefer a fully air-gapped tool, run the site offline once and the cached assets work without any further network access.

What is the difference between TOML and INI?

INI is a convention; TOML is a specification. INI grew up on Windows, has no formal grammar, and every parser handles edge cases (quoting, escapes, list values, nested sections) slightly differently. TOML 1.0 is a strict, versioned spec used by Cargo, Poetry, Hugo, and increasingly Python packaging via pyproject.toml. Both use [section] headers and key = value pairs, so they look similar at a glance, but TOML adds typed values, datetime literals, and inline tables that classic INI files do not have.

Does the diff normalise key casing or whitespace?

No. API_KEY=abc and api_key=abc are different lines to the diff because they are different bytes. Most loaders are case-sensitive too (dotenv, java.util.Properties, configparser by default), so this matches reality. Whitespace around the = is preserved as-is, which matters because a few stricter parsers (notably some Bash sourcing patterns) reject FOO = bar with spaces and only accept FOO=bar. The Format button trims trailing whitespace on each line, which is the only normalisation step it applies.

Can I compare an INI file against a TOML version of the same config?

You can, but the diff will be noisy. Text diff is format-blind, so an INI section header [database] will line up with TOML [database], but quoted versus unquoted strings, comment characters (# versus ;), and array notation (a,b,c versus ["a","b","c"]) all flag as changes even when the semantic value is the same. The pragmatic move is to convert both sides to the same format first. For .env to TOML 1.0, a small script wrapping each KEY=value as key = "value" is enough. The diff that remains is the real semantic delta.

Privacy and how this works

Your config files never leave your browser. The diff, the formatter, and every byte of your input stay on your machine, locally. No analytics on the contents of either pane, no logs, no upload. That matters more here than on most diff tools because .env files routinely contain database passwords, API keys, and OAuth client secrets, and pasting them into a tool that round-trips through a server would be a real security incident. Background reading on the formats: TOML 1.0, INI files, and the Unix concept of environment variables. For secrets that must not live in a file at all, look at sops, HashiCorp Vault, or AWS Parameter Store.