Someone says “we run OpenShift” and a Kubernetes learner hears “we run something else entirely.” That is rarely true. OpenShift is a distribution of Kubernetes with Red Hat packaging, opinionated defaults, and extra platform APIs. The scheduler still schedules Pods. Deployments still roll out ReplicaSets. Services still select Pods by labels.

I learned this the hard way. I spent months getting comfortable with kubectl, Ingress, and namespace RBAC on vanilla clusters. Then I joined a team on OpenShift and felt like a beginner again — not because Pods worked differently, but because the front door for HTTP traffic was a Route, the security model included SCCs, and oc had shortcuts I did not recognize.

This post is for that transition. I will not pretend OpenShift is trivial or that it is identical to every managed Kubernetes offering. I will separate what transfers cleanly from what deserves a deliberate read of the docs.

Same Kubernetes core underneath

OpenShift ships a tested, supported Kubernetes version. The API groups you use daily — apps/v1 Deployments, v1 Services and ConfigMaps, batch/v1 Jobs — are upstream Kubernetes resources.

If you can write this Deployment, it runs on OpenShift the same way:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  labels:
    app: api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: registry.example.com/team/api:1.4.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10

Apply it with kubectl or oc — both talk to the same API server:

kubectl apply -f deployment.yaml
# or
oc apply -f deployment.yaml

Check it the way you already would:

kubectl get deploy,rs,pod -l app=api
kubectl describe pod -l app=api
kubectl logs -l app=api --tail=50

On OpenShift, the same commands work. oc adds conveniences — oc status, oc logs -f deployment/api, project switching — but it is not a separate orchestrator. Think of it as kubectl plus platform-aware helpers.

What stays familiar:

  • Pod lifecycle, probes, and restart policies
  • Deployments, StatefulSets, DaemonSets, CronJobs
  • Services, Endpoints, EndpointSlices, DNS inside the cluster
  • ConfigMaps, Secrets, PersistentVolumeClaims
  • NetworkPolicy (with OVN-Kubernetes as the default CNI on modern OpenShift)
  • RBAC with Roles, RoleBindings, ClusterRoles

When documentation says “Kubernetes does X,” OpenShift usually does X too — unless the platform adds a wrapper or a stricter default.

Vendor packaging — what Red Hat adds

Calling OpenShift “just Kubernetes” undersells the platform layer. Calling it “completely different” oversells it. The honest middle ground: Red Hat ships Kubernetes plus an integrated product stack.

That stack typically includes:

  • OpenShift Router — exposes HTTP/S via Route objects (OpenShift’s long-standing edge pattern)
  • Ingress Controller — also available; Ingress resources work, often alongside Routes
  • Built-in image registryimage-registry.openshift-image-registry.svc inside the cluster
  • Operators and OLM — install and lifecycle-manage platform and third-party software
  • Web console — GUI for projects, routes, builds, monitoring
  • Monitoring and logging — Prometheus, Alertmanager, cluster logging operators pre-integrated on many installs
  • Security Context Constraints — Pod admission rules beyond Pod Security Standards
  • Projects — namespaces with extra metadata and default quotas/limits

You may not touch all of these on day one. Application developers often interact with Projects, Routes, Deployments, Services, and Secrets. Platform teams live in Operators, SCCs, cluster networking, and upgrade channels.

The packaging matters for operations, not for the mental model of “I deploy a containerized app behind a Service.” That model still holds.

Routes — the HTTP front door you will meet first

On many vanilla clusters, HTTP ingress means an Ingress resource plus a separately installed Ingress controller (nginx, traefik, AWS ALB, etc.). OpenShift has had Route as a first-class route.openshift.io API for years.

A Route maps an external hostname and path to a Service:

apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: api
  labels:
    app: api
spec:
  host: api.apps.cluster.example.com
  to:
    kind: Service
    name: api
    weight: 100
  port:
    targetPort: 8080
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

The OpenShift Router (historically HAProxy-based; implementations evolve) watches Route objects and programs edge load balancing. Edge TLS termination is common — the Router terminates HTTPS and forwards HTTP to the Service inside the cluster.

If you know Ingress, Routes will feel familiar with different field names:

ConceptIngress (typical)OpenShift Route
External hostnamespec.rules[].hostspec.host
Backend Servicebackend.servicespec.to.name
Portport.numberspec.port.targetPort
TLSspec.tlsspec.tls with termination modes

