How to Deploy an Application in OpenShift Container Platform: Imperative and Declarative Methods – EX280 Series 2 (Lesson 4)

deploy app in openshift

Last updated: October 2026

In this lesson, you will learn how to deploy application in OpenShift cluster, and at the end of this lesson, you will also practice the recent ex280 exam practice questions relating to this topic to reinforce your understanding and prepare for the Red Hat Certified OpenShift exam.

Understanding The Subject Matter

Deploying an application on Red Hat OpenShift Container Platform involves more than starting a container. You must create the necessary Kubernetes resources, ensure the application is running, and configure a way for users or other applications to access it.

OpenShift provides different ways to accomplish these tasks. You can use imperative commands through the OpenShift CLI (oc), or you can define the desired configuration in YAML files and apply it declaratively.

In this guide, we will use both methods to deploy a simple web application. You will learn how to create an OpenShift project, deploy an application, understand the resources OpenShift creates, andverify that the application is running.

What You Will Learn

By the end of this tutorial, you should be able to:

  • Create and switch between OpenShift projects.
  • Explain the difference between a Pod, Deployment, ReplicaSet, Service, and Route.
  • Deploy an application using imperative commands.
  • Deploy the same application using declarative YAML manifests.
  • Verify application health and troubleshoot common deployment problems.
  • Understand when to use imperative and declarative approaches.

Prerequisites

Before proceeding, ensure you have:

  • Access to a running OpenShift Container Platform cluster.
  • The OpenShift CLI (oc) installed on your workstation.
  • Credentials that allow you to log in and create resources in a project.
  • Network access to pull the container image used in this tutorial.
  • An OpenShift cluster with a configured router if you want to expose the application externally.

The commands in this guide are run from a workstation, not directly on an OpenShift node. You do not need to SSH into a cluster node to deploy or test the application.

The examples use the nginxinc/nginx-unprivileged:stable container image, which listens on port 8080 and is designed to run without root privileges. The examples assume that this image is available from your cluster and that the tag has not changed unexpectedly. For production, pin and validate an appropriate image version or digest.

Note: Command outputs are examples. Your cluster IP addresses, Pod names, route hostnames, and resource ages will be different.


1. Understanding Application Deployment in OpenShift

An application deployed on OpenShift commonly consists of several resources that work together.

ResourcePurpose
ProjectGroups and isolates application resources.
DeploymentDescribes the desired application state, including the container image and number of replicas.
ReplicaSetMaintains the required number of Pod replicas for a Deployment.
PodRuns one or more containers.
ServiceProvides a stable network endpoint for accessing application Pods inside the cluster.
RouteExposes a Service through a hostname that can be reached through the OpenShift router. Some environment uses Ingress or Istio to expose applications

A Deployment does not run the container directly. It manages ReplicaSets, which maintain the required Pods. The Pods run the application’s containers.

A Service provides a stable endpoint even when individual Pods are replaced and receive different IP addresses. A Route provides a way to reach the Service from outside the cluster, subject to DNS, routing, and network configuration.

The general relationship is:

Deployment → ReplicaSet → Pods ← Service ← Route

The Service selects the appropriate Pods using labels. The Route directs incoming requests to the Service.

What is the difference between a Deployment and a DeploymentConfig?

A Deployment is a Kubernetes resource that OpenShift supports and recommends for most new application workloads.

A DeploymentConfig is an OpenShift-specific resource that uses a different deployment mechanism, including ReplicationControllers. It remains relevant to existing environments and workloads that depend on its specific features, but it should not be introduced as the default method for every new application.

In this tutorial, we will use a standard Kubernetes Deployment.

Deploy Application in OpenShift  Container Platform

ACTION TIME

2. Log in to the OpenShift Cluster

Before creating resources, authenticate to the cluster.

If you are using the OpenShift web console, you can usually copy a login command from your user menu. Use the command and token supplied by your cluster rather than copying credentials from an example.

[tekneed@osl ~]$ oc login https://api.ocp.example.com:6443

Example output:

Authentication required for https://api.ocp.example.com:6443
Username: learner
Password:
Login successful.

You have access to projects on this server.

The exact output depends on your authentication method and permissions.

Confirm that you are connected to the expected cluster:

[tekneed@osl ~]$ oc whoami

Example output:

learner

You can also inspect the active server:

[tekneed@osl ~]$ oc whoami --show-server

Example output:

https://api.ocp.example.com:6443

Always verify that you are connected to the correct cluster before creating or deleting resources.

3. Create an OpenShift Project

