Skip to content

Networking

Other services reach yours by name. The internet reaches it through a route you declare.

Traffic from the internet enters through the gateway, another service reaches web by name, and another project is blocked

compose.production.yaml
services:
  web:
    image: my-app:local
    expose:
      - "9090" # (1)!
    ports:
      - "8080:8000" # (2)!
    x-ingress:
      routes:
        - hostname: app.example.com # (3)!
          port: 8000 # (4)!
  1. Open to the other services only.
  2. On your machine, localhost:8080 reaches port 8000 of the container.
  3. The public address of the service.
  4. The port of the container, not the one of your machine.

Generated on every release. You never write these files or see them.

apiVersion: v1
kind: Service
metadata:
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
  name: web
  namespace: my-app
spec:
  ports:
  - name: p-8000-tcp # (1)!
    port: 8000
    protocol: TCP
    targetPort: 8000
  - name: p-9090-tcp # (2)!
    port: 9090
    protocol: TCP
    targetPort: 9090
  selector:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
  type: ClusterIP # (3)!
  1. From ports: the port of the container. The 8080 of your machine is not here.
  2. From expose.
  3. An address inside the cluster only.

One route for each hostname, attached to the gateway of the platform.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-28059829b1
  namespace: my-app
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
spec:
  hostnames:
  - app.example.com # (1)!
  parentRefs:
  - name: envoy-external # (2)!
    namespace: network
  rules:
  - backendRefs:
    - name: web
      port: 8000 # (3)!
    matches:
    - path:
        type: PathPrefix
        value: / # (4)!
  1. Your hostname.
  2. The gateway of the platform.
  3. Your port.
  4. Your path.

ports opens the port on your machine

In a development Compose, ports opens the port on your machine. On the cluster it does nothing by itself: combined with x-ingress, the platform builds the public entry.

Fields

Field Required Default Values
expose No Ports for the other services: "8000", a range "8000-8005", or with a protocol "53/udp"
ports No Ports you publish, in Compose syntax: "8080:8000"
x-ingress.routes Yes One route or more
x-ingress.routes[].hostname Yes A domain, in lowercase
x-ingress.routes[].port Yes A container port declared in ports
x-ingress.routes[].path No / A path that starts with /

Any other field in x-ingress is an error.

What you get

On your machine

You declare What it does
expose The other services of the stack reach it by name, at web:9090
ports Opens the port on your machine: localhost:8080 reaches port 8000 of the container
x-ingress Nothing. Docker does not serve the domain

On the cluster

You declare What it does
expose The other services reach it by the same name and port, web:9090
ports Nothing by itself. Port 8080 of your machine is not opened anywhere
ports with x-ingress The platform builds the public entry: app.example.com reaches port 8000

The gateway, its certificates, and the entry to the cluster belong to the platform. You write no manifest.

Who can reach your services

You declare nothing. Compose puts every service of your application on one network, and the platform keeps it that way on the cluster.

Who calls Reaches your services
Another service of your application Yes
The internet Only the port of a route in x-ingress, through the gateway
A service of another project No
Your application in another environment No

Your services keep their way out: they reach the internet and resolve names as before.

compose.production.yaml
services:
  web:
    image: my-app:local
    ports:
      - "8080:8000"
    x-ingress:
      routes:
        - hostname: app.example.com
          port: 8000
  worker:
    image: my-app:local
    command: [python, worker.py]

Generated on every release. You never write these files or see them.

The services of your application accept each other, and nobody else.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.network.default: 'true'
  name: default-network
  namespace: my-app
spec:
  ingress:
  - from: # (1)!
    - podSelector:
        matchLabels:
          com.docker.compose.project: my-app
          com.docker.compose.network.default: 'true'
  podSelector:
    matchLabels:
      com.docker.compose.project: my-app
      com.docker.compose.network.default: 'true'
  policyTypes:
  - Ingress # (2)!
  1. Who may come in: only the services of your application.
  2. Only what comes in is limited. Your services still reach the internet.

The gateway reaches web, and only the port of its route.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
  name: web-gateway
  namespace: my-app
spec:
  ingress:
  - from:
    - namespaceSelector: # (1)!
        matchLabels:
          kubernetes.io/metadata.name: network
      podSelector:
        matchLabels:
          gateway.envoyproxy.io/owning-gateway-name: envoy-external
          gateway.envoyproxy.io/owning-gateway-namespace: network
    ports:
    - port: 8000 # (2)!
      protocol: TCP
  podSelector:
    matchLabels:
      com.docker.compose.project: my-app
      com.docker.compose.service: web
  policyTypes:
  - Ingress
  1. The gateway of the platform, and nobody else.
  2. Only the port of your route.

Several domains

Add one route for each domain. All of them reach the same service.

compose.production.yaml
services:
  web:
    image: my-app:local
    ports:
      - "8080:8000"
    x-ingress:
      routes:
        - hostname: ${WEB_UY_HOST:?Set WEB_UY_HOST} # (1)!
          port: 8000
        - hostname: ${WEB_AR_HOST:?Set WEB_AR_HOST}
          port: 8000
  1. A Compose variable, so each environment sets its own domain. Compose stops with your message if the variable has no value.

Generated on every release. You never write these files or see them. Here the variables are uy.example.com and ar.example.com.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-32a076014b
  namespace: my-app
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
spec:
  hostnames:
  - uy.example.com
  parentRefs:
  - name: envoy-external
    namespace: network
  rules:
  - backendRefs:
    - name: web
      port: 8000
    matches:
    - path:
        type: PathPrefix
        value: /
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-3a76e3c803
  namespace: my-app
  labels:
    com.docker.compose.project: my-app
    com.docker.compose.service: web
spec:
  hostnames:
  - ar.example.com
  parentRefs:
  - name: envoy-external
    namespace: network
  rules:
  - backendRefs:
    - name: web
      port: 8000
    matches:
    - path:
        type: PathPrefix
        value: /

Rules

Rule Detail
A published port without a route stays internal The platform warns and publishes nothing
A route points to a published port port is a TCP container port declared in ports
A route is HTTP A UDP port, or one that does not speak HTTP, cannot have a route
A route is unique Two routes cannot share hostname and path
No ports, no address A service with no expose and no ports is not reachable by name on the cluster
One network for the application A networks declaration of your own is rejected