Environment Variables Reference
Overview v2.3.0+
Section titled “Overview ”Ahoy can load environment variables from files before executing any command. Files are declared using the env field at the config level (global) or within individual commands (per-command). Both levels accept the same three forms:
- A single file path:
env: .env - An array of file paths v2.5.0+
- An array mixing file paths with
{ path, optional }mapping entries v3.0.1+, which mark the files that are allowed to be absent - see Optional env files
File path resolution
Section titled “File path resolution”All env file paths are relative to the directory containing the .ahoy.yml file - not your current working directory. This ensures consistent behaviour regardless of which subdirectory you run commands from.
Precedence
Section titled “Precedence”Variables are applied in this order, from lowest to highest priority:
- Shell environment - variables already in your shell when you run
ahoy - Global env files - loaded in the order listed under the top-level
envkey - Command-specific env files - loaded in the order listed under a command’s
envkey
Later sources override earlier ones. A command-specific variable always wins over a global one with the same name.
Optional env files v3.0.1+
Section titled “Optional env files ”Each entry under an env key is required by default. When a required file is absent, Ahoy
writes a warning to stderr and carries on:
[warn] environment file '.env.local' not found, continuing without it. Add 'optional: true' to that entry if it is meant to be absent.The command still runs, and the exit code is unchanged; only the reporting is new. Because the warning goes to stderr, a command’s stdout stays clean for piping.
To declare a file that is expected to be missing, write the entry as a mapping with path and
optional:
env: - .env.base # Required - path: .env.local optional: true # Absent without comment - path: .env.deploy optional: false # Same as the plain string formoptional means the same thing here as it does for
imports: the entry may legitimately not resolve to a file. It is set on
the env entry itself, though - the command-level optional flag covers imports only, and does not
silence a missing env file.
The two forms mix freely within one list, and both work at the global and per-command level. Every
pre-v3.0.1 form parses unchanged: the env: .env shorthand, plain lists, empty lists and nulls.
ahoy config validate reflects the distinction:
🌍 Environment Files: ✅ .env.base (global) ➖ .env.local (global) - missing, marked optional ❌ .env.missing (global) - missingRuntime variables v3
Section titled “Runtime variables ”Ahoy automatically injects two variables into every command’s environment, regardless of any env file configuration:
| Variable | Value |
|---|---|
AHOY_COMMAND_NAME |
The name of the command being run |
AHOY_CMD |
Path to the ahoy binary |
These cannot be overridden by env files.
File format specification
Section titled “File format specification”Ahoy env files use standard shell variable format. The parser follows these rules:
- One variable per line:
NAME=VALUE - Lines starting with
#are treated as comments and ignored - Empty lines are ignored
- Leading and trailing whitespace on each line is trimmed
- The
exportkeyword prefix is accepted but has no effect - Variable references within values (e.g.
${OTHER_VAR}) are not resolved by Ahoy - they are passed through as literals and may be expanded by the shell when the command runs
Examples
Section titled “Examples”# Simple valuesDB_HOST=localhostDB_PORT=3306APP_NAME=myapp
# Values with spaces (quotes are part of the value - the shell strips them)FULL_NAME="Jane Smith"GREETING='Hello, World!'
# Comments are fully ignored# This line does nothing
# Inline comments are also ignoredAPI_KEY=abc123def456 # production key
# Empty value (sets the variable to an empty string)OPTIONAL_VALUE=
# Export prefix (accepted but no special meaning)export DATABASE_URL=postgres://user:pass@localhost/myapp
# Multi-line valuesSSH_KEY="-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEA...-----END RSA PRIVATE KEY-----"What the parser does not support
Section titled “What the parser does not support”- Variable interpolation within the file (
API_URL=${BASE_URL}/apiis passed through literally) - Backslash continuation across lines
- Shell arithmetic or command substitution
If you need these, set variables in a shell script and call that script from your cmd.
Variable naming
Section titled “Variable naming”By convention, environment variable names use UPPER_SNAKE_CASE. Names can contain letters, digits, and underscores. Names starting with a digit are technically invalid in most shells.
Security
Section titled “Security”Never commit real credentials to version control. A common pattern:
project/├── .env.example # Committed - contains placeholder values only├── .env # Not committed - contains real local values└── .gitignore # Contains ".env" pattern.env.env.local.env.*.local*.secretsOrganisation patterns
Section titled “Organisation patterns”By environment:
.env # Shared defaults.env.local # Developer overrides (not committed).env.staging # Staging-specific (may be committed).env.production # Production-specific (not committed)By purpose:
.env # Global project defaults.env.db # Database credentials.env.deploy # Deployment configurationLayered approach (useful for team projects):
env: - .env.defaults # Committed defaults for the whole team - .env.local # Personal overrides, gitignoredAccessing variables in commands
Section titled “Accessing variables in commands”Variables are available through standard shell syntax in bash commands:
commands: connect: env: .env.db cmd: mysql -u$DB_USER -p$DB_PASS -h$DB_HOST $DB_NAMEOr with defaults using bash parameter expansion:
commands: serve: cmd: python -m http.server ${PORT:-8000}When using a non-bash entrypoint, access variables through the appropriate mechanism for that runtime (e.g. process.env.VARIABLE in Node.js, os.environ['VARIABLE'] in Python).