# Environment variables

When you use `WithReference` to connect resources in the AppHost, Aspire automatically injects environment variables into the consuming resource. This process is called _configuration injection_.

These environment variable names follow specific conventions based on the referenced resource's name and type. Understanding the conventions is especially useful for applications that read environment variables directly instead of relying on typed Aspire client integrations.

## Naming conventions

Aspire generates environment variables in different formats depending on the type of resource being referenced. The following sections describe each category.

### Connection strings

When you reference a resource that exposes a connection string (such as a database, cache, or messaging resource), Aspire keeps the resource name, or an explicit `connectionName`, as the **logical connection name**. The original environment variable uses the `ConnectionStrings__` prefix:

```txt
ConnectionStrings__{resource-name}
```

Aspire also generates a **portable alias** for deployment targets with stricter environment-variable naming rules. The logical name doesn't change. For example:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("my-cache");
var db = builder.AddPostgres("postgres").AddDatabase("my-db");

var api = builder.AddProject<Projects.Api>("api")
    .WithReference(cache)
    .WithReference(db);

// After adding all resources, run the app...
builder.Build().Run();
```

On targets that support both spellings, the `api` resource receives both aliases for each connection:

| Original environment variable | Portable environment variable | Description |
| ----------------------------- | ----------------------------- | ----------- |
| `ConnectionStrings__my-cache` | `ConnectionStrings__my_cache` | Connection string for the Redis cache |
| `ConnectionStrings__my-db` | `ConnectionStrings__my_db` | Connection string for the PostgreSQL database |

Azure App Service, Kubernetes-based publishers (Kubernetes, AKS, and Radius), and Foundry Hosted Agents emit only the portable alias. Updated Aspire client integrations still accept the logical connection name, such as `my-cache`.

#### Portable connection-name encoding

Portable connection names preserve casing, replace characters other than ASCII letters, digits, or underscores with `_`, prefix a leading digit with `_`, and collapse consecutive underscores to a single `_`. Collapsing underscores prevents the suffix from introducing another .NET configuration path delimiter (`__`).

| Logical connection name | Portable environment variable |
| ----------------------- | ----------------------------- |
| `mydb` | `ConnectionStrings__mydb` |
| `my-db` | `ConnectionStrings__my_db` |
| `my--db` | `ConnectionStrings__my_db` |
| `my__db` | `ConnectionStrings__my_db` |
| `MyDb` | `ConnectionStrings__MyDb` |
| `1db` | `ConnectionStrings___1db` |

The `ConnectionStrings__` prefix is unchanged; encoding applies only to the suffix. When both spellings are identical, Aspire emits one variable.

Alias collision checks are case-insensitive. Different references that map to the same physical name, such as logical names `my-db` and `my_db`, fail during environment resolution instead of overwriting each other. Choose distinct explicit `connectionName` values on those references.

An integration can override the physical name through `IResourceWithConnectionString.ConnectionStringEnvironmentVariable`. Aspire emits that exact name only, without normalization; `connectionName` doesn't rename it. Publishers still validate explicit and unrelated environment variables, so portable aliases don't make an arbitrary invalid variable name valid.

#### Migrating connection-string consumers

Update your Aspire client integrations alongside the AppHost packages. Updated integrations resolve `ConnectionStrings:<logical-name>` through the composed .NET configuration first, then try the portable key only if the logical lookup returns `null`. This isn't a provider-by-provider search across both aliases.

Direct `IConfiguration.GetConnectionString` calls don't automatically translate hyphens or collapse underscores. For a known logical name such as `my-cache`, use the explicit fallback shown in [Accessing environment variables](#accessing-environment-variables). Applications that read environment variables directly can use the portable physical name.

If you must keep an older client integration, set the reference's `connectionName` to a portable name such as `my_db` and use **that same name** in the application's client registration or configuration lookup. Changing the reference without changing the consumer isn't sufficient.

#### Custom publishers and integrations

For generated injections, inspect `ConnectionStringReference` values in the environment-variable dictionary, not resource annotations. Their `EnvironmentVariableNames` property exposes a `ConnectionStringEnvironmentVariableNames` record with `LogicalName`, `OriginalName`, `PortableName`, and `IsExplicit`. Its `Create` factory derives names from a resource and logical name; `GetPhysicalNames` enumerates the distinct physical aliases.

:::caution[Experimental naming metadata]
`ConnectionStringEnvironmentVariableNames` and `ConnectionStringReference.EnvironmentVariableNames` are experimental under `ASPIRECONNECTIONSTRINGS001` and may change. This doesn't make ordinary `WithReference` usage experimental.
:::

A standalone reference created with the existing two-argument constructor has no naming metadata. Don't infer that it's a generated alias. Resolve the reference's `ConnectionStringExpression`, not `Resource.ConnectionStringExpression`, because a reference can select another value on the same resource, such as Qdrant's HTTP connection.

Publisher alias projection relies on at least one entry retaining its reference metadata. Replacing both entries with user-authored values removes that metadata, so normal environment-name validation applies instead.

### Endpoint URLs

When you reference a resource that exposes endpoints (such as a project or container service), Aspire generates an environment variable for each endpoint. The resource name and endpoint name are **encoded** (hyphens and other non-alphanumeric characters are replaced with underscores), then **uppercased**:

```txt
{RESOURCE_NAME}_{ENDPOINT_NAME}
```

For example:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var api = builder.AddProject<Projects.Api>("my-api");

var frontend = builder.AddJavaScriptApp("frontend", "./app")
    .WithReference(api);

// After adding all resources, run the app...
builder.Build().Run();
```

