ManagementGuideService ExamplesHTTP-based Applications
Octelium as a Kubernetes Ingress Controller

As Octelium Clusters operate on top of Kubernetes, the Octelium Cluster can seamlessly function as an advanced Kubernetes ingress controller that provides L7-based routing, filtering, access control, visibility as well as automatic TLS termination, load balancing, request and response manipulation including paths, headers, and body content, including serialized JSON body content. It is important, however, to distinguish Octelium from conventional ingress controllers. Octelium does not utilize or monitor native Kubernetes Ingress resources or their associated rules. Instead, it employs its own Services to implement ingress functionality (read more about Services here). Because Octelium operates independently of the standard Kubernetes Ingress API, it can be deployed alongside traditional ingress controllers within the same cluster without conflict

A Simple Example

In this guide we are going to mostly assume that your internal Kubernetes services require no authentication and thus use anonymous Services (read more here).

If your internal Kubernetes service has the name my-k8s-svc in the default Kubernetes namespace and listening over the TCP/8080 port, then you can expose it as follows:

1
kind: Service
2
metadata:
3
name: my-svc
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
config:
9
upstream:
10
url: http://my-k8s-svc.default.svc:8080

In addition to being able to use internal Kubernetes services as upstreams for your Octelium Service, Octelium also enables you to directly deploy, manage and scale your containerized applications and serve them as Services by reusing the underlying Kubernetes infrastructure that runs the Octelium Cluster and deploying your Dockerized images on top of it. (read more about managed containers here)

1
kind: Service
2
metadata:
3
name: my-api
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
config:
9
upstream:
10
container:
11
port: 3000
12
image: ghcr.io/<ORG>/<IMAGE>:<TAG>
13
replicas: 3
14
credentials:
15
usernamePassword:
16
username: linus
17
password:
18
fromSecret: reg-password
19
resourceLimit:
20
cpu:
21
millicores: 2000
22
memory:
23
megabytes: 4000

You can now apply the creation of the Service as follows (read more here):

octeliumctl apply /PATH/TO/SERVICE.YAML

Now your internal my-k8s-svc Kubernetes service is exposed publicly at the https://my-svc.<DOMAIN>.

NOTE

You might want to have a look at Namespaces (read more here) to group and organize your Services' FQDNs.

Octelium Services are implemented as Kubernetes deployment with a single replica by default. You can horizontally scale up or down your Service as follows:

1
kind: Service
2
metadata:
3
name: my-svc
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
config:
9
upstream:
10
url: http://my-k8s-svc.default.svc:8080
11
deployment:
12
replicas: 5

Dynamic Routing

Octelium enables you to dynamically route to a specific upstream, not only based on request paths as in Kubernetes-native ingress controllers, but also based on request headers, method, query parameters, and even body content, including serialized JSON body content. You can use dynamic configuration (read more here) to define multiple configurations that can be chosen on a per-request basis via CEL/OPA policy-as-code rules. Here is a simple example:

1
kind: Service
2
metadata:
3
name: my-api
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
dynamicConfig:
9
configs:
10
- name: v1
11
upstream:
12
url: http://apiv1.default.svc:8080
13
- name: v2
14
upstream:
15
url: http://apiv2.default.svc:8080
16
rules:
17
- condition:
18
match: 'ctx.request.http.path.startsWith("/v1")'
19
configName: v1
20
- condition:
21
all:
22
of:
23
- match: 'ctx.request.http.path.startsWith("/v2")'
24
- match: ctx.request.http.method in ["POST", "GET", "PUT"]
25
- match: ctx.request.http.headers["user-agent"].toLower().contains("my-client")
26
- match: int(ctx.request.http.queryParams.page) < 100
27
configName: v2
28
- condition:
29
matchAny: true
30
configName: v1

You can also simultaneously deploy multiple containers that correspond to multiple configurations (e.g. multiple API versions) and route among them on a per-request basis. Here is an example:

1
kind: Service
2
metadata:
3
name: my-web-app
4
spec:
5
mode: WEB
6
dynamicConfig:
7
configs:
8
- name: c1
9
upstream:
10
container:
11
port: 3000
12
image: ghcr.io/org/image:v1
13
- name: c2
14
upstream:
15
container:
16
port: 3000
17
image: ghcr.io/org/image:v2
18
rules:
19
- condition:
20
match: ctx.request.http.path.startsWith("/v1")
21
configName: c1
22
- condition:
23
match: ctx.request.http.path.startsWith("/v2")
24
configName: c2

Request and Response Manipulation

Octelium also enables you to modify and rewrite your requests and responses. That includes header manipulation, path manipulation, CORS and retries. Here is a detailed example:

1
kind: Service
2
metadata:
3
name: my-svc
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
config:
9
upstream:
10
url: http://my-k8s-svc.default.svc:8080
11
http:
12
header:
13
addRequestHeaders:
14
- key: X-Header-1
15
value: VALUE-1
16
removeRequestHeaders:
17
- X-Header-2
18
addResponseHeaders:
19
- key: X-Header-3
20
value: VALUE-3
21
removeResponseHeaders:
22
- X-Header-4
23
path:
24
removePrefix: /v1
25
addPrefix: /v2
26
enableRequestBuffering: true
27
body:
28
maxRequestSize: 100000
29
mode: JSON
30
retry:
31
maxRetries: 10
32
initialInterval:
33
milliseconds: 500
34
maxInterval:
35
seconds: 5
36
maxElapsedTime:
37
seconds: 12
38
multiplier: 1.5
39
statusCodes: [503, 429]
40
retryOnServerErrors: true
41
cors:
42
allowOriginStringMatch: ["https://example.com"]
43
allowMethods: "POST, GET, OPTIONS"
44
allowHeaders: "X-PINGOTHER, Content-Type"
45
exposeHeaders: "Content-Encoding, Kuma-Revision"
46
maxAge: "86400"
47
allowCredentials: true

