Environment Variables
Recommended Spry environment setup using direnv and .envrc, with details on creation, invocation, and workflow integration.
Environment Variables and .envrc in Spry
Spry supports environment-based configuration, allowing you to manage environment specific values in a clean, isolated way.
The recommended method is to use a directory-scoped .envrc file along with direnv.
This ensures all developers or automation pipelines can share consistent environment setups without hardcoding sensitive or system-specific details.
Why use .envrc?
- Isolation: Configuration applies only to the current project directory.
- Consistency: All contributors share the same environment setup.
- Security:
direnvonly loads.envrcafter explicit approval usingdirenv allow. - Automation-ready: Integrates seamlessly with Spry's dev and deployment workflows.
Recommended Setup — Environment Variables and .envrc
The recommended practice is to define your environment variables in a local .envrc file.
When you use direnv, it automatically loads these values each time you enter the project directory.
Create .envrc
Create a file named .envrc in the root of your Spry project directory.
# .envrc (bash/zsh)
export SPRY_DB="sqlite://sqlpage.db?mode=rwc"
export PORT=9227Your project structure should look like this:
Once the file is created, run the following command in your terminal:
direnv allowThis grants direnv permission to load environment variables from .envrc into your shell whenever you enter this directory.
Creating .envrc using a Task
If you prefer automating .envrc creation through a Spry task or script, you can do so using a shell task.
Usage — envrc env -C ./.envrc Syntax
Sometimes, you may see documentation or examples using:
```envrc env -C ./.envrc --gitignore --descr "Generate .envrc file and add it to local .gitignore if it's not already there"
export SPRY_DB="sqlite://scf-2025.3.sqlite.db?mode=rwc"
export PORT=9227
```What does this mean?
This syntax illustrates how environment variables can be defined and loaded. It creates the .envrc file** by itself.
Manual Script-Based Creation
# Create .envrc file with default values
echo 'export SPRY_DB="sqlite://sqlpage.db?mode=rwc"' > .envrc
echo 'export PORT=9227' >> .envrcThen run:
direnv allowExecute via Spry Task
Spry also allows running the environment setup through a task defined in your Spry workflow.
If your Spry configuration includes a task named env, you can execute it as follows:
spry rb task envThis command internally runs the equivalent of:
```envrc env -C ./.envrc --gitignore --descr "Generate .envrc file and add it to local .gitignore if it's not already there"
export SPRY_DB="sqlite://scf-2025.3.sqlite.db?mode=rwc"
export PORT=9227
```After running the task, you should still execute:
direnv allowto authorize direnv to load the variables defined in .envrc.
Using OS Environment Variables with ${env.*} in Spry
Spry allows playbooks to access operating system environment variables using the ${env.*} interpolation namespace. This is useful for injecting configuration values such as emails, tokens, tenant IDs, or secrets without hardcoding them in playbooks.
Overview
- OS environment variables are exposed in Spry via
${env.VARIABLE_NAME} - Interpolation happens at runtime
- Interpolation must be explicitly enabled using
--interpolate - This works across Linux, macOS, Windows, and different shells (with shell-specific setup)
Basic Example
Environment variable:
EMAIL=tester@gmail.comSpry HTTP playbook:
```mirror-1 --interpolate --descr "verify env interpolation"
POST https://request-mirror.ohdear.app/post HTTP/1.1
Content-Type: application/json
{"username": "Admin", "email": "${env.EMAIL}"}
```At runtime, ${env.EMAIL} is replaced with the value from the OS environment.
Why --interpolate Is Required
Spry does not parse ${...} expressions by default.
The --interpolate flag tells Spry to:
- Scan the playbook for
${...}expressions - Resolve them from supported namespaces (such as
env) - Replace them before execution
Without --interpolate, ${env.EMAIL} will remain literal text.
Verifying Interpolation Using a Request Mirror
To verify that interpolation is working correctly, a request mirror service can be used:
https://request-mirror.ohdear.app/postThis endpoint:
- Accepts any HTTP request
- Returns exactly what it received in the response
This makes it ideal for validating:
- HTTP headers
- Request bodies
- Interpolated values
Expected Result
If interpolation succeeds, the response will contain:
{
"json": {
"username": "Admin",
"email": "tester@gmail.com"
}
}If interpolation fails, the response will show:
{
"json": {
"username": "Admin",
"email": "${env.EMAIL}"
}
}Shell-Specific Setup (Important)
Spry inherits environment variables from the shell it is executed in. The way variables are defined depends on the shell.
Bash / Zsh (Ubuntu default)
export EMAIL="tester@gmail.com"
spry run mirror-1Verify:
echo "$EMAIL"Fish Shell (Ubuntu)
Fish does not support export.
Correct Fish syntax:
set -x EMAIL tester@gmail.com
spry run mirror-1Using a .env file in Fish
Fish cannot source bash-style .env files.
✅ Correct .env for Fish:
set -x EMAIL tester@gmail.com
set -x ID 2256Load it:
source .envUsing direnv (Recommended)
For consistent behavior across shells and environments, direnv is recommended.
.envrc (Fish-compatible):
set -x EMAIL tester@gmail.com
set -x ID 2256Enable it once:
direnv allowSpry will automatically inherit these variables on every run.
Using ${env.*} in Headers and Bodies
Environment variables can be interpolated anywhere in an HTTP request.
Headers
Authorization: Bearer ${env.API_TOKEN}Body
{
"tenant": "${env.TENANT_ID}",
"email": "${env.EMAIL}"
}Common Errors and Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
${env.VAR} not replaced | --interpolate missing | Add --interpolate |
| Value is empty | Env var not set | Verify with echo $VAR |
| Works in bash, fails in Fish | Shell syntax mismatch | Use set -x |
.env fails in Fish | Bash syntax used | Use Fish-native syntax |
Best Practices
- Do not hardcode secrets in playbooks
- Use
${env.*}for environment-specific values - Prefer
direnvfor local development - Use request mirrors to validate interpolation behavior
- Fail early if required env vars are missing
Summary
| Method | Description | Usage |
|---|---|---|
| Manual | Create .envrc manually, then enable it with direnv allow. | cat > .envrc ... then direnv allow |
| Recommended: Task-based | Automates the recommended command using a Spry task. | spry rb task env then direnv allow |
| Runtime Interpolation | Use ${env.*} to inject OS environment variables into playbooks at runtime. | Add --interpolate flag to playbook execution |
By following these approaches, you can ensure consistent, isolated, and easily maintainable environment setups across all Spry-based projects.
The values shown above (SPRY_DB, PORT, EMAIL) are example values used for demonstration.
Replace them with your project's actual database URL, credentials, desired port number, and environment-specific values.
How is this guide?
Last updated on