The `frontend` resource receives:

| Environment variable | Example value            |
| -------------------- | ------------------------ |
| `MY_API_HTTP`        | `http://localhost:5000`  |
| `MY_API_HTTPS`       | `https://localhost:5001` |

The suffix comes from the endpoint name, not necessarily its URI scheme. For example, a named endpoint called `admin` on `my-api` produces `MY_API_ADMIN`. For resource endpoints, Aspire retains the endpoint suffix even when the resource exposes only one endpoint.

### Service discovery variables

Aspire also generates service discovery variables for .NET service resolution. These use the format:

```txt
services__{resource-name}__{endpoint-key}__{index}
```

The `services` prefix and double-underscore (`__`) separators are fixed, but the resource name preserves the casing used in the AppHost. For example, `AddProject<Projects.Api>("MyApi")` with an HTTP endpoint produces `services__MyApi__http__0`. The endpoint key is the scheme for endpoints named `http` or `https`; otherwise, Aspire uses the endpoint name. Applications that don't use .NET service discovery can read the [endpoint URL variables](#endpoint-urls) instead.

### Resource properties

Some integrations expose individual resource properties as environment variables. The resource name is **encoded** (hyphens replaced with underscores) and **uppercased**, with the property name appended:

```txt
{RESOURCE_NAME}_{PROPERTY}
```

For example, a ClickHouse resource named `my-clickhouse` exposes:

| Environment variable         | Description       |
| ---------------------------- | ----------------- |
| `MY_CLICKHOUSE_HOST`         | The hostname      |
| `MY_CLICKHOUSE_PORT`         | The port number   |
| `MY_CLICKHOUSE_USERNAME`     | The username      |
| `MY_CLICKHOUSE_PASSWORD`     | The password      |
| `MY_CLICKHOUSE_DATABASENAME` | The database name |

Connection properties, such as a password passed separately, use different naming rules from a complete connection string. Their prefixes preserve repeated underscores; portable connection-string aliases collapse them. For example, a Radius reference with the logical connection name `db__primary` uses:

| Value | Environment variable |
| ----- | -------------------- |
| Password connection property | `DB__PRIMARY_PASSWORD` |
| Complete connection string | `ConnectionStrings__db_primary` |

Use the property name emitted by the integration rather than deriving it from the portable connection-string alias.

## Resource name encoding rules

When a resource name is used in an endpoint URL or property variable, Aspire applies the following transformations:

