A self-hosted PaaS that also secures what it runs
Octelium is a free and open source, self-hosted zero trust access platform. Managed containers let the Cluster deploy, scale and serve your containerized applications as Services, so the thing that runs an application and the thing that decides who may reach it are the same declarative resource.
- Free and open source
- Managed TLS and DNS
kind: Service
metadata:
name: web
spec:
mode: WEB
isPublic: true
config:
upstream:
container:
port: 3000
image: ghcr.io/acme/web:1.2.0
replicas: 3kind: Service
metadata:
name: blog
spec:
mode: WEB
isPublic: true
isAnonymous: true
config:
upstream:
container:
port: 3000
image: ghcr.io/acme/blog:2.4.0kind: Policy
metadata:
name: staff
spec:
rules:
- effect: ALLOW
condition:
all:
of:
- match: ctx.user.spec.type == "HUMAN"
- match: '"staff" in ctx.user.spec.groups'A unified secure access platform
Deployment is the last of them, and the one this page is about. Each capability is described in detail below, in the same order.
- Unified access platform
One architecture for client-based zero-config VPN-like access over WireGuard and QUIC tunnels and clientless access over browsers for humans and OAuth2 access for workloads.
Read more - L7-aware access control
Identity-based, application-layer aware ABAC access control with policy-as-code via CEL and OPA, applied on a per-request basis.
Read more - Secretless access
Dynamic secretless access where teams and AI agents access protected infrastructure without ever holding a credential.
Read more - Unified identity management
Teams, workloads and AI agents share the same model for identity management, access control, and auditing.
Read more - Real-time visibility and auditing
OpenTelemetry-native, real-time, application-layer-aware visibility and structured logging.
Read more - GitOps-friendly management
Octelium is administered like Kubernetes. Define your resources in YAML files, store them in a Git repository.
Read more
One Service definition, whoever is reaching it
Each application is represented by a Service, which is served by an identity-aware proxy. Whether the application is deployed by the Cluster or already running elsewhere is a detail of the upstream, and how it is reached is a separate field.
- Published clientlessly, the application is served at its own public FQDN over HTTPS, for people in a browser and for workloads over standard OAuth2 and bearer authentication.
- Left private, it is reachable only over a client-based WireGuard or QUIC tunnel at a stable private name, which suits internal tools that should not be on the internet at all.
- Made anonymous, it is a public endpoint with authentication switched off, which is what a marketing site, a documentation site or a public API needs.
kind: Service
metadata:
name: api
spec:
mode: HTTP
isPublic: true
config:
upstream:
container:
port: 8080
image: ghcr.io/acme/api:3.1.0
replicas: 4kind: Service
metadata:
name: api
spec:
mode: HTTP
isPublic: true
config:
upstream:
url: http://10.0.6.40:8080kind: Service
metadata:
name: api
spec:
mode: HTTP
config:
upstream:
loadbalance:
endpoints:
- url: https://backend1.example.com
- url: https://backend2.example.comLayer-7 aware access control with policy-as-code
Access is authorized on a per-request basis, using CEL or Open Policy Agent, against the identity behind the request and the content of the request itself. For a deployed application that removes the need to build authentication into it.
- Application-layer-aware access control that includes HTTP methods, paths, headers and serialized JSON body content, SSH users, Kubernetes verbs, resources and namespaces, and PostgreSQL and MySQL users and databases.
- Anonymous Services can enable anonymous authorization, which keeps them publicly reachable while still filtering on the request, so a public API is not simply open to everything.
- Plugins add rate limiting with a sliding window, JSON schema validation, response caching, direct responses and arbitrary manipulation with Lua or Envoy ExtProc.
kind: Policy
metadata:
name: api-read-only
spec:
rules:
- effect: ALLOW
condition:
all:
of:
- match: ctx.request.http.method == "GET"
- match: ctx.request.http.path.startsWith("/v1/")
- match: '"partners" in ctx.user.spec.groups'kind: Service
metadata:
name: blog
spec:
mode: HTTP
isPublic: true
isAnonymous: true
config:
upstream:
container:
port: 3000
image: ghcr.io/acme/blog:2.4.0
authorization:
enableAnonymous: true
inlinePolicies:
- spec:
rules:
- effect: ALLOW
condition:
match: ctx.request.http.method == "GET"kind: Service
metadata:
name: api
spec:
mode: HTTP
isPublic: true
config:
upstream:
container:
port: 8080
image: ghcr.io/acme/api:3.1.0
http:
plugins:
- name: minute-limit
condition:
matchAny: true
rateLimit:
limit: 120
window:
minutes: 1Configuration and credentials in one place
Secrets are Cluster resources. They fill environment variables in a deployed container, authenticate a pull from a private registry, and authenticate the upstreams that the application itself reaches.
- Environment variables can be literal values, references to Cluster Secrets, or references to an existing Kubernetes secret, so nothing has to be committed alongside the manifest.
- Private registry credentials are held the same way, so pulling a private image is part of the Service definition rather than a separate Kubernetes object to maintain.
- The APIs and databases a deployed application reaches can themselves be Services with their credentials injected, so the application holds no API key or database password of its own.
kind: Service
metadata:
name: api
spec:
mode: HTTP
config:
upstream:
container:
port: 8080
image: ghcr.io/acme/api:3.1.0
env:
- name: LOG_LEVEL
value: info
- name: DATABASE_PASSWORD
fromSecret: pg-passwordkind: Service
metadata:
name: api
spec:
mode: HTTP
config:
upstream:
container:
port: 8080
image: ghcr.io/acme/api:3.1.0
credentials:
usernamePassword:
username: ghcr-user
password:
fromSecret: ghcr-tokenkind: Service
metadata:
name: stripe
spec:
mode: HTTP
config:
upstream:
url: https://api.stripe.com
http:
auth:
bearer:
fromSecret: stripe-api-keyA unified identity model for humans and workloads
Human and workload Users share the same identity management, authentication, access control and visibility model. The pipeline that deploys an application and the people who use it are identities in the same system.
- Deployment pipelines authenticate with authentication tokens, the OAuth2 client credentials flow, or secretlessly with federated OIDC assertions from GitHub Actions and other CI platforms.
- People authenticate through any OpenID Connect or SAML 2.0 identity provider, or GitHub OAuth2, with native FIDO2 and WebAuthn passkeys, TOTP and TPM 2.0 authenticators on top of it.
- MCP servers and inference servers are deployed exactly like any other application, and get the protocol-aware MCP and LLM gateway modes in front of them.
- Cordium, built on Octelium, provides isolated sandboxes for agents and developers, where processes reach authorized Services through the Workspace identity with no secrets inside the sandbox.
kind: IdentityProvider
metadata:
name: github-actions
spec:
oidcIdentityToken:
issuerURL: https://token.actions.githubusercontent.com
audience: https://example.comkind: User
metadata:
name: deployer
spec:
type: WORKLOAD
groups:
- cikind: Service
metadata:
name: tools-mcp
spec:
mode: MCP
isPublic: true
config:
upstream:
container:
port: 8080
image: ghcr.io/acme/mcp:1.4.0
replicas: 2
mcp:
endpoint: /mcpReal-time visibility at the application layer
OpenTelemetry-ready, application-layer aware, structured auditing and visibility emitted to your OpenTelemetry OTLP receivers, where they can be exported to your log management and SIEM tools.
- Application-layer detail includes HTTP requests and responses, gRPC services and methods, and MCP and LLM operations, without instrumenting the deployed application itself.
- Logs are exported over OTLP to the log management and SIEM providers you already operate, so a deployment does not come with its own separate observability stack.
Replicas, limits, volumes and probes
A Cluster runs on Kubernetes and uses it to scale its own data plane, place Service proxies across Gateways, and run the containerized applications it deploys.
- Replica counts, CPU and memory limits, extended resources such as a GPU, security contexts, volumes and health probes are all set on the Service that runs the container.
- Public DNS records and TLS certificates for published Services are managed by the Cluster, so putting an application on a domain is not a separate certificate workflow.
- The whole platform is managed declaratively. Applications, Policies, Groups and Secrets are YAML in a Git repository, applied with a single octeliumctl apply.
$ octeliumctl apply ./cluster
Secret: ghcr-token Updated
Secret: pg-password Updated
IdentityProvider: github Updated
Policy: staff Created
Group: staff Created
Group: ci Updated
Service: web.apps Created
Service: api.apps Created
Service: blog.apps Created
Namespace: apps Created
Cluster Core resources successfully applied
7 resources created
3 resources updated
ClusterConfig updatedWhat a deployment platform has to do, and what does each part
Running an application is only the first of the jobs a platform ends up owning. These are the rest, next to the mechanism in an Octelium Cluster that handles each one.
- 01
Run the container
A container image is set as the Service upstream and scheduled on the Cluster's data-plane nodes, using the Kubernetes infrastructure the Cluster already runs on. Custom commands and arguments are supported where the image needs them.
Read more - 02
Pull it from where it lives
Public and private registries are both supported, with registry credentials referenced from Cluster Secrets rather than stored as a Kubernetes pull secret you maintain separately.
Read more - 03
Configure it
Environment variables can be literal values, references to Cluster Secrets, or references to an existing Kubernetes secret, so configuration and credentials arrive the same way in every environment.
Read more - 04
Scale it and bound it
Replica counts scale an application up and down, and CPU, memory and extended resource limits are set per Service, including requesting a GPU where the Kubernetes cluster can schedule one.
Read more - 05
Give it storage
Kubernetes volumes are mounted into managed containers, including persistent volume claims and emptyDir volumes with a size limit, so an application that needs state is not excluded.
Read more - 06
Put it on a domain
A published Service gets its own public FQDN under your Cluster's domain, with the public DNS record and the TLS certificate managed by the Cluster rather than by a separate certificate workflow.
Read more - 07
Decide who reaches it
The same Service is either anonymous and public, published behind your identity provider, private to connected clients, or several of those at once, and that is a field rather than a different deployment.
Read more - 08
Show what it is doing
Every request produces an identity-aware, application-layer AccessLog streamed over OpenTelemetry, and health probes and resource limits are part of the same Service definition.
Read more
Building and pushing images stays in whatever CI you already use. What this replaces is the layer after that: the ingress, the certificate, the authentication in front, and the audit trail behind.
The same platform capabilities ship with every deployment, whichever way you use it.
Frequently asked
- What can it deploy?
- Any container image that listens on 0.0.0.0 on a known port. That covers web applications built with Next.js, Vite, Astro and similar frameworks, REST and gRPC APIs, MCP servers, inference servers and ordinary background services. Custom commands and arguments, environment variables from Secrets, replica counts, resource limits, security contexts, volumes and health probes are all part of the Service definition.
- Does it build images as well?
- No. Octelium deploys and serves an image you have already built and pushed to a registry, public or private. Building and pushing stay in whatever CI you already use, and the pipeline that does it can itself be a workload User authorized to apply the change.
- How is an application exposed?
- Through the same Service that runs it. Publishing it makes the Cluster's internet-facing proxy serve it at its own public FQDN with a managed TLS certificate. It can require an identity from your own identity provider, or be anonymous and open to everyone, and an anonymous Service can still filter on path, method, headers and body content.
- Can it use persistent storage?
- Yes. Kubernetes volumes are mounted into managed containers, including persistent volume claims and emptyDir volumes with a size limit, with mount paths and read-only flags set per Service.
- Do I need Kubernetes experience?
- No. An Octelium Cluster runs on top of Kubernetes and uses it to schedule managed containers, but the interface you work with is Octelium's own resources. The quick installer provisions a complete single-node Cluster, including Kubernetes itself, on one fresh Linux VM with 2 vCPUs, 2 GB of RAM and a domain you own.
- Is Octelium free and open source?
- Yes. Octelium is free and open source and is designed for single-tenant self-hosting. There is no proprietary cloud-based control plane, and it is not a limited edition of a separate paid product. An enterprise package is available for organizations that need capabilities such as a web console, SCIM provisioning and secret encryption at rest, and it is free for personal, homelab and evaluation use.
Related solutions
Open source API gateway
Per-request authorization, secretless upstream credentials, rate limits and schema validation in front of your APIs.
Read morengrok alternative
Publish a local service on your own domain and your own infrastructure, with identity and policy in front of it.
Read moreHomelab remote access
Reach everything at home from anywhere without forwarding a port, and host what you want on your own domain.
Read moreDeploy Octelium on your own infrastructure in minutes
Free and open source. Self-hosted. No vendor lock-in.