OpenShift also supports standard Ingress resources. Teams sometimes standardize on Ingress for portability and use Routes where the platform defaults are simpler. Both can coexist. I have seen teams pick one pattern per environment to avoid confusion.

For kubectl users:

oc get route
oc describe route api
kubectl get route -n my-project

Routes are the single biggest “this feels different” moment for Kubernetes users. They are not a reason to relearn Deployments — they are a reason to learn one new object at the edge.

Security Context Constraints — Pod security with OpenShift flavor

Kubernetes now has Pod Security Admission with restricted, baseline, and privileged levels. OpenShift adds Security Context Constraints (SCCs) — an older, richer admission layer that still governs many clusters.

An SCC answers questions like:

  • May this Pod run as root (UID 0)?
  • Which volume types are allowed?
  • May it use hostNetwork or hostPID?
  • Which SELinux contexts are permitted?

By default, most user workloads land on the restricted-v2 or similarly named SCC. That means:

  • Containers should not assume they can run as root
  • Privileged Pods require explicit permission
  • Some Helm charts written for permissive clusters fail until security contexts are adjusted

Symptoms look like Kubernetes admission failures:

oc describe pod failing-app-xxxxx
# Events:
#   Warning  FailedCreate  ... unable to validate against any security context constraint

Compare with:

oc get scc
oc describe scc restricted-v2

Fix paths usually involve one of:

  • Setting runAsNonRoot: true and a concrete runAsUser in the Pod spec
  • Requesting a less restrictive SCC via RBAC (platform team decision, not something to self-grant)
  • Adjusting the chart or image to not require root

I have spent afternoons on SCC errors that looked like mysterious “Pod pending forever” issues. The Deployment was fine. The image wanted root. The platform said no. That is not OpenShift being hostile — it is the platform enforcing a baseline I should have read first.

SCCs overlap conceptually with Pod Security Standards. On newer OpenShift versions, the story is converging, but SCCs remain the object platform admins grep during incidents.

Projects — namespaces with platform semantics

A Project is a Kubernetes Namespace plus OpenShift metadata. For daily work, treat them as the same isolation boundary:

oc project my-team-dev
kubectl config set-context --current --namespace=my-team-dev
oc get all

List projects:

oc get projects
kubectl get namespaces

Differences that matter in practice:

  • The web console speaks in “Projects”
  • Default ResourceQuotas and LimitRanges are often applied per project by admins
  • oc new-project creates a namespace and sets RBAC for the creator
  • Some cluster-scoped policies reference project labels

If you understand namespaces, RBAC, and quotas, Projects are not a new concept — they are the name OpenShift uses in UX and docs. I still think in namespaces when writing YAML; the API accepts metadata.namespace either way.

Operators — how platform software is installed

OpenShift embraces the Operator pattern heavily. An Operator is a controller that watches Custom Resources and reconciles complex software — databases, service meshes, the ingress/router stack itself.

Operator Lifecycle Manager (OLM) installs Operators from catalogs. Cluster admins subscribe to an Operator; OLM creates CSVs, Deployments, and CRDs.

As an application developer, you might interact with Operators when:

  • A DBA provisions PostgreSQL via a PostgresCluster CR instead of raw StatefulSet YAML
  • The monitoring stack is managed by cluster-level Operators you do not touch
  • Your team installs a vendor Operator into a namespace for a message queue

As a learner coming from Helm-only clusters, the shift is: some infrastructure is a CRD with a reconciliation loop, not a chart you upgrade manually.

Useful discovery commands:

oc get csv -A
oc get operators
oc get crd | head
kubectl api-resources | grep -i postgres

You do not need to author Operators to work on OpenShift. You do need to recognize when a workload is “just Kubernetes YAML” vs “managed by an Operator CR” — upgrades and backups may be Operator-owned.

When Kubernetes skills transfer cleanly

If you can do these on any Kubernetes cluster, you can do them on OpenShift with minimal friction:

Deploy and roll out applications — Deployments, rollouts, kubectl rollout status, image updates.

Debug Pod failureskubectl describe pod, logs, events, probe misconfiguration, OOMKilled, image pull errors.

Wire internal traffic — ClusterIP Services, DNS names, port alignment between Service and container.

Manage configuration — ConfigMaps and Secrets, env vars, volume mounts.

Understand RBAC basics — who can create what in which namespace/project.

Read YAML and use dry-runkubectl apply --dry-run=server -f catches many issues before apply.

