Skip to content

Ingress & Routing

helm-me generates a Kubernetes Ingress resource when expose.ingress is set. You do not need to add public_paths on components for the basic case — a default / catch-all route is created automatically when there is exactly one routable web component.

Basic Ingress

The simplest setup — expose the whole app through a single hostname:

apiVersion: helm-me/v1alpha1
kind: Application
metadata:
  name: my-app

expose:
  ingress:
    host: app.example.com
    className: nginx

components:
  backend:
    type: web
    image: myapp/backend:latest
    port: 8000
from helm_me import App, ingress, web, image

class Deploy(App):
    name = "my-app"
    publish = ingress(
        host="app.example.com",
        class_name="nginx",
    )
    backend = web(
        image=image("myapp/backend:latest"),
        port=8000,
    )

This generates an Ingress with a single rule: app.example.com → /my-app-backend:8000.

Path-Based Routing

Route different URL paths to different components:

components:
  backend:
    type: web
    image: backend:latest
    port: 8000
    public_paths: ["/api", "/graphql"]

  frontend:
    type: web
    image: frontend:latest
    port: 80
    public_paths: ["/", "/docs"]
backend = web(
    image=image("backend:latest"),
    port=8000,
    public_paths=["/api", "/graphql"],
)
frontend = web(
    image=image("frontend:latest"),
    port=80,
    public_paths=["/", "/docs"],
)

This creates a single Ingress with four path rules routing to the appropriate services.

Note

When public_paths is defined on any component, the auto-fallback / route is disabled. If more than one component should be reachable through the same ingress, define paths explicitly on each component.

Internal Paths

Paths visible only within the cluster (not exposed through the Ingress):

components:
  backend:
    type: web
    image: backend:latest
    port: 8000
    public_paths: ["/api"]
    internal_paths: ["/api", "/internal", "/metrics"]
backend = web(
    image=image("backend:latest"),
    port=8000,
    public_paths=["/api"],
    internal_paths=["/api", "/internal", "/metrics"],
)

Base Path

Inject APP_BASE_PATH into the container so sub-path apps render links correctly:

components:
  chat:
    type: web
    image: chat:latest
    port: 8501
    base_path: "/chat"
    public_paths: ["/chat"]
chat = web(
    image=image("chat:latest"),
    port=8501,
    base_path="/chat",
    public_paths=["/chat"],
)

TLS

expose:
  ingress:
    host: app.example.com
    className: nginx
    tls:
      secretName: app-tls-cert
expose:
  ingress:
    host: app.example.com
    tls:
      name: app-tls
      cert_file: certs/tls.crt
      key_file: certs/tls.key
from helm_me import ingress, tls_secret, tls_secret_ref

publish = ingress(
    host="app.example.com",
    class_name="nginx",
    tls=tls_secret_ref("app-tls-cert"),
)

publish_inline = ingress(
    host="app.example.com",
    tls=tls_secret(
        name="app-tls",
        cert_file="certs/tls.crt",
        key_file="certs/tls.key",
    ),
)

When you use inline TLS (tls_secret(...) or YAML with name + cert_file / key_file), helm-me creates a Kubernetes TLS Secret and includes it in the rendered manifests automatically. Relative file paths are resolved from the directory containing the spec file.

Verify the Rendered Ingress

Before deploying, you can inspect the generated Ingress resource:

helm-me render deploy.yaml | grep -A 30 "kind: Ingress"

Or preview changes against a live cluster:

helm-me diff deploy.yaml