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 buildYou'll get a client bundle plus a server build (commonly dist/server/index.js). Start it locally to confirm:
node dist/server/index.jsIf 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-stringThe 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
- 1Push to Git.
- 2https://dashboard.pandastack.io → New App → connect the repo. PandaStack detects the Dockerfile, builds with rootless BuildKit, deploys via Helm.
- 3Add the environment variables above under the app's Environment tab.
- 4Every push redeploys.
CLI equivalent:
npm install -g @pandastack/cli
panda login
panda deployYour 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.