# How to Access Environment Variables in Macro: The Complete Guide to macro_env_var

> Discover how to access environment variables in Macro using the macro_env_var crate. Learn about compile-time validation, optional handling, and secure secret management for your Macro project.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: how-to-guide
- Published: 2026-08-21

---

**The Macro codebase requires all environment variables be accessed in Macro through the dedicated `macro_env_var` crate, which provides compile-time macros that enforce validation, optional handling, and centralized secret management.**

When you access environment variables in Macro, you must avoid direct calls to `std::env::var` or `std::env::var_os`. According to the macro-inc/macro repository standards, the `macro_env_var` crate serves as the **single, sanctioned wrapper** for all configuration and secret access. This architectural policy ensures that missing variables surface immediately at startup and that sensitive values can be fetched from remote secret managers like Doppler or AWS Secrets Manager in one centralized location.

## Why Macro Uses a Dedicated Environment Variable Crate

The enforcement of `macro_env_var` as the sole access point appears explicitly in the source at [`/crates/macro_env_var/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_env_var/src/lib.rs) at line 11, which states this crate is the sanctioned wrapper around standard environment access. This design delivers three critical benefits:

- **Security:** Centralizing access allows the team to implement secret-fetching logic, encryption, and auditing in a single codebase location rather than scattering it across dozens of services.
- **Consistency:** Every service—from the Authentication Service to the Native App Server—follows identical patterns for loading configuration, making audits straightforward.
- **Testability:** The macros can be stubbed or overridden in unit tests, allowing deterministic behavior without modifying the actual process environment.

## The Core Macros for Environment Access

The `macro_env_var` crate exposes declarative macros that handle distinct access patterns. These compile-time utilities generate code that validates state at startup.

### env_var! for Mandatory Variables

Use `env_var!` when a variable must exist for the service to function. It returns a `String` (or a parsed type) and **panics immediately if the variable is unset or empty**, ensuring services fail fast during initialization rather than at runtime.

```rust
use macro_env_var::env_var;

let db_url = env_var!("POSTGRES_URL");
// Panics if POSTGRES_URL is not set

```

### maybe_env_var! for Optional Configuration

Use `maybe_env_var!` when a variable is truly optional. It returns `Option<String>`, yielding `None` when the variable is unset or empty. This pattern appears in [`/crates/macro_aws_config/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_aws_config/src/lib.rs) at line 6 for optional AWS credential loading.

```rust
use macro_env_var::maybe_env_var;

let redis_url = maybe_env_var!("REDIS_URL");
// Returns None if REDIS_URL is not set

```

### env_vars! for Bulk Mandatory Loading

Use `env_vars!` to read multiple required variables at once, returning a generated struct with fields matching the variable names. This eliminates repetitive boilerplate when initializing configuration structs. The **Authentication Service** utilizes this pattern in [`/services/authentication_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main//services/authentication_service/src/config.rs) at line 7.

```rust
use macro_env_var::env_vars;

let Config {
    api_key,
    bucket_name,
    region,
} = env_vars! {
    API_KEY,
    BUCKET_NAME,
    REGION,
};

```

### maybe_env_vars! for Bulk Optional Loading

Use `maybe_env_vars!` to load a set of optional variables into a struct where each field is `Option<T>`. The **Native App Server** employs this approach in [`/tooling/native_app_server/src/main.rs`](https://github.com/macro-inc/macro/blob/main//tooling/native_app_server/src/main.rs) at line 4 to allow optional development defaults without failing when they are absent.

```rust
use macro_env_var::maybe_env_vars;

let Config {
    log_level,
    sentry_dsn,
} = maybe_env_vars! {
    LOG_LEVEL,
    SENTRY_DSN,
};

```

### Legacy Function-Style Helpers

For older code paths, the crate also provides `optional_read_env_var` and `maybe_read_env_var` as function-style alternatives for single optional reads. These return `Option<String>` but lack the compile-time struct generation and validation guarantees of the newer macros.

## Real-World Implementation Examples

The `macro_env_var` macros appear consistently across the repository, confirming the architectural standard is enforced in practice.

The **Authentication Service** initializes its configuration in [`/services/authentication_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main//services/authentication_service/src/config.rs) by importing both `env_vars` and `maybe_env_vars` macros to handle mixed required and optional settings in a single struct.

The **Worker Trigger** service follows the same idiom in [`/services/worker_trigger/src/config.rs`](https://github.com/macro-inc/macro/blob/main//services/worker_trigger/src/config.rs) at line 4, importing the macro-based loader to maintain consistency with the authentication service pattern.

For command-line utilities requiring strict guarantees, the **Task Dedup** crate uses `env_var!` directly in its binary entry point at [`/crates/task_dedup/src/bin/pull_task_corpus.rs`](https://github.com/macro-inc/macro/blob/main//crates/task_dedup/src/bin/pull_task_corpus.rs) line 48. This ensures required variables are present before the tool continues execution, preventing partial processing runs.

Even infrastructure helpers like the **AWS Config** utility at [`/crates/macro_aws_config/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_aws_config/src/lib.rs) line 6 employ `maybe_env_var!` to optionally read AWS credentials without failing when local environment variables are absent.

## Summary

- **All environment variables be accessed in Macro** through the `macro_env_var` crate located at `/crates/macro_env_var`; direct usage of `std::env::var` is prohibited.
- **Four primary macros** cover all use cases: `env_var!` (required single), `maybe_env_var!` (optional single), `env_vars!` (required bulk), and `maybe_env_vars!` (optional bulk).
- **Source enforcement** appears at line 11 of [`/crates/macro_env_var/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_env_var/src/lib.rs), which documents the crate as the sanctioned wrapper.
- **Service initialization** occurs in dedicated [`config.rs`](https://github.com/macro-inc/macro/blob/main/config.rs) modules, as seen in [`/services/authentication_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main//services/authentication_service/src/config.rs) and [`/services/worker_trigger/src/config.rs`](https://github.com/macro-inc/macro/blob/main//services/worker_trigger/src/config.rs).
- **Legacy functions** `optional_read_env_var` and `maybe_read_env_var` exist for older code but are superseded by the macro-based approach.

## Frequently Asked Questions

### Can I use std::env::var directly in the Macro codebase?

No. The source code at [`/crates/macro_env_var/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_env_var/src/lib.rs) at line 11 explicitly prohibits direct usage of `std::env::var` or `std::env::var_os`. All environment variables be accessed in Macro through the sanctioned wrapper to ensure consistent security policies, centralized secret fetching, and uniform error handling.

### How do I handle environment variables that might not be set?

Use the `maybe_env_var!` macro for individual optional variables, or `maybe_env_vars!` for bulk optional loading. Both return `Option<String>` (or `Option<T>` for parsed values), yielding `None` when variables are unset or empty. This pattern is implemented in [`/crates/macro_aws_config/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_aws_config/src/lib.rs) for optional AWS credential loading.

### Where is the macro_env_var crate located in the repository?

The crate lives at `/crates/macro_env_var` in the repository root. The core macros are defined in [`/crates/macro_env_var/src/lib.rs`](https://github.com/macro-inc/macro/blob/main//crates/macro_env_var/src/lib.rs), which includes the enforcement comment at line 11 stating this is the sanctioned wrapper around standard environment access.

### Why does Macro enforce macros instead of functions for environment access?

The compile-time macros ensure variables are evaluated at service startup, enabling immediate failure for missing required configuration. This approach supports generating typed structs for bulk loading via `env_vars!` and `maybe_env_vars!`, and allows the test suite to override values without modifying the actual process environment, as demonstrated in the authentication and worker trigger services.