Back to Blog
Tutorial9 min read2026-07-17

Deploying a Shopify Hydrogen Storefront Outside of Oxygen (2026)

Hydrogen is Shopify's React storefront framework, and it's built on Remix — which means you can run it as a normal Node server anywhere, not only on Oxygen. Here's how to containerize and deploy one on PandaStack.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Shopify Hydrogen (https://hydrogen.shopify.dev) is Shopify's opinionated React framework for building custom storefronts against the Storefront API. Shopify wants you to deploy it to Oxygen, their hosting layer — and Oxygen is genuinely good, tightly integrated, and free with a Shopify plan. So the first honest thing to say is: if Oxygen fits your needs, use Oxygen. This post is for the cases where it doesn't — you want your storefront next to the rest of your infrastructure, you need a Node runtime feature Oxygen's edge runtime doesn't expose, or you're consolidating everything onto one platform. I run PandaStack; here's how to run Hydrogen as a plain container.

Why this is even possible

Modern Hydrogen is built on Remix, and Remix apps compile to a standard Node server. That's the key insight: a Hydrogen app isn't locked to Oxygen's runtime — it's a Remix app that *can* target Oxygen, or Node, or any Remix-compatible adapter. If your project uses the Oxygen adapter, you swap in the Node adapter; the rest of your storefront code doesn't change.

Step 1: Target a Node server build

In your Hydrogen project, make sure the build produces a Node server. A Hydrogen/Remix vite.config.js typically looks like this once you're targeting Node:

import { defineConfig } from 'vite'
import { hydrogen } from '@shopify/hydrogen/vite'
import { oxygen } from '@shopify/mini-oxygen/vite' // remove if going pure Node
import { reactRouter } from '@react-router/dev/vite'

export default defineConfig({
  plugins: [hydrogen(), reactRouter()],
  ssr: { optimizeDeps: { include: [] } },
})

Then build:

npm run build

You'll get a client bundle plus a server build (commonly dist/server/index.js). Start it locally to confirm:

node dist/server/index.js

If it serves your storefront on localhost, it'll serve it in a container.

Step 2: Environment variables

Hydrogen needs your Storefront API credentials. Never commit these. You'll set them in PandaStack's encrypted env store:

PUBLIC_STOREFRONT_API_TOKEN=...
PRIVATE_STOREFRONT_API_TOKEN=...
PUBLIC_STORE_DOMAIN=your-store.myshopify.com
SESSION_SECRET=a-long-random-string

The PRIVATE_ token and SESSION_SECRET are server-only secrets — they stay on the server and never reach the browser.

Step 3: Dockerfile

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server/index.js"]

Adjust the server entry path to match your build output.

Step 4: Deploy

  1. 1Push to Git.
  2. 2https://dashboard.pandastack.io → New App → connect the repo. PandaStack detects the Dockerfile, builds with rootless BuildKit, deploys via Helm.
  3. 3Add the environment variables above under the app's Environment tab.
  4. 4Every push redeploys.

CLI equivalent:

npm install -g @pandastack/cli
panda login
panda deploy

Your storefront is live at https://your-store.pandastack.io. Add your real domain under Domains and SSL is automatic.

Step 5: Caching, which is the whole game for a storefront

Commerce sites live and die by cache hit rates. Hydrogen has a built-in caching API (cache options on storefront.query) that sets HTTP cache headers. Use it aggressively for product and collection data:

const { products } = await storefront.query(PRODUCTS_QUERY, {
  cache: storefront.CacheLong(),
})

Put Cloudflare (or any CDN) in front of your PandaStack app and those cache headers do real work — most storefront traffic never touches your origin. This matters more than which host you pick.

Step 6: Webhooks and background jobs

A real store needs to react to Shopify webhooks (orders, inventory, fulfillment). Add a Remix route to receive them — it's a normal POST handler with HMAC verification — and because your app has a stable HTTPS URL, you register that URL in Shopify's admin. Need a nightly job to sync inventory or email abandoned carts? A PandaStack cronjob container handles it without a always-on worker.

When to stay on Oxygen

Be honest with yourself:

  • Oxygen is free with Shopify, globally distributed at the edge, and zero-config for Hydrogen. If you have no reason to leave, don't.
  • Self-hosting on PandaStack wins when you want your storefront, custom APIs, databases, and cron jobs under one roof, one bill, and one RBAC model — or when you need Node runtime capabilities the edge runtime doesn't offer.

Honest tradeoffs

  • You take on the caching/CDN setup that Oxygen gives you for free. It's not hard, but it's yours now.
  • Node SSR isn't edge-distributed by default; pair it with a CDN for global speed.
  • PandaStack is newer than Oxygen for this specific use case — there's no Hydrogen-specific tooling, just standard container hosting. That's the point, but know it going in.

Wrap-up

Hydrogen is a Remix app underneath. Build it for Node, containerize it, deploy it to PandaStack, cache hard, and register your webhooks. Or use Oxygen — both are legitimate.

Docs: https://docs.pandastack.io. Start free: https://dashboard.pandastack.io.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also