Deploying Weaverse Themes with Docker
For production storefronts, use Shopify Oxygen — it is the officially supported target for Hydrogen. Docker is a good fit for development, testing, review apps, and infrastructure you must control yourself.
Quick Flow
1
Add a Dockerfile and .dockerignore
Multi-stage build;
.dockerignore must exclude .env*.2
Build the image with no secrets in it
docker build -t my-storefront .3
Supply configuration at run time
docker run --env-file .env -p 3000:3000 my-storefront4
Deploy
fly secrets set ... then fly deploy (or the equivalent for your platform).How it works
The container serves the production build withvite preview, as the theme’s npm run start script does (vite preview --port 3456 on Hydrogen 2026.10). The oxygen() Vite plugin from @shopify/mini-oxygen runs the built worker in a local workerd runtime behind Vite’s preview server. Two consequences matter:
- Runtime differences. Worker-specific behavior, caching, and performance characteristics are simulated, not identical to Oxygen. Validate anything runtime-sensitive before relying on it.
- Network binding. The preview server binds to
localhostby default, which is unreachable from outside the container. Pass Vite’s--host 0.0.0.0so the container can serve traffic.
Security rules
Concretely:- Add
.env*to.dockerignoreso env files never enter the build context. - Do not use
ENVor build args for tokens, API keys, orSESSION_SECRET. - Pass configuration at container start:
--env-file,-e, your orchestrator’s secret store, orfly secrets. - Rotate any credential that has already been committed to an image or to Git.
Prerequisites
- Docker (latest stable version)
- Node.js — the Pilot theme requires
^22.18.0 || ^24.11.0 - Fly.io CLI if you are following the Fly example
- A Weaverse theme project that builds locally (
npm run buildsucceeds)
Configuration Files
1. .dockerignore
Create this first, so the build context never contains secrets or junk:
.dockerignore
2. Dockerfile
Dockerfile
vite and @shopify/mini-oxygen are dev dependencies, and vite preview runs the same Vite config as the build, including the React Router plugin. That is why the production stage copies the whole project from the build stage (full node_modules, app/, react-router.config.ts and any local files the config imports) rather than only dist/ with npm ci --omit=dev. Copying only dist/ fails at startup with Could not find a root route module.3. Environment variables
Keep a local.env for development (it is already in the theme’s .gitignore), and use .env.example as the reference list of variables your theme reads:
.env
.env file) to the worker as bindings; before deploying, verify that your @shopify/mini-oxygen version still does:
Cloud Deployment (Fly.io)
1. fly.toml
fly.toml
2. Set secrets
Fly secrets are encrypted at rest and injected into the machine’s environment at run time:3. Deploy
--set-secrets, Kubernetes Secret + envFrom, Render/Railway environment settings. All of them inject variables into the process environment, which is exactly what the container expects.
Troubleshooting
Container starts but nothing responds on the mapped port The preview server is listening onlocalhost. Check that the container command passes --host 0.0.0.0, and that --port matches the port you publish (-p 3000:3000) and Fly’s internal_port.
Build failures
Confirm the Node version in the base image satisfies the theme’s engines.node (^22.18.0 || ^24.11.0), that npm run build succeeds locally, and that Docker has enough memory allocated.
Missing configuration at run time
Check the values actually reached the container: docker exec <container> printenv | sort, or fly ssh console -C printenv. Variable names are case-sensitive.
Weaverse Studio preview does not load
Verify WEAVERSE_PROJECT_ID is set in the running environment and that the deployed URL is registered on your Weaverse project. See Preview Errors.
Limitations and Considerations
- Runtime simulation —
vite previewwith Mini Oxygen runs the worker in a localworkerd. Not every Worker API or performance characteristic matches Oxygen. - Image size — the production stage ships the full project and
node_modulesbecause the preview server comes from dev dependencies (vite,@shopify/mini-oxygen) and loads the project’s Vite config. - Operational ownership — scaling, TLS, log retention, and monitoring are yours to configure. Oxygen handles these for you.
Related
- Deploy to Oxygen — the recommended production path
- Deploy to Cloudflare Workers
- Custom Domain and DNS
- Shopify: Self-hosting Hydrogen