{"id":49170,"date":"2025-06-21T15:35:48","date_gmt":"2025-06-21T15:35:48","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=49170"},"modified":"2026-02-21T07:29:31","modified_gmt":"2026-02-21T07:29:31","slug":"what-is-imagestreams-step-by-step-tutorials","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/what-is-imagestreams-step-by-step-tutorials\/","title":{"rendered":"Openshift: What is ImageStreams &amp; Step by Step Tutorials"},"content":{"rendered":"\n<p>\ud83d\udc49 <strong>What ImageStream really gives you<\/strong><br>\ud83d\udc49 <strong>Why OpenShift uses it<\/strong><br>\ud83d\udc49 <strong>How it\u2019s better (in some cases) than just pulling a normal image<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83e\udde0 First, imagine two situations:<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd35 Situation 1: <strong>Without ImageStream<\/strong> (Normal Kubernetes)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You deploy an app with a direct Docker image URL: <code>image: nginx:latest<\/code><\/li>\n\n\n\n<li>Kubernetes will <strong>never track<\/strong> changes in <code>nginx:latest<\/code>.<\/li>\n\n\n\n<li>If someone updates the <code>latest<\/code> image, your running pods <strong>stay the same<\/strong> unless you <strong>manually trigger<\/strong> an update (like delete the pod, rollout new version).<\/li>\n\n\n\n<li>There\u2019s <strong>no history<\/strong>, <strong>no auto-notifications<\/strong>, and <strong>no internal record<\/strong> of what &#8220;latest&#8221; pointed to when you deployed it.<\/li>\n\n\n\n<li>No control, no tracking, no automatic trigger.<\/li>\n<\/ul>\n\n\n\n<p>\u2705 Simple but very <strong>dumb<\/strong> way.<br>\u2705 Works when you fully control your CI\/CD pipeline.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udfe0 Situation 2: <strong>With OpenShift ImageStream<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Instead of pulling images directly, OpenShift tracks them through an ImageStream.<\/li>\n\n\n\n<li>When <code>nginx:latest<\/code> is updated <strong>on DockerHub<\/strong>, OpenShift <strong>automatically<\/strong> imports the new image metadata into ImageStream.<\/li>\n\n\n\n<li>You can:\n<ul class=\"wp-block-list\">\n<li><strong>Automatically redeploy<\/strong> apps when the image changes (without touching anything).<\/li>\n\n\n\n<li><strong>View history<\/strong> of all previous images that ever existed for that tag.<\/li>\n\n\n\n<li><strong>Tag images manually<\/strong> between environments (example: promote dev image \u2192 staging \u2192 prod by just changing a tag).<\/li>\n\n\n\n<li><strong>Control access<\/strong> to images inside OpenShift&#8217;s internal registry.<\/li>\n\n\n\n<li><strong>Pin exact images<\/strong> by digest to avoid accidental breakage.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>\u2705 Smart, controlled, observable, CI\/CD friendly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\u2728 So, in simple words:<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Normal Images (Kubernetes)<\/th><th>ImageStream (OpenShift)<\/th><\/tr><\/thead><tbody><tr><td>Pull and forget. No update tracking.<\/td><td>Tracks external and internal images in OpenShift.<\/td><\/tr><tr><td>No automatic redeploy when images change.<\/td><td>Can auto-trigger deployment when new image arrives.<\/td><\/tr><tr><td>No history of what version you pulled.<\/td><td>Maintains history of images for every tag.<\/td><\/tr><tr><td>Cannot promote images safely between environments.<\/td><td>Can tag\/promote images easily between dev \u2192 staging \u2192 prod.<\/td><\/tr><tr><td>No OpenShift access control for images.<\/td><td>Full OpenShift permission control for pull, tag, promote.<\/td><\/tr><tr><td>You manage everything outside cluster.<\/td><td>OpenShift manages image lifecycle inside cluster.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\udcf8 Example to make it even more practical<\/h1>\n\n\n\n<p>\u2705 <strong>Without ImageStream:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You push a new app build to DockerHub (<code>myapp:latest<\/code> updated).<\/li>\n\n\n\n<li>Your Kubernetes app <strong>still uses old container<\/strong> unless you manually trigger a deployment.<\/li>\n<\/ul>\n\n\n\n<p>\u2705 <strong>With ImageStream:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You push a new app build to DockerHub (<code>myapp:latest<\/code> updated).<\/li>\n\n\n\n<li>OpenShift <strong>automatically imports<\/strong> the new version into ImageStream.<\/li>\n\n\n\n<li>If a Deployment (with image trigger) is linked to that ImageStream:\n<ul class=\"wp-block-list\">\n<li>\u2794 <strong>OpenShift triggers redeployment automatically!<\/strong><\/li>\n\n\n\n<li>\u2794 Zero manual work.<\/li>\n\n\n\n<li>\u2794 New app version is live safely.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">Final Summary (Easy Language)<\/h1>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Normal images<\/strong> are like pulling files from the internet manually every time.<br><strong>ImageStreams<\/strong> are like having a smart librarian inside your OpenShift cluster who:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tracks when your images change.<\/li>\n\n\n\n<li>Keeps history of all versions.<\/li>\n\n\n\n<li>Alerts your applications and triggers updates.<\/li>\n\n\n\n<li>Controls who can pull what.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">Practical Advantages of ImageStreams<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Benefit<\/th><th>Why it matters<\/th><\/tr><\/thead><tbody><tr><td>Auto-updates<\/td><td>No need to manually redeploy when a new version is available.<\/td><\/tr><tr><td>History tracking<\/td><td>Rollback to any old image version easily.<\/td><\/tr><tr><td>Environment promotion<\/td><td>Promote builds from Dev \u2192 QA \u2192 Prod by tagging.<\/td><\/tr><tr><td>Pull secrets and control<\/td><td>Manage access securely for private registries.<\/td><\/tr><tr><td>Cluster registry integration<\/td><td>OpenShift can manage images internally without external dependency.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">Real Example<\/h1>\n\n\n\n<p>If you\u2019re running CI\/CD pipelines in OpenShift (Jenkins, Tekton, GitOps ArgoCD):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Build finishes \u2794 ImageStream updated \u2794 New Deployment triggered \u2794 App upgraded automatically.<\/li>\n<\/ul>\n\n\n\n<p>\u2705 No downtime.<br>\u2705 No manual intervention.<br>\u2705 Full visibility on which image you are using.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">Very Short Answer<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>If you want<\/th><th>Use<\/th><\/tr><\/thead><tbody><tr><td>Simple &#8220;set and forget&#8221; deploys<\/td><td>Normal Kubernetes image reference.<\/td><\/tr><tr><td>Smart, auto-tracked, secure, history-managed image lifecycle<\/td><td>OpenShift ImageStreams.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83e\udde0 What Are We Building?<\/h1>\n\n\n\n<p>You have three projects (namespaces):<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Environment<\/th><th>OpenShift Project<\/th><\/tr><\/thead><tbody><tr><td>Development (Dev)<\/td><td><code>app-dev<\/code><\/td><\/tr><tr><td>Staging<\/td><td><code>app-staging<\/code><\/td><\/tr><tr><td>Production<\/td><td><code>app-prod<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>We will:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Build or Import an Image into <code>app-dev<\/code><\/li>\n\n\n\n<li>Promote (copy) the image to <code>app-staging<\/code><\/li>\n\n\n\n<li>Finally promote it to <code>app-prod<\/code><\/li>\n\n\n\n<li>All using <strong>ImageStreams<\/strong> without rebuilding the app again<\/li>\n<\/ul>\n\n\n\n<p>\u2705 No re-building needed!<br>\u2705 Full traceability!<br>\u2705 Full security control!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\udee0 Prerequisites:<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>OpenShift CLI (<code>oc<\/code>) installed<\/li>\n\n\n\n<li>Access to an OpenShift 4.x+ cluster<\/li>\n\n\n\n<li><code>admin<\/code> or sufficient permissions on projects<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\ude80 Step-by-Step Tutorial<\/h1>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">1\ufe0f\u20e3 Create Three OpenShift Projects<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc <span class=\"hljs-keyword\">new<\/span>-project app-dev\noc <span class=\"hljs-keyword\">new<\/span>-project app-staging\noc <span class=\"hljs-keyword\">new<\/span>-project app-prod\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">2\ufe0f\u20e3 Create ImageStreams in Each Project<\/h2>\n\n\n\n<p>Create an <strong>ImageStream<\/strong> named <code>myapp<\/code> in each project.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># myapp-imagestream.yaml<\/span>\napiVersion: image.openshift.io\/v1\nkind: ImageStream\nmetadata:\n  name: myapp\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Apply in all three projects:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">project<\/span> <span class=\"hljs-selector-tag\">app-dev<\/span>\n<span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">apply<\/span> <span class=\"hljs-selector-tag\">-f<\/span> <span class=\"hljs-selector-tag\">myapp-imagestream<\/span><span class=\"hljs-selector-class\">.yaml<\/span>\n\n<span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">project<\/span> <span class=\"hljs-selector-tag\">app-staging<\/span>\n<span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">apply<\/span> <span class=\"hljs-selector-tag\">-f<\/span> <span class=\"hljs-selector-tag\">myapp-imagestream<\/span><span class=\"hljs-selector-class\">.yaml<\/span>\n\n<span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">project<\/span> <span class=\"hljs-selector-tag\">app-prod<\/span>\n<span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">apply<\/span> <span class=\"hljs-selector-tag\">-f<\/span> <span class=\"hljs-selector-tag\">myapp-imagestream<\/span><span class=\"hljs-selector-class\">.yaml<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>\u2705 Now, each project has an empty <code>myapp<\/code> ImageStream.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">3\ufe0f\u20e3 Import or Build Image into Dev (First Deployment)<\/h2>\n\n\n\n<p>For simplicity, let&#8217;s just import a public image (you could also BuildConfig if needed).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc project app-dev\n\noc <span class=\"hljs-keyword\">import<\/span>-image myapp:latest --<span class=\"hljs-keyword\">from<\/span>=nginx:latest --confirm\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>\u2705 Now, the <code>app-dev<\/code> project <code>myapp:latest<\/code> points to the latest nginx image!<\/p>\n\n\n\n<p>You can check:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">oc describe is myapp\n<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">4\ufe0f\u20e3 Create Deployment that Uses ImageStream (optional but recommended)<\/h2>\n\n\n\n<p>In Dev, create a Deployment that uses the ImageStream:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># myapp-deployment.yaml<\/span>\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: myapp-deployment\nspec:\n  replicas: <span class=\"hljs-number\">1<\/span>\n  selector:\n    matchLabels:\n      app: myapp\n  template:\n    metadata:\n      labels:\n        app: myapp\n    spec:\n      containers:\n      - name: myapp\n        image: image-registry.openshift-image-registry.svc:<span class=\"hljs-number\">5000<\/span>\/app-dev\/myapp:latest\n        ports:\n        - containerPort: <span class=\"hljs-number\">80<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Apply:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">oc<\/span> <span class=\"hljs-selector-tag\">apply<\/span> <span class=\"hljs-selector-tag\">-f<\/span> <span class=\"hljs-selector-tag\">myapp-deployment<\/span><span class=\"hljs-selector-class\">.yaml<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>\u2705 Dev environment is now running your image!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">5\ufe0f\u20e3 <strong>Promote Image from Dev \u2794 Staging<\/strong><\/h2>\n\n\n\n<p>Now the magic: <strong>Tagging<\/strong> from one project&#8217;s ImageStream to another project\u2019s ImageStream.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">oc tag app-dev\/myapp:latest app-staging\/myapp:latest\n<\/code><\/span><\/pre>\n\n\n<p>\u2705 This command <strong>copies the image reference<\/strong> into <code>app-staging<\/code>&#8216;s ImageStream.<\/p>\n\n\n\n<p><strong>Important:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No rebuild.<\/li>\n\n\n\n<li>No re-pulling from DockerHub.<\/li>\n\n\n\n<li>Full OpenShift internal secured movement.<\/li>\n<\/ul>\n\n\n\n<p>You can verify:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">oc project app-staging\noc describe is myapp\n<\/code><\/span><\/pre>\n\n\n<p>You will see that it points to the image from Dev.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">6\ufe0f\u20e3 Deploy in Staging<\/h2>\n\n\n\n<p>Create a Deployment in <code>app-staging<\/code>, pointing to:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">image<\/span>: image-registry.openshift-image-registry.svc:5000\/app-staging\/myapp:latest\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now staging runs the exact same image!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">7\ufe0f\u20e3 Promote Image from Staging \u2794 Production<\/h2>\n\n\n\n<p>Same simple command:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">oc tag app-staging\/myapp:latest app-prod\/myapp:latest\n<\/code><\/span><\/pre>\n\n\n<p>\u2705 Now your Production ImageStream is updated with the exact tested image.<\/p>\n\n\n\n<p>Create a Deployment in <code>app-prod<\/code> using:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">image<\/span>: image-registry.openshift-image-registry.svc:5000\/app-prod\/myapp:latest\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>\u2705 Production environment runs only after successful promotion from staging!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\udd25 Auto-Promotion Pipeline Idea (Optional)<\/h1>\n\n\n\n<p>You can fully automate this using <strong>Tekton Pipelines<\/strong>, <strong>Jenkins<\/strong>, <strong>GitHub Actions<\/strong>, or <strong>GitOps (ArgoCD)<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>After tests pass in Dev \u2794 automatically tag to Staging.<\/li>\n\n\n\n<li>After staging approval \u2794 automatically tag to Production.<\/li>\n<\/ul>\n\n\n\n<p>\u2705 Fully secure.<br>\u2705 CI\/CD friendly.<br>\u2705 Traceable images across all stages.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83c\udfc6 Final Architecture Diagram<\/h1>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">DockerHub or Build --&gt; app-dev\/myapp:latest\n                              |\n                       oc tag\n                              \u2193\n                     app-staging\/myapp:latest\n                              |\n                       oc tag\n                              \u2193\n                      app-prod\/myapp:latest\n<\/code><\/span><\/pre>\n\n\n<p>Each environment runs exactly the promoted image, NOT a rebuilt one. \ud83d\ude80<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\udccb Quick Commands Cheat Sheet<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Command<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>oc import-image myapp:latest --from=&lt;external-image&gt; --confirm<\/code><\/td><td>Import image from external registry<\/td><\/tr><tr><td><code>oc tag app-dev\/myapp:latest app-staging\/myapp:latest<\/code><\/td><td>Promote image from Dev to Staging<\/td><\/tr><tr><td><code>oc tag app-staging\/myapp:latest app-prod\/myapp:latest<\/code><\/td><td>Promote image from Staging to Prod<\/td><\/tr><tr><td><code>oc describe is myapp<\/code><\/td><td>View ImageStream details<\/td><\/tr><tr><td><code>oc get istag<\/code><\/td><td>List image tags<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83c\udfaf Real-World Benefits of This Method<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Benefit<\/th><th>Why It Matters<\/th><\/tr><\/thead><tbody><tr><td>No Rebuilding<\/td><td>Promotes the same built image \u2014 ensures consistency<\/td><\/tr><tr><td>Fast and Secure<\/td><td>No need to pull externally again \u2014 internal registry handles it<\/td><\/tr><tr><td>Full Traceability<\/td><td>You can always check where an image came from<\/td><\/tr><tr><td>Automation Friendly<\/td><td>Easily integrate with pipelines and GitOps<\/td><\/tr><tr><td>Safe Rollbacks<\/td><td>Previous tags\/history available if you want to revert<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\ude80 Conclusion<\/h1>\n\n\n\n<p>\u2705 <strong>ImageStreams<\/strong> make Dev \u2794 Staging \u2794 Prod promotions super clean and safe!<br>\u2705 <strong>Tagging<\/strong> avoids risks of &#8220;it works on my machine but fails on prod&#8221; issues.<br>\u2705 <strong>Production-ready<\/strong> method in OpenShift CI\/CD workflows!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h1 class=\"wp-block-heading\">Lab with Existing <\/h1>\n\n\n\n<p>Here&#8217;s a <strong>short and clear tutorial<\/strong> on how to <strong>check and deploy ImageStreams in OpenShift<\/strong>, using the example where you added <code>httpd<\/code> via the <strong>Developer Catalog UI<\/strong> and then used <code>oc<\/code> commands to deploy and expose it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"481\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36-1024x481.png\" alt=\"\" class=\"wp-image-49758\" srcset=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36-1024x481.png 1024w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36-300x141.png 300w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36-768x360.png 768w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36-1536x721.png 1536w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/04\/image-36.png 1905w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">Deploy ImageStreams in OpenShift (ARO)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 1: Add ImageStream via Developer Catalog (UI)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>In OpenShift Web Console:\n<ul class=\"wp-block-list\">\n<li>Go to <strong>Developer \u2192 +Add \u2192 Container Image<\/strong><\/li>\n\n\n\n<li>Search for <strong><code>httpd<\/code><\/strong> and add it<\/li>\n\n\n\n<li>This will create an <strong>ImageStream<\/strong> but <strong>not<\/strong> a running application<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 2: Check What Was Created<\/h3>\n\n\n\n<p>Use the CLI:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc <span class=\"hljs-keyword\">get<\/span> all -n test2\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You\u2019ll see only:<\/p>\n<\/blockquote>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">imagestream.image.openshift.io\/httpd\n<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 3: Deploy an App Using the ImageStream<\/h3>\n\n\n\n<p>To deploy a pod from the ImageStream you created:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc <span class=\"hljs-keyword\">new<\/span>-app httpd -n test2\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This automatically creates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DeploymentConfig<\/li>\n\n\n\n<li>Pod<\/li>\n\n\n\n<li>Service<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 4: Verify Deployment<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc <span class=\"hljs-keyword\">get<\/span> all -n test2\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You\u2019ll now see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>deploymentconfig.apps.openshift.io\/httpd<\/code><\/li>\n\n\n\n<li><code>pod\/httpd-xxxxx<\/code><\/li>\n\n\n\n<li><code>svc\/httpd<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 5: Expose the Service to Create a Public Route<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">oc expose svc\/httpd -n test2\n<\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 6: Get the Route URL<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">oc <span class=\"hljs-keyword\">get<\/span> route httpd -n test2\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Open the displayed URL in a browser to see the <code>httpd<\/code> default page.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddea Command History You Used<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-number\">512<\/span>  oc get all -n test2\n<span class=\"hljs-number\">513<\/span>  oc <span class=\"hljs-keyword\">new<\/span>-app jenkins              <span class=\"hljs-comment\"># (unrelated, probably test)<\/span>\n<span class=\"hljs-number\">514<\/span>  oc <span class=\"hljs-keyword\">new<\/span>-app httpd -n test2       <span class=\"hljs-comment\"># \u2705 deploy httpd app<\/span>\n<span class=\"hljs-number\">515<\/span>  oc get all -n test2\n<span class=\"hljs-number\">516<\/span>  oc expose svc\/httpd -n test2    <span class=\"hljs-comment\"># \u2705 expose the service<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Summary<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Task<\/th><th>Command<\/th><\/tr><\/thead><tbody><tr><td>Check what exists<\/td><td><code>oc get all -n test2<\/code><\/td><\/tr><tr><td>Deploy app from ImageStream<\/td><td><code>oc new-app httpd -n test2<\/code><\/td><\/tr><tr><td>Expose service<\/td><td><code>oc expose svc\/httpd -n test2<\/code><\/td><\/tr><tr><td>Access app<\/td><td><code>oc get route httpd -n test2<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49 What ImageStream really gives you\ud83d\udc49 Why OpenShift uses it\ud83d\udc49 How it\u2019s better (in some cases) than just pulling a normal image \ud83e\udde0 First, imagine two situations: \ud83d\udd35 Situation 1:&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[5153],"tags":[],"class_list":["post-49170","post","type-post","status-publish","format-standard","hentry","category-openshift"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49170","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=49170"}],"version-history":[{"count":4,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49170\/revisions"}],"predecessor-version":[{"id":59007,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49170\/revisions\/59007"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=49170"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=49170"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=49170"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}