Spry LogoOpsfolio
Core Concepts

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: direnv only loads .envrc after explicit approval using direnv allow.
  • Automation-ready: Integrates seamlessly with Spry's dev and deployment workflows.

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
# .envrc (bash/zsh)
export SPRY_DB="sqlite://sqlpage.db?mode=rwc"
export PORT=9227

Your project structure should look like this:

Spryfile.md
.envrc

Once the file is created, run the following command in your terminal:

direnv allow

This 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' >> .envrc

Then run:

direnv allow

Execute 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 env

This 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 allow

to 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.com

Spry 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:

  1. Scan the playbook for ${...} expressions
  2. Resolve them from supported namespaces (such as env)
  3. 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/post

This 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-1

Verify:

echo "$EMAIL"

Fish Shell (Ubuntu)

Fish does not support export.

Correct Fish syntax:

set -x EMAIL tester@gmail.com
spry run mirror-1
Using 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 2256

Load it:

source .env

For consistent behavior across shells and environments, direnv is recommended.

.envrc (Fish-compatible):

set -x EMAIL tester@gmail.com
set -x ID 2256

Enable it once:

direnv allow

Spry 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

IssueCauseFix
${env.VAR} not replaced--interpolate missingAdd --interpolate
Value is emptyEnv var not setVerify with echo $VAR
Works in bash, fails in FishShell syntax mismatchUse set -x
.env fails in FishBash syntax usedUse Fish-native syntax

Best Practices

  • Do not hardcode secrets in playbooks
  • Use ${env.*} for environment-specific values
  • Prefer direnv for local development
  • Use request mirrors to validate interpolation behavior
  • Fail early if required env vars are missing

Summary

MethodDescriptionUsage
ManualCreate .envrc manually, then enable it with direnv allow.cat > .envrc ... then direnv allow
Recommended: Task-basedAutomates the recommended command using a Spry task.spry rb task env then direnv allow
Runtime InterpolationUse ${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

On this page