Nebari integration
If you are running skillsctl on a cluster managed by Nebari, you can use the NebariApp CRD instead of configuring an Ingress manually. The nebari-operator handles routing, TLS, and OIDC authentication automatically.
Prerequisites
Section titled “Prerequisites”- nebari-operator v0.1.0-alpha.16 or later, with
KEYCLOAK_EXTERNAL_URLset on the controller deployment (thenicdeploy configures this automatically). Device flow client provisioning requires alpha.16+. - The target namespace must have the
nebari.dev/managed=truelabel (ArgoCD’smanagedNamespaceMetadatahandles this automatically)
What NebariApp does
Section titled “What NebariApp does”When nebariapp.enabled=true, the Helm chart creates a NebariApp resource. The nebari-operator processes that resource and provisions:
- An
HTTPRouteto route traffic to the skillsctl service - A TLS certificate for the hostname via cert-manager
- A confidential Keycloak OIDC client for the server
- A public device flow Keycloak client for CLI authentication
- A Kubernetes Secret with client credentials, issuer URL, and device flow client ID
- RBAC (Role + RoleBinding) scoping Secret access to the app’s ServiceAccount
This means you do not need to configure Ingress, cert-manager, or Keycloak clients manually.
When to use NebariApp vs Ingress
Section titled “When to use NebariApp vs Ingress”Use NebariApp when:
- You are deploying to a cluster running the nebari-operator
- You want to reuse the cluster’s Keycloak for authentication
Use ingress.enabled=true when:
- You are on a non-Nebari cluster
- You have your own Ingress controller and TLS setup
- You are using an external OIDC provider
Do not enable both at the same time.
Deploying with ArgoCD
Section titled “Deploying with ArgoCD”Most Nebari clusters use ArgoCD for GitOps. See examples/argocd-nebari.yaml in the repository for a complete ArgoCD Application manifest.
The key values:
nebariapp: enabled: true hostname: skillsctl.your-domain.com routing: routes: - pathPrefix: / auth: enabled: true provider: keycloak provisionClient: true enforceAtGateway: false deviceFlowClient: enabled: true scopes: - openid - profile - emailImportant settings:
routing.routesis required - without it, no HTTPRoute is created and the service is unreachableenforceAtGateway: falselets the server validate bearer tokens directly, which is required for CLI device flow authenticationdeviceFlowClient.enabled: trueprovisions a public Keycloak client forskillsctl auth login
The ArgoCD app should use managedNamespaceMetadata to label the namespace and ServerSideApply=true for compatibility with operator-managed resources:
syncPolicy: managedNamespaceMetadata: labels: nebari.dev/managed: "true" syncOptions: - CreateNamespace=true - ServerSideApply=trueDeploying with Helm directly
Section titled “Deploying with Helm directly”If you prefer Helm over ArgoCD, install from the published Helm repository:
helm repo add nebari https://nebari-dev.github.io/helm-repository/helm repo updatehelm install skillsctl nebari/skillsctl \ --namespace skillsctl --create-namespace \ -f values.yamlOr directly from the OCI registry:
helm install skillsctl oci://quay.io/nebari/charts/skillsctl \ --namespace skillsctl --create-namespace \ -f values.yamlOr from a local checkout:
git clone https://github.com/nebari-dev/skillsctl.githelm install skillsctl ./skillsctl/chart \ --namespace skillsctl --create-namespace \ -f values.yamlLabel the namespace for the operator:
kubectl label namespace skillsctl nebari.dev/managed=trueOIDC configuration
Section titled “OIDC configuration”When NebariApp is enabled with auth.provisionClient: true, the nebari-operator handles OIDC automatically. You do not need to set oidc.issuerURL or oidc.clientID in your values - the operator writes them to a Kubernetes Secret, and the Helm chart injects them as environment variables.
The server’s /auth/config endpoint returns the issuer URL, client ID, and device flow client ID so the CLI can self-configure. Users just run skillsctl auth login.
Verifying the deployment
Section titled “Verifying the deployment”Check that the NebariApp reconciled successfully:
kubectl describe nebariapp skillsctl -n skillsctlAll conditions should be True:
Ready- overall statusTLSReady- certificate provisionedRoutingReady- HTTPRoute createdAuthReady- OIDC clients provisioned
Check the Secret was created with all expected keys:
kubectl get secret skillsctl-oidc-client -n skillsctl -o jsonpath='{.data}' | jq 'keys'Expected: ["client-id", "client-secret", "device-client-id", "issuer-url"]
Check RBAC:
kubectl get role,rolebinding -n skillsctlExpected: skillsctl-oidc-secret-reader Role and RoleBinding.
Test the health endpoint:
curl https://skillsctl.your-domain.com/healthzExpected: ok
Test auth config:
curl https://skillsctl.your-domain.com/auth/configExpected: JSON with enabled: true, issuer_url, client_id, and device_client_id.
Next steps
Section titled “Next steps”- Configuration reference - environment variables
- Auth concepts - how the OIDC device flow works