Command-line tools, FTP clients, automation scripts, and HTTP libraries often need to locate credentials without asking the user to type a login and password on every run. The .netrc format originated in the Unix ecosystem as a host-oriented credential file. Python netrc parses that format, validates its syntax, and exposes a compact API for retrieving login, account, and password values.
The convenience comes with serious security responsibilities. Loose permissions, accidental logging, unsafe backups, and selecting the wrong host can expose secrets. This guide explains parsing, the default entry, diagnostic errors, POSIX permission checks, atomic updates, redirect risks, testing, and when a dedicated secret manager is a better choice.
It complements our guides to Python shlex, tempfile, atexit, zoneinfo, and mimetypes.
Basic file structure
A common entry uses the machine keyword, a host name, and authentication fields.
machine api.example.com
login user
password secret-valueThe historical account field may also be present, although many modern services do not use it.
Default location
When no filename is supplied, netrc.netrc() reads .netrc from the user’s home directory as resolved by os.path.expanduser().
from netrc import netrc
credentials = netrc()If the default file does not exist, initialization raises FileNotFoundError. This lets an application distinguish missing configuration from invalid syntax.
Reading an explicit file
from netrc import netrc
credentials = netrc("/etc/my-app/credentials.netrc")An explicit path gives deployment tooling control over ownership, permissions, and mount location. Service files should live in a restricted directory rather than a shared writable area.
Looking up a host
authenticators() returns a tuple containing login, account, and password.
auth = credentials.authenticators("api.example.com")
if auth is None:
raise RuntimeError("credentials are missing")
login, account, password = authModern Python versions can return empty strings for omitted fields. Validate every field required by the target client before opening a connection.
The default entry
The format supports a special fallback entry.
default
login guest
password shared-secretauthenticators() checks the exact machine first and then uses default. This is convenient but dangerous when a typo or an attacker-controlled host causes a generic credential to be sent to the wrong server. Sensitive applications should avoid the fallback or enforce an explicit host allowlist.
POSIX permission checks
When the default file contains passwords, Python checks ownership and permissions on platforms that support os.getuid(). If another user can read or write the file, parsing raises NetrcParseError.
chmod 600 ~/.netrcThe file should be owned by the account running the process. In containers, verify the effective UID and the ownership of mounted volumes.
Other operating systems
Platforms without os.getuid() cannot apply the same automatic rule. That does not make file protection optional. Use operating-system ACLs, profile protection, encrypted disks, and appropriate service-account policies.
Handling parse errors
Syntax problems raise NetrcParseError, whose instances expose a message, filename, and line number.
from netrc import netrc, NetrcParseError
try:
config = netrc()
except NetrcParseError as error:
print(error.msg)
print(error.filename)
print(error.lineno)Show enough context for an operator to repair the file, but do not log the full source line because it may contain a password.
UTF-8 and special characters
Since Python 3.10, the parser tries UTF-8 before a locale-specific encoding. Tokens may include non-ASCII characters and whitespace. Compatibility with older external clients can still vary.
When the same file is shared with third-party software, test spaces, accented characters, and escaping on the actual target systems.
Optional fields
Recent Python versions no longer require every token. Missing values default to an empty string.
machine token-only.example
password abc123A parser may accept this entry even when the consuming library requires a login. Application-level validation should fail early with a clear message.
Inspecting hosts safely
The instance exposes a public hosts dictionary.
for host in credentials.hosts:
print(host)Diagnostic tools can report configured host names and whether required fields exist. They should never print tuple values or serialize the complete object.
Macros
The historical format supports macros, exposed through the macros dictionary. They were designed for FTP clients and are uncommon in modern applications.
If credentials are the only requirement, ignore macros. Never interpret their text as shell commands unless you have designed and secured a separate execution system.
The repr output contains secrets
repr(config) emits a netrc-style representation. It discards comments and may reorder entries, but more importantly it includes credentials.
Do not send this representation to logs, error trackers, telemetry systems, or support tickets. It is also unsuitable as a comment-preserving editor.
HTTP client integration
def credentials_for(host: str):
auth = config.authenticators(host)
if auth is None:
raise LookupError(f"credentials missing for {host}")
login, _, password = auth
if not login or not password:
raise ValueError("empty login or password")
return login, passwordValidate the host before lookup. An attacker-controlled URL must not be able to select arbitrary internal credentials.
Redirect risks
HTTP clients may follow redirects to another domain. Ensure that authorization values are not forwarded to a different host. A safe policy compares normalized host names and, when relevant, ports and URL schemes.
Host names and ports
A netrc entry usually contains a machine name rather than a full URL. Define how services on different ports are represented and confirm the lookup behavior of the consuming library.
Normalize host names carefully. DNS names are case-insensitive, may contain a trailing dot, and internationalized names need consistent IDNA handling.
Environment variables versus netrc
Environment variables are convenient in containers and CI, but may appear in process inspection, diagnostics, child processes, or administrative interfaces. Netrc organizes credentials by host but depends on file protection.
Neither method is universally safe. Choose according to the threat model, rotation requirements, deployment platform, and audit needs.
When to use a secret manager
A dedicated vault is usually preferable when:
- many servers or teams share credentials;
- automatic rotation is required;
- access must be audited;
- credentials are short-lived;
- infrastructure is shared;
- policy requires centralized encryption and revocation.
Netrc remains useful for local developer tools and clients that already support the format.
Never commit the file
Add .netrc and project-specific credential files to .gitignore. Inspect Git history, CI artifacts, container layers, and backups as well. Removing a secret from the latest commit does not erase earlier versions.
Credential rotation and atomic updates
Write replacements atomically. Create a temporary file with restrictive permissions, write and flush the new content, call fsync when the durability requirement justifies it, and replace the destination.
This avoids a window in which another process reads a partially written credential file.
Testing without real secrets
from pathlib import Path
from netrc import netrc
content = """machine test.local
login example-user
password example-secret
"""
path = Path("test-credentials.netrc")
path.write_text(content, encoding="utf-8")
config = netrc(str(path))Use temporary directories and synthetic values. On POSIX, set modes explicitly when testing permission-sensitive behavior.
Testing permission failures
Create one secure file and one file readable by a group or other users. The automatic check depends on using the default path and on platform UID support, so tests should be conditional and document platform differences.
Safe error messages
A useful error identifies the host, configuration path, and problem category. It excludes passwords, complete entries, and object representations.
raise RuntimeError(
f"credentials for {host!r} are not configured"
)Common mistakes
- Leaving the file readable by other users.
- Logging
repr(config). - Trusting the
defaultentry for every host. - Forwarding credentials after a cross-domain redirect.
- Letting external input select a host.
- Committing secrets to source control.
- Ignoring empty fields.
- Assuming POSIX checks exist everywhere.
Best practices
- Use mode 600 and correct ownership on POSIX.
- Validate host, login, and password before connecting.
- Never log credential content.
- Avoid
defaultin sensitive systems. - Update files atomically.
- Use synthetic credentials in tests.
- Block authorization forwarding across hosts.
- Move to a vault when rotation and auditing are required.
Conclusion
Python netrc provides a compatible, compact way to read credentials by host. Its small API integrates naturally with network clients and command-line tools.
Security depends on the surrounding design: file permissions, host selection, logs, redirects, backups, and secret governance. Use netrc for controlled local configuration and adopt a secret manager when the environment requires rotation, auditing, or distributed delivery. Consult the official netrc documentation and the netrc format documentation for interoperability details.







