The INI file format has been used to store application settings since the early days of Windows, and it remains in active use across Python projects, PHP configurations, MySQL, Git, and dozens of other tools. Creating one correctly requires understanding a handful of syntax rules, knowing where parsers vary in their behaviour, and choosing the right format for your use case. This guide covers all of it — from the first line to validation.
What is an INI file?
An INI file is a plain text configuration file that stores settings as key-value pairs, optionally grouped into named sections. The name comes from "initialisation" — INI files were used to initialise Windows applications with their settings before the Windows Registry existed. The format never had a formal specification, but a de-facto standard emerged from widespread use.
Where INI files are used today
- Python packaging — `setup.cfg`, `tox.ini`, `pytest.ini`, `mypy.ini`, `.flake8`
- PHP runtime — `php.ini` controls PHP interpreter settings globally
- MySQL / MariaDB — `my.ini` (Windows) and `my.cnf` (Unix) configure the database server
- Git — `.gitconfig` and `.git/config` use an INI-like format for repository and user settings
- Wine — `wine.inf` configures the Windows compatibility layer on Linux and macOS
- Windows applications — thousands of legacy and modern desktop apps store preferences in `.ini` files in the AppData folder
INI vs the Windows Registry
Microsoft moved Windows application settings to the Registry in the early 1990s for reasons of performance and centralised management. However, many developers continue to prefer INI files for portability — an INI file can be inspected and edited with any text editor, committed to version control, and copied between machines without any export/import tooling. The Registry cannot.
Note
INI file syntax rules
Despite the lack of a formal spec, INI syntax follows consistent conventions across virtually all parsers. These are the rules you can rely on regardless of what reads your file.
An INI file is the simplest possible configuration format: sections in brackets, key-value pairs beneath them, and semicolons for comments. Everything else is parser-specific.
Universal rules
- One key-value pair per line — `key = value` or `key=value`; the space around `=` is optional but consistent spacing is readable
- Section headers — `[SectionName]` on its own line; no trailing content after the closing bracket
- Comment lines — start with `;` for maximum compatibility; `#` is supported by some parsers (Python `configparser`, Linux tools) but not Windows native APIs
- Blank lines — ignored by all parsers; use them freely to separate logical groups within a section
- No nesting — INI is flat: sections contain key-value pairs, not other sections
- String values — all values are strings unless the parser converts them; `count = 5` is the string "5" to most parsers
What the parser sees
The parser builds a two-level map: section name → key → value. If a file has no section headers, values are in an implicit "default" section — Python's `configparser` calls this `DEFAULT`. Whether the parser merges the default section with named sections varies. Keys and section names are almost universally treated as case-insensitive by convention, though this is not guaranteed by all implementations.
Tip
Creating your first INI file
Creating an INI file takes fewer than five steps. The only tool required is a plain text editor — any editor that saves as UTF-8 or ASCII without a byte order mark (BOM) works correctly.
Create a new text file with the .ini extension
Open your text editor (VS Code, Notepad, nano, vim — any of them work) and create a new file. Save it with the `.ini` extension before writing any content so the editor applies INI syntax highlighting if available. On Windows, make sure "Save as type" is set to "All files" in Notepad to avoid the file being saved as `config.ini.txt` instead of `config.ini`.
Add your first section header
Write your first section name in square brackets on its own line. Section names are descriptive labels — `[database]`, `[server]`, `[logging]` are conventional choices. You can also start writing key-value pairs immediately without any section header if your config is simple enough to not need grouping.
Add key-value pairs under each section
Below the section header, write one `key = value` pair per line. Keys should be lowercase with underscores (snake_case) for maximum cross-parser compatibility. Values can include spaces, punctuation, and most special characters. Do not wrap values in quotes — quotes are treated as literal characters by most parsers, not as string delimiters.
Add comments to document non-obvious values
Start comment lines with a semicolon (`;`). Comments must be on their own dedicated line — placing a comment after a value on the same line (`host = localhost ; primary DB`) is not reliably supported by all parsers and may include the comment text in the value. If you need inline notes, put them on the preceding line as a standalone comment.
Validate the finished file
Paste your completed INI file into the INI Validator to check for syntax errors, duplicate section names, and format compliance. The validator reports issues with line numbers so you can fix them before putting the file into production. If you need consistent formatting, run it through the INI Formatter first.
INI Validator
Check any INI or CFG file for syntax errors, duplicate sections, and format compliance — line-level error reports with no uploads required.
Sections, keys, and values in depth
The three structural elements of an INI file — sections, keys, and values — have rules and edge cases worth understanding before writing a config that will be read by someone else's parser.
Section naming conventions
Section names go inside square brackets and appear on their own line. They can contain letters, numbers, spaces, and most punctuation — but spaces in section names are poorly supported by some parsers and should be avoided. Use `[DatabaseConfig]` or `[database_config]` rather than `[database config]`. Duplicate section names are either merged or cause an error depending on the parser — treat them as forbidden and validate with the INI Validator to catch duplicates.
Key naming rules
Keys must not contain the `=` sign or a newline. Beyond that, the conventions vary, but the safest practice is to use only lowercase letters, digits, and underscores — the same rules as Python variable names. Avoid hyphens in keys if you plan to read them in Python with `configparser`, since Python returns keys as-is and hyphenated keys cannot be accessed as attributes.
Value types and multiline values
All values in INI files are strings unless your parser explicitly converts them. `enabled = true` is the string "true" — your code must convert it to a boolean. Python's `configparser` provides `getboolean()`, `getint()`, and `getfloat()` methods for this purpose. Multiline values are supported by some parsers (Python's `configparser` treats lines with leading whitespace as continuations of the previous value) but not all — check your parser's documentation before relying on this.
The DEFAULT section
Python's `configparser` treats a section named `[DEFAULT]` (case-insensitive) as a special fallback section. Any key defined in `[DEFAULT]` is available in all other sections as a fallback — if a section does not define a key, the value from `[DEFAULT]` is returned instead. This is a Python-specific behaviour not found in most other parsers. If you are writing INI files for Python specifically, `[DEFAULT]` is a useful way to define shared values without repeating them in every section.
Warning
Reading INI files in code
Most languages provide a built-in or standard library parser for INI files. Here are the standard approaches for the most common environments.
Python: configparser
Python's `configparser` module is the standard way to read INI files in Python. Import it, create a `ConfigParser()` instance, call `.read()` with your filename, and access values with `config["SectionName"]["key"]` or `config.get("SectionName", "key")`. The `.get()` method accepts a `fallback` argument that returns a default value when the key is missing — useful for optional config values. Use `getboolean()`, `getint()`, and `getfloat()` for typed values rather than casting strings manually.
PHP: parse_ini_file()
PHP provides `parse_ini_file($filename, $process_sections)` as a built-in function. With `$process_sections = true`, the function returns a nested associative array organised by section name. With `false`, it returns a flat array with all keys merged. PHP's parser is strict about certain special characters in unquoted values — values containing =, opening/closing braces, |, &, ~, !, [, ] need to be quoted in the INI file to be parsed correctly.
Node.js and other environments
Node.js has no built-in INI parser, but the `ini` npm package (MIT licence) provides a standard `parse()` and `stringify()` interface. For Java, the `org.ini4j` library is the standard choice. For Go, the `gopkg.in/ini.v1` package is the most widely used option. In each case, the library handles the same two-level section/key structure — the API shapes vary but the underlying format is identical.
Tip
INI vs TOML vs YAML
INI is not always the right configuration format. Understanding where it fits — and where TOML or YAML is a better choice — helps you make the right decision for new projects.
| Feature | INI | TOML | YAML |
|---|---|---|---|
| Syntax complexity | Minimal | Moderate | High |
| Native type support | ✗ Strings only | ✓ Full types | ✓ Full types |
| Nested structures | ✗ Two levels max | ✓ Inline tables | ✓ Unlimited depth |
| Arrays / lists | ✗ Not standard | ✓ Native arrays | ✓ Block sequences |
| Comments | ✓ ; and # | ✓ # only | ✓ # only |
| Formal specification | ✗ No official spec | ✓ TOML spec | ✓ YAML 1.2 spec |
| Best for | Simple app config | Rust, Python pkg | DevOps, Kubernetes |
| Readability | Very high | High | Medium (indent-sensitive) |
When to use INI
INI is the right choice when your configuration is two levels deep (sections and flat key-value pairs), when the target parser already expects INI format (PHP, Python ecosystem, MySQL, Git), and when you want the absolute simplest possible format that any developer can read without prior knowledge. It is not appropriate for configurations that need arrays, nested objects, or typed data.
When to use TOML or YAML instead
Choose TOML when your config needs typed values, arrays, or inline tables and you want a strict specification with predictable parsing. TOML is the format for `pyproject.toml`, `Cargo.toml`, and Hugo's configuration files. Choose YAML when you need deeply nested structures or are working in an ecosystem where YAML is already standard — Kubernetes, GitHub Actions, Docker Compose, and Ansible are all YAML-first environments.
Key takeaways
- An INI file is a plain text configuration file with named sections in `[brackets]` and `key = value` pairs beneath them.
- Use `;` for comments — not `#` — for maximum compatibility across Windows, PHP, Python, and other INI parsers.
- Save INI files as UTF-8 without BOM; avoid inline comments (after a value on the same line) as they are not universally supported.
- All INI values are strings unless your parser explicitly converts them — use `getboolean()`, `getint()`, and `getfloat()` in Python.
- Never store passwords or API keys in INI files committed to version control — use environment variables for sensitive values.
- Validate with the INI Validator before deployment to catch syntax errors, duplicate sections, and format problems.
- Use TOML for configs needing typed values and arrays; use YAML for deeply nested structures — INI is ideal only for simple two-level configurations.
Comments and encoding
Comments and character encoding are the two aspects of INI files most likely to cause silent problems when files are shared across different tools, operating systems, or programming languages.
Comment characters: ; vs #
The semicolon (`;`) is the universally supported comment character — it works in Python's `configparser`, Windows native APIs, PHP's `parse_ini_file()`, MySQL, and virtually every other INI parser. The hash (`#`) is supported by Python's `configparser` and most Linux-based parsers but is not supported by Windows' `GetPrivateProfileString()`. If your INI file will only ever be read by Python, either character is safe. For cross-platform files, use `;` exclusively.
Character encoding: UTF-8 vs Windows-1252
Save INI files as UTF-8 without BOM for modern tools. The BOM (byte order mark, the invisible `\uFEFF` character at the start of some UTF-8 files saved by Windows tools) causes problems with parsers that treat it as part of the first key name. Python's `configparser` handles UTF-8 natively since Python 3. If you are writing an INI file for a legacy Windows application that expects Windows-1252 encoding, match what the application expects — mixing encodings is a common source of character corruption in values.
Line endings
INI files work with both Windows (CRLF, `\r\n`) and Unix (LF, `\n`) line endings. Use the line ending convention of your target platform. If you are editing an INI file on Windows for deployment on Linux, configure your text editor to save with LF line endings to avoid the carriage return character appearing in values on Linux parsers. The INI Formatter normalises line endings and spacing in one pass.