# How TOML Configuration Is Loaded at Server Startup Using Environment Variables in Switchyard

> Learn how Switchyard loads TOML configuration at server startup. Discover how it reads paths, deserializes into ServerConfig, and injects environment variable values for runtime server state.

- Repository: [NVIDIA-NeMo/Switchyard](https://github.com/NVIDIA-NeMo/Switchyard)
- Tags: how-to-guide
- Published: 2026-08-14

---

**Switchyard loads TOML configuration by reading a filesystem path, deserializing it into `ServerConfig`, then resolving `*_env` fields by looking up the named environment variables and injecting their values into the runtime server state.**

This pattern lets operators store sensitive credentials like API keys outside of version-controlled configuration files. The implementation lives primarily in the Rust server crate, with Python CLI helpers passing the configuration path through environment variables.

## Overview of the Configuration Loading Flow

The Switchyard server follows a four-stage initialization process when it boots:

1. **Path resolution** – The server obtains a TOML file path, typically from the `SWITCHYARD_CONFIG` environment variable
2. **File reading** – Raw bytes are read from disk using standard Rust filesystem APIs
3. **TOML deserialization** – `toml::from_str` parses the file into the `ServerConfig` struct
4. **Environment variable injection** – Fields ending in `_env` trigger lookups via `std::env::var`, with results merged into the final configuration

This architecture separates structural configuration (routes, limits, timeouts) from secret material (API keys, authentication tokens), addressing a common operational security requirement for LLM gateway deployments.

## Key Source Files and Their Roles

| File | Purpose | Direct Source Link |
|------|---------|------------------|
| [`crates/switchyard-server/src/config.rs`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/config.rs) | Core configuration types, TOML parsing, and `*_env` resolution logic | [config.rs on GitHub](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/config.rs) |
| [`crates/switchyard-server/src/lib.rs`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/lib.rs) | Server initialization and `load_server_state` export | [lib.rs on GitHub](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/lib.rs) |
| [`switchyard/cli/launch_command.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launch_command.py) | Python CLI that sets `SWITCHYARD_CONFIG` and invokes the Rust binary | [launch_command.py on GitHub](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launch_command.py) |
| [`dev-server/config.toml`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/dev-server/config.toml) | Reference configuration demonstrating environment variable patterns | [config.toml on GitHub](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/dev-server/config.toml) |

## Implementation Details in config.rs

The `ServerConfig` struct in **[`crates/switchyard-server/src/config.rs`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/config.rs)** defines the schema for TOML configuration. Critical to environment variable support are fields like `api_key_env` and `base_url_env`, which store the *name* of an environment variable rather than its value.

After deserialization completes, the configuration loader iterates over these `*_env` fields:

- Calls `std::env::var(env_var_name)` for each configured variable name
- Returns a descriptive error if the variable is unset or contains only whitespace
- Substitutes the retrieved value into the corresponding operational field (e.g., `api_key` for LLM client authentication)

This two-step process ensures that TOML files can be committed to repositories safely, while still permitting per-deployment customization of secrets and endpoint URLs.

## Running the Server with Environment-Driven Configuration

### Shell Example

```bash

# Point Switchyard to your TOML configuration file

export SWITCHYARD_CONFIG=/etc/switchyard/production.toml

# Provide secrets that the TOML file references by variable name

export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...

# Launch the server (CLI passes SWITCHYARD_CONFIG to the Rust binary)

switchyard launch

```

### Minimal TOML Snippet with Environment References

```toml
[llm_clients.openai_gpt4]
base_url = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"  # Value pulled from environment at startup

[llm_clients.anthropic_claude]
base_url = "https://api.anthropic.com/v1/complete"
api_key_env = "ANTHROPIC_API_KEY"

```

## Verifying Configuration Resolution Programmatically

For debugging or integration testing, you can inspect the resolved configuration from Python:

```python
from switchyard_rust.server import load_server_state
import os

# Mimic the environment setup that the CLI performs

os.environ["SWITCHYARD_CONFIG"] = "dev-server/config.toml"
os.environ["OPENAI_API_KEY"] = "sk-test-key-12345"

# Load and validate the full server state

state = load_server_state(os.getenv("SWITCHYARD_CONFIG"))

# The API key has been resolved from the environment and is now populated

print(state.llm_clients["openai_gpt4"].api_key)  # outputs: sk-test-key-12345

print(state.routes)                               # shows fully configured routing table

```

This pattern is particularly useful in test suites that need to verify configuration parsing without spawning a full HTTP server.

## Error Handling for Missing Variables

The Switchyard implementation is strict about environment variable presence. If a TOML file declares `api_key_env = "REQUIRED_SECRET"` but `REQUIRED_SECRET` is unset at runtime, `load_server_state` fails fast with an error message identifying:

- The configuration file path
- The field requiring the variable
- The exact environment variable name that was not found

This fail-fast behavior prevents silent misconfigurations that would otherwise manifest as authentication failures downstream.

## Summary

- **TOML configuration in Switchyard** is loaded via `load_server_state` in [`crates/switchyard-server/src/config.rs`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/crates/switchyard-server/src/config.rs)
- **Environment variables** are referenced by name in `*_env` fields and resolved after TOML parsing completes
- **Secrets stay out of files** – only variable names are stored in TOML, with values injected at runtime from `std::env::var`
- **CLI integration** – The `switchyard launch` command sets `SWITCHYARD_CONFIG` before invoking the Rust server binary
- **Strict validation** – Missing environment variables produce immediate, descriptive errors rather than silent defaults

## Frequently Asked Questions

### What environment variable specifies the TOML configuration file path?

Switchyard uses `SWITCHYARD_CONFIG` as the conventional environment variable for the configuration file path. The Python launch command sets this automatically when you invoke `switchyard launch`, or you can set it manually to override the default location.

### Can I use environment variables for values other than API keys?

Yes. The `*_env` pattern applies to multiple configuration fields. In addition to `api_key_env`, the Switchyard schema supports `base_url_env` and potentially other environment-referenced fields defined in `ServerConfig`. Any value that varies across deployments—endpoints, timeout values, feature flags—can be externalized using this mechanism.

### What happens if an environment variable referenced in my TOML file is missing?

The server fails during startup with a clear error message. The `load_server_state` implementation validates that every `*_env` field resolves to a non-empty string, returning an error that identifies the missing variable by name. This prevents the server from starting in a partially configured state.

### Is there a way to inspect the resolved configuration without starting the HTTP server?

Yes. Import `load_server_state` from `switchyard_rust.server` in Python, set your environment variables and `SWITCHYARD_CONFIG` path, then call the function directly. This returns a populated `ServerState` object containing all resolved values without binding to any network ports.