An OpenShift project is an environment for organizing application resources. It is closely related to a Kubernetes namespace, with additional OpenShift-specific capabilities.

For this exercise, create a project called app-project.

[tekneed@osl ~]$ oc new-project app-project

Example output:

Now using project "app-project" on server
"https://api.ocp.example.com:6443".

You can add applications to this project with the 'new-app' command.

Confirm the active project:

[tekneed@osl ~]$ oc project

Example output:

Using project "app-project" on server
"https://api.ocp.example.com:6443".

You can list the projects available to your account using:

[tekneed@osl ~]$ oc projects

Example output:

You have access to the following projects and can switch between them:

  default
  app-project
  openshift
  openshift-ingress
  ...

Your output will depend on your account permissions.

Important: Most commands in this tutorial operate on the current project unless you explicitly specify another namespace. Confirm the active project before creating resources.


4. Deploy an Application Using the Imperative Approach

An imperative approach uses commands to tell OpenShift what action to perform.

For example:

[tekneed@osl ~]$ oc create deployment nginx-app \
  --image=nginxinc/nginx-unprivileged:stable \
  --port=8080

This command asks OpenShift to create a Deployment named nginx-app using the specified container image.

Imperative commands are convenient for learning, quick experiments, and one-off operations. They can also be used in scripts, although scripts need appropriate error handling and validation.

4.1 Create the Deployment

Run the command:

[tekneed@osl ~]$ oc create deployment nginx-app \
  --image=nginxinc/nginx-unprivileged:stable \
  --port=8080

Example output:

deployment.apps/nginx-app created

You have created the Deployment, but that does not necessarily mean the application is ready to serve traffic. OpenShift must still schedule the Pod, pull the image if necessary, and start the container.

4.2 Check the Deployment

[tekneed@osl ~]$ oc get deployments

Example output:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app   0/1     1            0           10s

The Deployment may initially show 0/1 because the Pod is still starting. Wait briefly and check again:

[tekneed@osl ~]$ oc get deployments

Example output once the application is ready:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app   1/1     1            1           45s

Here is what the columns mean:

  • READY: The number of ready replicas compared with the desired number.
  • UP-TO-DATE: The number of replicas updated to the Deployment’s current specification.
  • AVAILABLE: The number of replicas available to serve requests.
  • AGE: How long the resource has existed.

The output may take longer to reach 1/1 if image downloads are slow or the cluster has scheduling problems.

4.3 Check the Pod

A Deployment creates and manages Pods indirectly through a ReplicaSet.

[tekneed@osl ~]$ oc get pods

Example output:

NAME                         READY   STATUS    RESTARTS   AGE
nginx-app-6b7f8c9d7f-abcde   1/1     Running   0          45s

Notice that the Pod is not named simply nginx-app. The suffix is generated as part of the naming structure used by the Deployment and ReplicaSet.

The Deployment name remains stable even if a Pod is deleted and replaced.

To display additional details, including the node on which the Pod is scheduled, run:

[tekneed@osl ~]$ oc get pods -o wide

Example output:

NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE
nginx-app-6b7f8c9d7f-abcde   1/1     Running   0          45s   10.128.2.15   worker-01

The Pod IP is an internal cluster address. It is not normally the address you give external users to access the application.

4.4 Understand the ReplicaSet

Check the ReplicaSet created by the Deployment:

[tekneed@osl ~]$ oc get replicasets

Example output:

NAME                   DESIRED   CURRENT   READY   AGE
nginx-app-6b7f8c9d7f    1         1         1       45s

The ReplicaSet helps maintain the required number of Pods. The Deployment manages the ReplicaSet and supports operations such as rolling updates and rollbacks.

This is one reason a Deployment is generally preferable to creating a standalone Pod for a long-running application.

4.5 Inspect the Deployment

To examine the Deployment and its recent events, run:

[tekneed@osl ~]$ oc describe deployment nginx-app

The output contains information such as the container image, desired replicas, available replicas, labels, and events.

You can also inspect a Pod:

[tekneed@osl ~]$ oc describe pod nginx-app-6b7f8c9d7f-abcde

Replace the example Pod name with the one returned by oc get pods.

If the Pod does not start, the Events section often helps explain why. For example, it may report an image pull failure, an insufficient resource error, or a security-related issue.


5. Create a Service to Access the Application

At this point, the application is running in a Pod, but you have not yet created a stable network endpoint for it.

A Service provides a stable endpoint and forwards traffic to the matching Pods.

Create a Service for the Deployment:

[tekneed@osl ~]$ oc expose deployment nginx-app --port=8080