1. **Unsupported characters are replaced with underscores**: Hyphens (`-`), dots (`.`), and any other characters that aren't ASCII letters, digits, or underscores are replaced with `_`.
2. **Leading digits get a prefix**: If the name starts with a digit, an underscore (`_`) is prepended.
3. **The result is uppercased**: The encoded name is converted to uppercase for the final environment variable name.

For example, a resource named `foundry-demo-proj` becomes `FOUNDRY_DEMO_PROJ` in environment variable prefixes:

| Resource name       | Encoded prefix      | Example variable        |
| ------------------- | ------------------- | ----------------------- |
| `api`               | `API`               | `API_HTTP`              |
| `my-api`            | `MY_API`            | `MY_API_HTTPS`          |
| `foundry-demo-proj` | `FOUNDRY_DEMO_PROJ` | `FOUNDRY_DEMO_PROJ_URI` |

**Note:** Connection-string aliases use [portable connection-name encoding](#portable-connection-name-encoding) instead: they preserve casing and collapse underscore runs. The original spelling is also emitted when the target supports it.

## Accessing environment variables

The following examples try the original connection name first and fall back to the portable alias only when the original is absent. This works both when a target supplies both aliases and when it supplies only the portable alias.

### C\#

In .NET applications, updated Aspire client integrations handle connection-string alias lookup automatically. For manual access, try the logical key first and then its portable form:

```csharp title="C# — Program.cs"
// Connection strings
string? cache = builder.Configuration.GetConnectionString("my-cache")
    ?? builder.Configuration.GetConnectionString("my_cache");

// Endpoint URLs
string apiUrl = builder.Configuration.GetValue<string>("MY_API_HTTP");

// Resource properties
string host = builder.Configuration.GetValue<string>("MY_CLICKHOUSE_HOST");
```

### Python

```python title="Python — main.py"
import os

# Connection strings
cache_conn = os.getenv("ConnectionStrings__my-cache")
if cache_conn is None:
    cache_conn = os.getenv("ConnectionStrings__my_cache")

# Endpoint URLs
api_url = os.getenv("MY_API_HTTP")

# Resource properties
db_host = os.getenv("MY_CLICKHOUSE_HOST")
```

### JavaScript / TypeScript

```javascript title="JavaScript — app.js"
// Connection strings
const cacheConn = process.env["ConnectionStrings__my-cache"]
    ?? process.env.ConnectionStrings__my_cache;

// Endpoint URLs
const apiUrl = process.env.MY_API_HTTP;

// Resource properties
const dbHost = process.env.MY_CLICKHOUSE_HOST;
```

**Caution:** In JavaScript, `process.env` is an object. If you read an original alias containing hyphens, use bracket notation: `process.env["ConnectionStrings__my-cache"]`. That alias isn't available on targets that emit only portable names.

## Custom environment variables

If you need different variable names, use `WithEnvironment` to set custom environment variables:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var db = builder.AddPostgres("postgres").AddDatabase("my-db");

var api = builder.AddPythonApp("api", "../api", "main.py")
    .WithReference(db)
    .WithEnvironment("DB_HOST", db.Resource.Parent.PrimaryEndpoint.Property(EndpointProperty.Host))
    .WithEnvironment("DB_PORT", db.Resource.Parent.PrimaryEndpoint.Property(EndpointProperty.Port));

// After adding all resources, run the app...
builder.Build().Run();
```

This approach lets you define explicit, predictable variable names without relying on the automatic naming conventions.

**Tip:** Use the [Aspire dashboard](/dashboard/explore/#resource-details) to inspect the actual environment variables that Aspire injects into each resource. This is the fastest way to discover the exact variable names available to your application.

## See also

- [Service discovery](/fundamentals/service-discovery/)
- [Inner-loop networking overview](/fundamentals/networking-overview/)
- [Python integration](/integrations/frameworks/python/)
- [JavaScript integration](/integrations/frameworks/javascript/)
- [Executable resources](/app-host/executable-resources/)
- [Legacy deployment manifest format](/deployment/azure/manifest-format/)