GitOps workflows — Argo CD and Flux work on OpenShift; they reconcile the same resource types (plus Routes and other OpenShift CRs if you use them).

NetworkPolicy thinking — label selectors, explicit allow rules, default deny when policies select Pods.

The habits that transfer best are layered debugging and reading events. OpenShift incidents still start with “what changed in the Pod, Service, and route layer?”

Honest limits — what does not transfer automatically

I wish someone had listed these for me earlier.

Ingress muscle memory is incomplete. Routes are the native edge object. Ingress works, but docs, samples, and colleague snippets may assume Routes. Learn both names if your org uses both.

Security assumptions from permissive labs break. Kind and minikube often let you run privileged Pods. OpenShift may not. Charts that “just worked” locally need security context review.

Platform-owned namespaces are not yours. openshift-*, kube-*, and operator namespaces are managed by the cluster. Applying random YAML there is a fast way to lose platform-team trust.

BuildConfigs and ImageStreams are OpenShift-specific. Some teams build outside the cluster (CI pipelines, Tekton, GitHub Actions) and only deploy. Others use oc new-app and BuildConfigs. Know which pattern your team uses before copying old tutorials.

Storage classes and CSI drivers are cluster-specific. PVCs work the same; the storage class names and performance characteristics are not portable.

Documentation sprawl. You will read Kubernetes docs, OpenShift docs, and vendor Operator docs. The answer to “which API version?” depends on the cluster’s Kubernetes minor version — check oc version.

oc vs kubectl policy. Some orgs standardize on oc for audit wrappers or login integration. Some allow kubectl with a kubeconfig from oc login. Ask instead of assuming.

Licensing and support boundaries. OpenShift is a commercial product with subscription tiers. That affects upgrade cadence, support calls, and what the platform team will change for you. Not a technical skill gap, but a workflow difference.

None of these invalidate Kubernetes learning. They are the packaging tax on top of a shared core.

oc and kubectl — practical coexistence

oc embeds kubectl-compatible commands. Many teams alias or use either:

oc login https://api.cluster.example.com:6443
oc whoami
oc project my-team-dev

kubectl get pods
oc get pods

oc apply -f app/
kubectl apply -f app/

OpenShift-specific helpers worth knowing:

oc status
oc get route
oc logs -f deployment/api
oc rsh deployment/api
oc set env deployment/api LOG_LEVEL=debug
oc rollout restart deployment/api

oc explain works on Route and other OpenShift types:

oc explain route.spec.tls
kubectl explain route.spec.tls

I still reach for kubectl out of habit. I use oc when I need login, project context, or Route-aware output. Pick one primary tool per shell session; mixing contexts causes more confusion than mixing commands.

A modest learning path

If you already know Kubernetes basics, I would not restart from zero. I would layer OpenShift in this order:

  1. Get accessoc login, oc whoami, list projects, set default project.
  2. Deploy something boring — Deployment + Service, confirm Pods and endpoints.
  3. Expose it — create a Route, hit the hostname, confirm TLS behavior.
  4. Break it on purpose — wrong selector, failing readiness probe, non-root violation — and fix each with describe/logs/events.
  5. Read one SCC and one quota — understand why the platform blocked or throttled something.
  6. Find what Operators own — identify one CRD your stack depends on and read its status fields.

That path took me a few focused days, not months. The Kubernetes foundation was the part that actually saved time.

OpenShift vs “Kubernetes on cloud” — quick framing

Managed Kubernetes (EKS, GKE, AKS) gives you a control plane and leaves many add-ons as choices. OpenShift ships more decisions pre-made: router, registry integration, console, default monitoring, SCCs.

Neither model is universally better. OpenShift trades flexibility in component choice for integrated operations. Vanilla Kubernetes trades integration for pick-your-own ingress, policy engine, and observability stack.

Compare what your organization actually runs, not marketing slides.

Final thought

OpenShift vs Kubernetes is the wrong binary. Better question: what is upstream Kubernetes, and what is Red Hat platform on top?

Deployments, Services, probes, and RBAC are the shared language. Routes, SCCs, Projects, and Operators are the local dialect. Learn the dialect when you need it — usually at the edge, at admission time, and when talking to platform admins.

I still Google SCC error messages. I still check Endpoints before Routes when HTTP fails. The core skills from kubectl-centric learning were not wasted. They were the part that transferred on day one.