Example output:

service/nginx-app exposed

The Service uses port 8080 and selects the Pods associated with the Deployment. The container image used in this exercise listens on port 8080.

Inspect the Service:

[tekneed@osl ~]$ oc get services

Example output:

NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
nginx-app   ClusterIP   172.30.45.120   <none>        8080/TCP   10s

A ClusterIP Service is accessible from within the cluster. The EXTERNAL-IP field being <none> is expected for this Service type.

You can inspect the Service’s endpoints as well:

[tekneed@osl ~]$ oc get endpoints nginx-app

Depending on the OpenShift and Kubernetes version, you may see the older Endpoints resource or use EndpointSlices:

[tekneed@osl ~]$ oc get endpointslices \
  -l kubernetes.io/service-name=nginx-app

The endpoint information should identify the backend Pod addresses and port when the Pod is ready and selected by the Service.

If no backend endpoints appear, check that the Pod is ready and that the Service selector matches the Pod labels.


6. Expose the Application Through an OpenShift Route

A Service is normally used for internal cluster access. To make this web application reachable through the OpenShift router, create a Route.

Run:

[tekneed@osl ~]$ oc expose service nginx-app

Example output:

route.route.openshift.io/nginx-app exposed

Now retrieve the route hostname:

[tekneed@osl ~]$ oc get routes

Example output:

NAME        HOST/PORT                                  PATH   SERVICES    PORT
nginx-app   nginx-app-app-project.apps.ocp.example.com        nginx-app   8080

The hostname shown above is an example. Your cluster will generate a hostname based on its configured application domain unless you specify a custom host.

The route depends on the OpenShift router, DNS resolution, and any relevant network or firewall rules being configured correctly.

6.1 Test the Application

You can test the route from your workstation using curl:

[tekneed@osl ~]$ curl -I \
  http://nginx-app-app-project.apps.ocp.example.com

Example output:

HTTP/1.1 200 OK
Content-Type: text/html
...

Use the actual hostname returned by oc get routes.

You can also open the URL in a browser. If the route is configured for HTTPS, use the HTTPS URL and ensure the certificate is trusted.

A successful HTTP response confirms that the request reached a web server through the route. If the request fails, check the route hostname, DNS, router configuration, Service, Pod readiness, and the application’s listening port.

At this stage, you have deployed the application imperatively and created the resources required to access it.


7. Scale the Application

One advantage of using a Deployment is that you can change the desired number of application replicas without manually creating individual Pods.

Scale the application to two replicas:

[tekneed@osl ~]$ oc scale deployment nginx-app --replicas=2

Example output:

deployment.apps/nginx-app scaled

Check the Deployment:

[tekneed@osl ~]$ oc get deployments

Example output:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app   2/2     2            2           5m

Check the Pods:

[tekneed@osl ~]$ oc get pods

Example output:

NAME                         READY   STATUS    RESTARTS   AGE
nginx-app-6b7f8c9d7f-abcde   1/1     Running   0          5m
nginx-app-6b7f8c9d7f-fghij   1/1     Running   0          20s

Your actual Pod names will differ.

The Service continues to provide a stable endpoint and can distribute requests across the ready Pods selected by its selector. The Route does not need to be recreated just because the Deployment has more replicas.

Scaling replicas improves capacity and can help with availability, but two replicas alone do not guarantee high availability. Node placement, resource availability, application behavior, storage, and other design considerations also matter.


8. Deploy an Application Using the Declarative Approach

The imperative approach uses commands to perform actions directly. The declarative approach describes the desired state in configuration files, commonly YAML, and submits that configuration to OpenShift.

For example, instead of issuing separate commands to create a Deployment, Service, and Route, you can define those resources in YAML and apply them.

This is useful because the configuration can be saved in version control, reviewed before changes are made, reused in other environments, and applied repeatedly.

If you want a deeper explanation of managing OpenShift resources with YAML, see the companion guide:

Create and Manage OpenShift Resources Using the Declarative Approach:
https://tekneed.com/create-manage-openshift-resources-by-declarative/

For this exercise, we will deploy the same application using declarative manifests.

8.1 Create a YAML Manifest

Create a file called nginx-app.yaml on your workstation.

For example:

[tekneed@osl ~]$ vi nginx-app.yaml

Add the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
  labels:
    app: nginx-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-app
  template:
    metadata:
      labels:
        app: nginx-app
    spec:
      containers:
        - name: nginx
          image: nginxinc/nginx-unprivileged:stable
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-app
  labels:
    app: nginx-app
