---
title: Environments
icon: Layers
description: Switch between production, staging, and other environments in Earl templates.
---
Environments let a single template target different backends — typically a production API and a staging one — without duplicating the command logic. You define named environments at the provider level, then optionally override the full operation for specific environments in each command.
## How it works
There are two pieces:
1. **The `environments` block** at the top of the template defines the available environments and their values (usually base URLs). This is where you set the default.
2. **`environment_overrides` blocks** inside individual commands replace the entire operation when a specific environment is active.
The two pieces are independent. You can use `environments` without any `environment_overrides`, and just interpolate `{{ vars.base_url }}` into your operation URL. Or you can skip the top-level block and use `environment_overrides` to swap operations entirely.
## The `environments` block
This goes at the top of the template file, before any `command` blocks:
```hcl
environments {
default = "production"
secrets = ["github.token"]
production {
base_url = "https://api.github.com"
}
staging {
base_url = "https://staging.github.internal"
}
}
```
When a command runs, the active environment's fields are available as `{{ vars.<key> }}`. In the example above, `{{ vars.base_url }}` resolves to `https://api.github.com` in production and `https://staging.github.internal` in staging.
`secrets` lists secrets that apply to all environments. Individual environments can add their own fields — if staging uses a different token, you can put that key name in the staging entry:
```hcl
environments {
default = "production"
secrets = ["github.token"]
production {
base_url = "https://api.github.com"
}
staging {
base_url = "https://staging.github.internal"
token_key = "github.staging_token"
}
}
```
The top-level `secrets` list is for secrets shared across all environments. Per-environment values in the `environments { }` inner block are data fields that end up in `vars.*`, not automatic secret references.
## Using `{{ vars.* }}` in operations
Once you have an `environments` block, use `vars.<key>` anywhere in the operation:
```hcl
operation {
protocol = "http"
method = "GET"
url = "{{ vars.base_url }}/search/repositories"
auth {
kind = "bearer"
secret = "github.token"
}
}
```
This is the simplest form of environment switching: one operation, parameterized by environment values.
## `environment_overrides`
When the difference between environments is more than a URL — different auth, different protocol, different headers — you can replace the entire operation for a specific environment:
```hcl
command "search_repos" {
title = "Search repositories"
summary = "Search GitHub repos by query"
description = "Search GitHub repositories."
annotations {
mode = "read"
secrets = ["github.token", "github.staging_token"]
}
param "query" {
type = "string"
required = true
description = "Search query"
}
operation {
protocol = "http"
method = "GET"
url = "{{ vars.base_url }}/search/repositories"
auth {
kind = "bearer"
secret = "github.token"
}
query = { q = "{{ args.query }}" }
}
environment_overrides {
staging {
operation {
protocol = "http"
method = "GET"
url = "https://staging.github.internal/search/repositories"
auth {
kind = "bearer"
secret = "github.staging_token"
}
query = { q = "{{ args.query }}" }
}
}
}
}
```
When the active environment is `staging`, Earl uses the override operation and ignores the default one entirely. For all other environments, the default operation runs.
The override is a complete replacement, not a merge. If the default operation has headers or query params you want to keep in staging, you need to repeat them.
## Activating an environment
Pass `--env` to `earl call`:
```bash
earl call --env staging github.search_repos --query "rust"
```
Or set a default in `~/.config/earl/config.toml`:
```toml
[environments]
default = "staging"
```
The `--env` flag overrides the config file default. The config file default overrides the template's `default` value.
## Protocol switching
By default, Earl rejects environment overrides that switch the protocol (e.g., from `http` to `grpc`). This prevents silent behavior changes when swapping environments.
If you need to switch protocols between environments, set the annotation:
```hcl
annotations {
allow_environment_protocol_switching = true
}
```
Without this, an override that changes `protocol` will cause Earl to error when the template loads.
## Complete example
```hcl
version = 1
provider = "github"
categories = ["scm"]
environments {
default = "production"
secrets = ["github.token"]
production {
base_url = "https://api.github.com"
}
staging {
base_url = "https://staging.github.internal"
}
}
command "search_repos" {
title = "Search repositories"
summary = "Search GitHub repos by query"
description = "Search GitHub repositories using GitHub's search API."
annotations {
mode = "read"
secrets = ["github.token"]
}
param "query" {
type = "string"
required = true
description = "Search query (e.g. 'language:rust stars:>100')"
}
param "per_page" {
type = "integer"
required = false
default = 20
description = "Results per page (max 100)"
}
operation {
protocol = "http"
method = "GET"
url = "{{ vars.base_url }}/search/repositories"
auth {
kind = "bearer"
secret = "github.token"
}
query = {
q = "{{ args.query }}"
per_page = "{{ args.per_page }}"
}
}
result {
decode = "json"
output = "Found {{ result.total_count }} repos:\n{% for r in result.items[:5] %} - {{ r.full_name }}\n{% endfor %}"
}
}
```
Run against staging:
```bash
earl call --env staging github.search_repos --query "language:rust"
```
For a field-by-field reference, see [Template Schema](/docs/template-schema). For using external secret URIs in environment-specific auth, see [External Secrets](/docs/external-secrets).