Quickstart
Deploy OGO on an OpenShift cluster with PostgreSQL and the OpenShell gateway.
Prerequisites
- OpenShift 4.16+ cluster with
ocCLI configured openshellCLI installed (brew install nvidia/tap/openshell)
Choose a deployment path
| Path | Auth | TLS | Prerequisites |
|---|---|---|---|
| With Envoy Gateway | OpenShift SSO (browser login) | Let's Encrypt via cert-manager | cert-manager |
| Without Envoy Gateway | mTLS (client certificates) | Self-signed (operator-managed) | None |
Envoy Gateway is required for OpenShift SSO because the OpenShell gateway
needs the OIDC issuer on the same hostname as the gateway endpoint. Without
Envoy, use mTLS client certificates or port-forward for access. OGO
installs and configures Envoy Gateway itself — you don't need Helm or a
manual install for the common case (see
Advanced: bring your own Envoy Gateway
if you already run it for other workloads).
With Envoy Gateway
This path gives you browser-based SSO login and Let's Encrypt TLS. OGO
installs Envoy Gateway, its CRDs, a GatewayClass, and the Gateway API
resources automatically when you create a CR with route.gatewayAPI.enabled:
true — no manual Helm install needed.
1. Install cert-manager
If not already installed:
oc apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
oc wait --for=condition=Available deployment/cert-manager -n cert-manager --timeout=120s
Create a ClusterIssuer for Let's Encrypt (requires DNS challenge for wildcard certs, or HTTP challenge for single-host certs):
cat <<EOF | oc apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-key
solvers:
- http01:
ingress: {}
EOF
2. Deploy the operator
make deploy IMG=quay.io/aknochow/ogo:latest
oc wait --for=condition=Available deployment/ogo-controller-manager -n ogo --timeout=120s
3. Set up PostgreSQL
oc adm policy add-scc-to-user anyuid -z default -n ogo
oc create deployment ogo-pg -n ogo --image=docker.io/library/postgres:16
oc set env deployment/ogo-pg -n ogo \
POSTGRES_USER=openshell POSTGRES_PASSWORD=openshell POSTGRES_DB=openshell
oc expose deployment/ogo-pg -n ogo --port=5432
oc create secret generic ogo-pg -n ogo \
--from-literal=uri='postgresql://openshell:openshell@ogo-pg.ogo.svc:5432/openshell'
4. Create user groups
oc adm groups new openshell-users
oc adm groups add-users openshell-users your-username
oc adm groups new openshell-admins
oc adm groups add-users openshell-admins your-username
5. Create the Gateway
Replace your-cluster.example.com with your cluster's apps domain:
# ogo-gateway.yaml
apiVersion: gateway.ogo.aknochow.io/v1alpha1
kind: OpenShellGateway
metadata:
name: openshell
spec:
namespace: ogo
replicas: 1
database:
secretName: ogo-pg
sandbox:
defaultImage: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
workspaceStorageSize: "2Gi"
appArmorProfile: "Unconfined"
tls:
enabled: false
certManager:
issuerName: letsencrypt
issuerKind: ClusterIssuer
route:
hostname: openshell.apps.your-cluster.example.com
gatewayAPI:
enabled: true
auth:
openshift:
userGroup: openshell-users
adminGroup: openshell-admins
tokenTTL: "8h"
logLevel: info
networkPolicy:
enabled: true
oc apply -f ogo-gateway.yaml
OGO installs Envoy Gateway (CRDs, controller, a GatewayClass named eg
configured for a ClusterIP proxy Service — not a cloud LoadBalancer,
which commonly fails outright on managed OpenShift clusters with a
LoadBalancer quota), grants it the SCCs it needs, and bridges an OpenShift
Route to it. No manual SCC-granting step needed.
Verify the gateway is running:
oc get openshellgateway
# Should show: openshell Running https://openshell.apps.your-cluster.example.com:443
If it's not Running after a couple minutes, check status.conditions for
which stage is stuck (EnvoyGatewayReady, EnvoyProxySCCReady,
EnvoyRouteReady) — each one reports a specific reason instead of just
"not ready":
oc get openshellgateway openshell -o jsonpath='{.status.conditions}' | jq
6. Connect
openshell gateway add https://openshell.apps.your-cluster.example.com \
--name my-cluster \
--oidc-issuer https://openshell-auth.apps.your-cluster.example.com
openshell sandbox create --gateway my-cluster
Advanced: bring your own Envoy Gateway
If you already run Envoy Gateway on this cluster for other workloads,
create its GatewayClass yourself (with the name you want to reference in
route.gatewayAPI.gatewayClassName) before creating the OGO CR — OGO
detects an existing GatewayClass and won't try to install its own.
The certgen pre-install hook runs as uid 65534, outside OpenShift's allowed UID range. Pre-create the namespace and service account, grant SCC, then install with the UID overridden.
IMPORTANT: --skip-crds skips all CRDs bundled in the Helm chart, not
just the core gateway.networking.k8s.io ones that need skipping on
OpenShift 4.20+ (managed by the Ingress Operator). It also throws out
Envoy Gateway's own gateway.envoyproxy.io CRDs (EnvoyProxy,
ClientTrafficPolicy, etc.), which nothing else provides — install those
explicitly instead of relying on the Helm chart for them:
# 1. Pre-create namespace and certgen service account
oc create namespace envoy-gateway-system
oc create sa eg-gateway-helm-certgen -n envoy-gateway-system
oc adm policy add-scc-to-user anyuid -z eg-gateway-helm-certgen -n envoy-gateway-system
# 2. Install Envoy Gateway's own CRDs (skipped by --skip-crds below, and not
# covered by the OpenShift Ingress Operator's Gateway API CRD management)
helm show crds oci://docker.io/envoyproxy/gateway-helm --version v1.3.2 > /tmp/eg-crds.yaml
awk '
BEGIN { doc = "" }
/^---/ { if (doc ~ /group: gateway\.envoyproxy\.io/) print doc "---"; doc = ""; next }
{ doc = doc $0 "\n" }
END { if (doc ~ /group: gateway\.envoyproxy\.io/) print doc }
' /tmp/eg-crds.yaml > /tmp/eg-envoyproxy-crds.yaml
oc apply -f /tmp/eg-envoyproxy-crds.yaml --server-side
# 3. Install — skip only the core Gateway API CRDs (OpenShift 4.20+ manages
# those), override certgen UID so OpenShift assigns from the namespace range
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.3.2 -n envoy-gateway-system --skip-crds \
--set-json 'certgen.job.securityContext.runAsUser=null' \
--set-json 'certgen.job.securityContext.runAsGroup=null' \
--set-json 'certgen.job.securityContext.seccompProfile=null'
# 4. Grant anyuid SCC to the main controller service account — same
# fixed-UID problem as certgen above (65532, outside the namespace's
# allocated range). anyuid allows an arbitrary fixed UID without
# granting privileged's host-level access.
oc adm policy add-scc-to-user anyuid -z envoy-gateway -n envoy-gateway-system
# 5. Remove the seccompProfile anyuid's provider rejects — same
# seccomp-annotation problem as certgen, but on the main Deployment's
# container, which the --set-json overrides above don't reach
oc patch deployment envoy-gateway -n envoy-gateway-system --type=json \
-p='[{"op": "remove", "path": "/spec/template/spec/containers/0/securityContext/seccompProfile"}]'
Create an EnvoyProxy so the managed proxy Service is ClusterIP (a cloud
LoadBalancer — Envoy Gateway's own default — commonly fails outright on
managed OpenShift clusters with a LoadBalancer quota), then reference it
from the GatewayClass:
cat <<EOF | oc apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: openshift-clusterip
namespace: envoy-gateway-system
spec:
provider:
type: Kubernetes
kubernetes:
envoyService:
type: ClusterIP
EOF
cat <<EOF | oc apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: openshift-clusterip
namespace: envoy-gateway-system
EOF
Verify Envoy Gateway is ready, then continue from step 2 of the main path above:
oc get gatewayclasses
# Should show: eg ACCEPTED True
If you're retrying after an earlier failed attempt, OGO's own
auto-install may have already created cluster-scoped resources with names
that collide with a fresh Helm install (invalid ownership metadata from
Helm). Check for and remove them first:
oc get clusterrole,clusterrolebinding -l app.kubernetes.io/managed-by=ogo
# If present, delete the ones matching eg-gateway-helm-* / envoy-gateway-*
Without Envoy Gateway
This path uses self-signed TLS with a passthrough OpenShift Route. No external prerequisites. Authentication uses mTLS client certificates.
1. Deploy the operator
make deploy IMG=quay.io/aknochow/ogo:latest
oc wait --for=condition=Available deployment/ogo-controller-manager -n ogo --timeout=120s
2. Set up PostgreSQL
oc create deployment ogo-pg -n ogo --image=docker.io/library/postgres:16
oc set env deployment/ogo-pg -n ogo \
POSTGRES_USER=openshell POSTGRES_PASSWORD=openshell POSTGRES_DB=openshell
oc expose deployment/ogo-pg -n ogo --port=5432
oc adm policy add-scc-to-user anyuid -z default -n ogo
oc create secret generic ogo-pg -n ogo \
--from-literal=uri='postgresql://openshell:openshell@ogo-pg.ogo.svc:5432/openshell'
3. Create the Gateway
# ogo-gateway.yaml
apiVersion: gateway.ogo.aknochow.io/v1alpha1
kind: OpenShellGateway
metadata:
name: openshell
spec:
namespace: ogo
replicas: 1
database:
secretName: ogo-pg
sandbox:
defaultImage: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
workspaceStorageSize: "2Gi"
tls:
enabled: true
route:
hostname: openshell.apps.your-cluster.example.com
gatewayAPI:
enabled: false
auth:
openshift:
enabled: false
userGroup: openshell-users
logLevel: info
networkPolicy:
enabled: false
oc apply -f ogo-gateway.yaml
Verify:
oc get openshellgateway
oc get route -n ogo
# Should show a passthrough Route
4. Connect
openshell gateway add https://openshell.apps.your-cluster.example.com \
--name my-cluster --gateway-insecure
openshell sandbox create --gateway my-cluster
The --gateway-insecure flag is needed because the self-signed TLS
certificate is not trusted by the CLI. For production, use the Envoy
Gateway path with Let's Encrypt certificates.
Teardown
To completely remove OGO from the cluster:
# 1. Delete the gateway CR (triggers finalizer cleanup of all resources)
oc delete openshellgateway openshell
# 2. Delete the database
oc delete deployment ogo-pg -n ogo
oc delete svc ogo-pg -n ogo
oc delete secret ogo-pg -n ogo
# 3. Undeploy the operator, CRDs, and RBAC
make undeploy
# 4. Clean up cluster-scoped resources
oc delete oauthclient openshell 2>/dev/null
oc delete groups openshell-users openshell-admins 2>/dev/null
# 5. Delete the namespace
oc delete ns ogo
# 6. (If using the manual "bring your own Envoy Gateway" path) remove it
helm uninstall eg -n envoy-gateway-system
oc delete ns envoy-gateway-system
oc delete gatewayclass eg
If OGO auto-installed Envoy Gateway (the default "With Envoy Gateway" path), it doesn't remove it on CR deletion — it's a cluster-scoped shared component, so this is a deliberate choice, not an oversight. Check the operator logs to confirm whether it installed one:
oc logs -n ogo deployment/ogo-controller-manager | grep "Envoy Gateway cleanup skipped"
If it did, remove the auto-installed resources with:
oc delete deployment,service,configmap,serviceaccount,job,gatewayclass,envoyproxy \
-l app.kubernetes.io/managed-by=ogo,ogo.aknochow.io/component=envoy-gateway \
-n envoy-gateway-system
oc delete clusterrole,clusterrolebinding \
-l app.kubernetes.io/managed-by=ogo,ogo.aknochow.io/component=envoy-gateway
Next steps
- Dev Spaces - create sandboxes from Dev Spaces workspaces
- Envoy Gateway - gRPC ingress architecture details
- OpenShift SSO - authentication and user groups
- Provider - inject API keys into sandboxes
- Policy - control sandbox network and filesystem access