spec:
  selector:
    app: nginx-app
  ports:
    - name: http
      port: 8080
      targetPort: 8080
  type: ClusterIP
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: nginx-app
spec:
  to:
    kind: Service
    name: nginx-app
  port:
    targetPort: http

Save the file.

The manifest defines three resources:

  1. Deployment: Runs two replicas of the NGINX application.
  2. Service: Provides a stable internal endpoint on port 8080.
  3. Route: Exposes the Service through the OpenShift router.

The --- separators divide multiple YAML documents within the same file.

The selector.matchLabels field in the Deployment must match the labels assigned to the Pod template. The Service selector must also match the Pod labels. These relationships allow the Deployment to manage its Pods and the Service to direct traffic to them.

The Route’s targetPort refers to the named Service port http.

Because the file contains an OpenShift-specific Route resource, it is intended for OpenShift Container Platform rather than a standard Kubernetes cluster without the OpenShift Route API.

8.2 Review the Manifest Before Applying It

Before creating resources, you can ask OpenShift to validate the configuration on the server without persisting it.

[tekneed@osl ~]$ oc apply --dry-run=server -f nginx-app.yaml

The output should indicate that the resources would be created or configured, without saving them to the cluster.

This check requires access to the relevant API resources and permissions. It does not guarantee that the image will successfully start or that the Route will be reachable.

8.3 Apply the YAML File

Run:

[tekneed@osl ~]$ oc apply -f nginx-app.yaml

Example output for a first deployment:

deployment.apps/nginx-app created
service/nginx-app created
route.route.openshift.io/nginx-app created

If the resources already exist and the manifest changes their configuration, the output may say configured instead of created. If there are no changes, it may say unchanged.

This is an important difference between creating resources once and applying a declarative configuration repeatedly.

8.4 Verify the Resources

Check the Deployment:

[tekneed@osl ~]$ oc get deployment nginx-app

Example output:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app   2/2     2            2           60s

Check the Pods:

[tekneed@osl ~]$ oc get pods -l app=nginx-app

Example output:

NAME                         READY   STATUS    RESTARTS   AGE
nginx-app-6b7f8c9d7f-abcde   1/1     Running   0          60s
nginx-app-6b7f8c9d7f-fghij   1/1     Running   0          60s

Check the Service and Route:

[tekneed@osl ~]$ oc get service,route

Example output:

NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/nginx-app   ClusterIP   172.30.45.120   <none>        8080/TCP   60s

NAME                                HOST/PORT
route.route.openshift.io/nginx-app  nginx-app-app-project.apps.ocp.example.com

Test the application using the hostname shown by oc get routes:

[tekneed@osl ~]$ curl -I \
  http://nginx-app-app-project.apps.ocp.example.com

Example output:

HTTP/1.1 200 OK
Content-Type: text/html
...

The example output assumes the application has become ready and the Route is reachable.

8.5 Update the Application Declaratively

Suppose you want to scale the application from two replicas to three.

Open nginx-app.yaml and change:

spec:
  replicas: 2

to:

spec:
  replicas: 3

Then apply the file again:

[tekneed@osl ~]$ oc apply -f nginx-app.yaml

Example output:

deployment.apps/nginx-app configured
service/nginx-app unchanged
route.route.openshift.io/nginx-app unchanged

Check the Deployment:

[tekneed@osl ~]$ oc get deployment nginx-app

Example output after the rollout completes:

NAME        READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app   3/3     3            3           4m

OpenShift reconciles the Deployment toward the desired replica count in the manifest.

This illustrates the main benefit of declarative management: you update the configuration describing the intended state and apply it, rather than having to remember every individual command required to reach that state.

Important: oc apply is not a guarantee that every live field will be reset to the file’s values. The result depends on the resource, field ownership, and the type of change. Avoid manually changing the same fields in multiple management systems without a clear ownership strategy.


9. Imperative vs Declarative: What Is the Difference?

Both approaches can create and manage OpenShift resources. The main difference is how you express the desired result.

AreaImperativeDeclarative
How it worksCommands specify actions to perform.YAML files describe the desired resource state.
Exampleoc scale deployment nginx-app --replicas=3Change replicas in YAML and run oc apply -f nginx-app.yaml.
LearningConvenient for understanding individual commands and experimenting.Helps learners understand resource specifications and configuration.
ReuseCommands can be saved in scripts.Manifests can be versioned and reused across environments.
ReviewChanges are expressed as commands.Changes can be reviewed in the configuration file before applying.
Repeated useSome create commands fail if the resource already exists.oc apply can create resources or update supported configuration fields.
Common useTroubleshooting, quick operations, and one-off changes.Repeatable deployments, Git-based workflows, and configuration management.

