# Expose services with Ingress and Gateway API

When you deploy an Aspire application to Kubernetes, your services are accessible only within the cluster by default. To expose services to external traffic — such as a web frontend that users access from the internet — you need to configure an **Ingress** or **Gateway** resource that routes traffic from outside the cluster to your services.

Aspire provides first-class APIs for defining Ingress and Gateway API resources directly in your AppHost, generating the appropriate Kubernetes YAML as part of the Helm chart output.
**Note:** This guide covers exposing services to external traffic. For the basics of
  deploying to Kubernetes with Aspire, see [Deploy to
  Kubernetes](/deployment/kubernetes/). If you're targeting AKS via
  [`AddAzureKubernetesEnvironment`](/deployment/kubernetes/aks/), the
  integrated AGC + cert-manager walkthrough lives in
  [Deploy to AKS](/deployment/kubernetes/aks/#expose-your-app-to-the-internet)
  and is the recommended path for that scenario.
**Prefer Gateway API for new deployments:** The Kubernetes project recommends [Gateway
  API](https://kubernetes.io/docs/concepts/services-networking/gateway/) as the
  long-term successor to Ingress — the Ingress API is feature-frozen and all
  new traffic-routing capabilities are landing in Gateway API instead. For new
  Aspire deployments, prefer
  [`AddGateway(...)`](/deployment/kubernetes-gateway-aks/) over
  `AddIngress(...)` unless your cluster or controller doesn't yet support
  Gateway API. See [Gateway API
  concepts](https://gateway-api.sigs.k8s.io/concepts/api-overview/) for the
  upstream design rationale.

## Ingress vs. Gateway API

Kubernetes offers two approaches for routing external traffic to your services:

| | **Ingress** | **Gateway API** |
|---|---|---|
| **Maturity** | Legacy (stable since K8S 1.19) | Modern (GA since K8S 1.27) |
| **Resource model** | Single `Ingress` resource | Three-tier: `GatewayClass` → `Gateway` → `HTTPRoute` |
| **Protocol support** | HTTP/HTTPS only | HTTP, HTTPS, TCP, UDP, gRPC |
| **Routing features** | Host and path matching | Host, path, header, query, method matching |
| **Extensibility** | Controller-specific annotations | Standardized filters and policies |
| **Traffic splitting** | Not standardized | First-class weighted backends |
| **Aspire API** | `AddIngress()` | `AddGateway()` |

**Recommendation:** Use **Gateway API** for new deployments. It's the future direction of Kubernetes traffic management and offers richer routing capabilities with better portability across providers. Use **Ingress** when your cluster or ingress controller doesn't support the Gateway API yet.

## What Aspire generates

When you use `AddIngress()` or `AddGateway()`, Aspire generates additional Kubernetes resources in the Helm chart output:

**For Ingress (`AddIngress`):**
- A `networking.k8s.io/v1 Ingress` resource with rules, TLS configuration, and controller annotations

**For Gateway API (`AddGateway`):**
- A `gateway.networking.k8s.io/v1 Gateway` resource with listeners (HTTP on port 80, HTTPS on port 443 if TLS is configured)
- One or more `HTTPRoute` resources attached to the Gateway, grouped by hostname

## Routing paths to services

Use `WithPath` on an Ingress (or `WithRoute` on a Gateway) to forward traffic for a URL path to one of your Aspire services. Each call adds a rule to the generated `Ingress` (or `HTTPRoute`) resource pointing at the named endpoint's backing Kubernetes service.

```csharp title="AppHost.cs"
var k8s = builder.AddKubernetesEnvironment("k8s");

var api = builder.AddProject<Projects.Api>("api");
var web = builder.AddProject<Projects.Web>("web");

var ingress = k8s.AddIngress("public")
    .WithIngressClass("nginx")
    .WithHostname("app.example.com")
    .WithTls();

// Route /api/* to the API service and everything else to the web frontend.
ingress.WithPath("/api", api.GetEndpoint("http"));
ingress.WithPath("/", web.GetEndpoint("http"));
```
```typescript title="apphost.mts"
const k8s = await builder.addKubernetesEnvironment('k8s');

const api = await builder.addProject('api');
const web = await builder.addProject('web');

const ingress = await k8s.addIngress('public');
await ingress.withIngressClass('nginx');
await ingress.withHostname('app.example.com');
await ingress.withTls();

// Route /api/* to the API service and everything else to the web frontend.
await ingress.withPath('/api', api.getEndpoint('http'));
await ingress.withPath('/', web.getEndpoint('http'));
```
`WithPath` defaults to a `Prefix` match — pass an `IngressPathType` argument to request `Exact` or `ImplementationSpecific` matching when your controller supports it. There's also a host-scoped overload (`WithPath("api.example.com", "/", endpoint)`) for routing different hostnames to different backends from the same Ingress. If you'd rather forward all unmatched traffic to a single service without enumerating paths, call `WithDefaultBackend(endpoint)` instead — the two APIs can be combined.
**Note:** Prior to Aspire 13.4, the Ingress path API was named `WithRoute`. The Gateway
  API equivalent (`gateway.WithRoute(...)`) keeps the original name to match
  the `HTTPRoute` resource it generates.

## TLS and certificates

When you configure TLS with `WithTls()`, Aspire handles the initial bootstrapping:

1. **First deploy**: Aspire generates a self-signed placeholder TLS certificate and creates the Kubernetes Secret. This makes the Ingress/Gateway valid immediately so the load balancer can start routing traffic.

2. **Certificate replacement**: You then configure a certificate management solution (such as [cert-manager](https://cert-manager.io/)) to replace the self-signed certificate with a real one from a trusted CA like Let's Encrypt or ZeroSSL.

3. **Subsequent deploys**: The existing Secret (with the real certificate) is preserved — the bootstrap step only creates the Secret if it doesn't already exist.
**Caution:** The self-signed bootstrap certificate is a placeholder only. Browsers will
  show security warnings when accessing your site with this certificate.
  Configure cert-manager or provide your own certificate for production use.

## Using parameters for deployment configuration

Aspire's Ingress and Gateway APIs accept parameters, allowing you to reuse the same AppHost across multiple deployment environments (development, staging, production) with different hostnames, TLS secrets, and controller configurations:

```csharp title="AppHost.cs"
var hostname = builder.AddParameter("hostname");
var ingressClass = builder.AddParameter("ingressclass");

var ingress = k8s.AddIngress("ingress")
    .WithIngressClass(ingressClass)
    .WithHostname(hostname)
    .WithTls();
```
```typescript title="apphost.mts"
const hostname = builder.addParameter('hostname');
const ingressClass = builder.addParameter('ingressclass');

const ingress = await k8s.addIngress('ingress');
ingress.withIngressClass(ingressClass);
ingress.withHostname(hostname);
ingress.withTls();
```
Parameter values are provided at deploy time via the Aspire CLI or configuration files, keeping environment-specific details out of your source code.

## Deploying without TLS
**Danger:** Deploying without TLS exposes all traffic in plaintext, including
  authentication tokens, session cookies, and user data. Only use this for
  development clusters or internal services that are not accessible from the
  internet.

You can omit `WithTls()` to deploy without TLS. The Ingress/Gateway will serve traffic over HTTP only. This is useful for quick testing but should never be used in production.

## Next steps

Choose the walkthrough that matches your deployment approach:

- **[Configure Gateway API on AKS](/deployment/kubernetes-gateway-aks/) (Recommended)** — Use the Kubernetes Gateway API with Application Gateway for Containers on AKS. Supports cert-manager HTTP-01 for automatic TLS with **no DNS API or DNS zone required**.

- **[Configure Ingress on AKS](/deployment/kubernetes-ingress-aks/)** — Use the Kubernetes Ingress API with Application Gateway for Containers on AKS. Requires cert-manager with DNS-01 for automatic TLS.
**Note:** These walkthroughs use AKS with Application Gateway for Containers as the
  reference implementation. The Aspire APIs (`AddIngress`, `AddGateway`) work
  with any Kubernetes cluster that has an ingress controller or Gateway API
  implementation. Controller-specific annotations (such as
  `alb.networking.azure.io/alb-name`) will differ for other providers (NGINX,
  Traefik, Istio, etc.).