You can also use Octelium's HTTP plugins, including Lua scripts and Envoy's ExtProc compliant servers, to sanitize and manipulate your request and responses (read more about HTTP plugins here) in arbitrarily complex ways. For example, you can use this to manipulate the JSON body content of requests/responses and call external APIs to implement geolocation-based access control and/or rate limiting. Here is an example:

1
kind: Service
2
metadata:
3
name: my-svc
4
spec:
5
mode: HTTP
6
isPublic: true
7
isAnonymous: true
8
config:
9
upstream:
10
url: http://my-k8s-svc.default.svc:8080
11
http:
12
plugins:
13
- name: minute-rate-limit
14
condition:
15
matchAny: true
16
ratelimit:
17
limit: 100
18
window:
19
minutes: 2
20
- name: hour-rate-limit
21
condition:
22
matchAny: true
23
ratelimit:
24
limit: 1000
25
window:
26
hours: 6
27
- name: rate-limit-auth
28
condition:
29
match: ctx.request.http.path.startsWith("/auth")
30
ratelimit:
31
limit: 10
32
window:
33
minutes: 1
34
- name: validate-users
35
condition:
36
all:
37
of:
38
- match: ctx.request.http.path == "/users"
39
- match: ctx.request.http.method in ["POST", "PUT"]
40
jsonSchema:
41
inline: <YOUR_JSON_SCHEMA>
42
- name: drop-php
43
condition:
44
match: ctx.request.http.path.toLower().endsWith(".php")
45
direct:
46
statusCode: 400
47
body:
48
inline: '{"error": "invalid path"}'
49
- name: lua-cleanup
50
condition:
51
matchAny: true
52
lua:
53
inline: |
54
function onRequest(ctx)
55
if strings.hasPrefix(ctx.request.http.path, "/apis/v0") then
56
octelium.req.exit(400)
57
end
58
end
59
60
61
function onResponse(ctx)
62
octelium.req.deleteResponseHeader("X-Internal")
63
end

Access Control

So far, we have used an anonymous Service, which allows all access publicly over the internet. This could be useful for hosting and testing public websites, APIs, webhooks, etc. Since Octelium is a zero trust access platform, it is primarily designed for identity-based access control you might want to only restrict the Service access to the Cluster's Users who can access the Service after authenticating to a web-based IdentityProvider (e.g. OpenID Connect, SAML 2.0, GitHub OAuth2) via their browsers (read more here). You can do so by removing the isAnonymous field and adding Policies for who is allowed to access the Service (read more about Policies and access control here). Here is an example:

1
kind: Service
2
metadata:
3
name: my-svc
4
spec:
5
mode: HTTP
6
isPublic: true
7
config:
8
upstream:
9
url: http://my-k8s-svc.default.svc:8080
10
authorization:
11
inlinePolicies:
12
- spec:
13
rules:
14
- effect: ALLOW
15
condition:
16
all:
17
of:
18
- match: ctx.user.spec.email.endsWith("@example.com")
19
- match: ctx.request.http.path.startsWith("/apis")
20
- match: ctx.request.http.method in ["GET", "POST", "PUT", "DELETE"]
21
- match: ctx.request.http.uri == "/apis/users?name=john"
22
- match: ctx.request.http.queryParams.name == "john"
23
- match: ctx.request.http.headers["x-custom-header"] == "this-value"
24
- match: ctx.request.http.scheme == "http"
25
- match: string(ctx.request.http.body).toLower().contains("value1")
26
- match: ctx.request.http.bodyMap.key1 == "value1"

Moreover, Octelium also supports anonymous authorization where you can apply your Policies and InlinePolicies' rules in the anonymous mode. You can read more about anonymous authorization here.

Visibility

Octelium also provides OpenTelemetry-ready, application-layer L7 aware visibility and access logging in real time (see an example for HTTP here). You can read more about visibility here.

This was a very short guide to show you how to use Octelium to deploy, scale, route and provide dynamic zero trust secure access to your workloads. Here are a few more related features that you might be interested in:

  • Routing not just by request paths, but also by header keys and values, request body content including JSON (read more here).
  • Request/response header manipulation (read more here).
  • Cross-Origin Resource Sharing (CORS) (read more here).
  • gRPC mode (read more here).
  • Secretless access to upstreams and injecting bearer, basic, or custom authentication header credentials (read more here).
  • Exposing the API publicly for anonymous access (read more here).
  • Application layer-aware ABAC access control via policy-as-code using CEL and Open Policy Agent (read more here).
  • OpenTelemetry-ready, application-layer L7 aware auditing and visibility (read more here).
© 2026 octelium.comOctelium Labs, LLCAll rights reserved
Octelium and Octelium logo are trademarks of Octelium Labs, LLC.
WireGuard is a registered trademark of Jason A. Donenfeld