For example, these two operations both aim to scale the same Deployment to three replicas.

Imperative:

[tekneed@osl ~]$ oc scale deployment nginx-app --replicas=3

Declarative:

spec:
  replicas: 3

After saving the updated manifest:

[tekneed@osl ~]$ oc apply -f nginx-app.yaml

In a declarative workflow, the YAML file should remain the source of truth. If you change the replica count with an imperative command but leave the file unchanged, a later application of the old manifest may return the Deployment to the replica count specified in that file.

Neither approach is automatically secure or production-ready. Both require appropriate access controls, image management, resource limits, monitoring, and change management.

10. Troubleshooting Common Deployment Problems

A command reporting that a resource was created does not necessarily mean that the application is ready. The following commands help identify common problems.

Problem 1: The Pod Is Stuck in Pending

Check the Pods:

[tekneed@osl ~]$ oc get pods

Inspect the affected Pod:

[tekneed@osl ~]$ oc describe pod <pod-name>

Look at the Events section for messages about insufficient CPU or memory, scheduling restrictions, unbound PersistentVolumeClaims, or other scheduling problems.

Replace <pod-name> with the actual Pod name.

Problem 2: The Pod Is in ImagePullBackOff

Inspect the Pod:

[tekneed@osl ~]$ oc describe pod <pod-name>

Possible causes include:

  • An incorrect image name or tag.
  • A registry that cannot be reached.
  • Registry authentication problems.
  • Network restrictions or certificate issues.

Check the exact event message before changing registry credentials or other settings.

Problem 3: The Container Is Restarting

Check the Pod:

[tekneed@osl ~]$ oc get pods

Inspect the logs:

[tekneed@osl ~]$ oc logs <pod-name>

If the container has restarted, retrieve the previous container’s logs when available:

[tekneed@osl ~]$ oc logs <pod-name> --previous

Also inspect the Pod events and termination reason. A restart alone does not tell you whether the cause is an application error, a failed health check, an out-of-memory termination, or another issue.

Problem 4: The Deployment Is Not Ready

Check its status:

[tekneed@osl ~]$ oc rollout status deployment/nginx-app

Inspect the Deployment:

[tekneed@osl ~]$ oc describe deployment nginx-app

Check the ReplicaSet and Pods to determine whether the desired number of replicas can be created and become ready.

Problem 5: The Route Exists but the Application Cannot Be Reached

Inspect the Route and Service:

[tekneed@osl ~]$ oc get route nginx-app
[tekneed@osl ~]$ oc describe route nginx-app
[tekneed@osl ~]$ oc describe service nginx-app

Check the Pod labels and readiness, Service selectors and endpoints, route hostname, DNS resolution, and router reachability. Also verify that the Service’s target port matches the port on which the application is listening.

A Route object can exist even when the application behind it is unhealthy or inaccessible.


11. Clean Up the Lab Resources

When you finish, you can delete the resources created by the declarative manifest:

[tekneed@osl ~]$ oc delete -f nginx-app.yaml

Example output:

deployment.apps "nginx-app" deleted
service "nginx-app" deleted
route.route.openshift.io "nginx-app" deleted

This removes the resources described in the file. It does not delete the project itself.

If you created additional resources outside the manifest, they may remain.

To delete the entire project and its namespaced resources, use:

[tekneed@osl ~]$ oc delete project app-project

Example output:

project.project.openshift.io "app-project" deleted

Caution: Deleting a project removes its namespaced resources, not just this sample application. Only delete it if you created it for this exercise and have confirmed that nothing else needs to remain.


Conclusion

Deploying an application on OpenShift involves creating the resources that run the application and provide network access to it. A Deployment manages the desired number of Pods, a Service provides a stable internal endpoint, and an OpenShift Route can expose the application through the cluster’s router.

The imperative approach is useful for learning, quick operations, and troubleshooting. The declarative approach is valuable when you want configuration that can be reviewed, stored in version control, reused, and applied consistently.

For further study, continue with the companion lesson on creating and managing OpenShift resources using the declarative approach. Understanding both methods will help you work more effectively with OpenShift in development, administration, and DevOps workflows.


Recent OpenShift EX280 Exam Practice Questions & Answers on How To Deploy Application in OpenShift Container Platform

Watch Video on How To Deploy Application in OpenShift Container Platform

Be the first to comment

Leave a Reply

Your email address will not be published.


*