Docker
What you'll have at the end: a Vura app running in a Docker container, buildable and runnable on any Docker-capable host.
Supports all route kinds: serverless, hot (WebSocket), task.
How the Dockerfile is generated
When vura build detects hot routes, it emits dist/Dockerfile automatically. The build context for that Dockerfile is the dist/ directory itself — all paths inside the file are relative to dist/.
When no hot routes are present, the same Dockerfile pattern still works; you just need to provide it yourself (copy the one below).
Steps
1. Scaffold and build
npm create vura@latest my-app
cd my-app
npm install
npm run build
After npm run build, dist/Dockerfile will be present if the project has hot routes.
2. The Dockerfile
If dist/Dockerfile was not emitted (no hot routes), create it:
# dist/Dockerfile
# Generated by vura build — hand-edits will be overwritten on next build.
# Build context must be the dist/ directory:
# docker build -f dist/Dockerfile dist
FROM node:22-slim
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev --no-audit --no-fund
COPY . ./
ENV NODE_ENV=production PORT=3000
EXPOSE 3000
CMD ["node", "server/entry.js"]
This is the exact content vura build emits into dist/Dockerfile. The build context is dist/ — note the trailing argument below.
3. Build the image
docker build -f dist/Dockerfile -t my-app dist
Note: the build context is dist, not . — COPY . ./ copies the contents of dist/ into the image.
4. Run the container
docker run -d -p 3000:3000 --name my-app my-app
5. Verify
curl -fsS http://localhost:3000/api/hello
Expected: JSON response from the hello route.
Check logs:
docker logs my-app
Expected: [vura] listening on port 3000
6. Healthcheck (optional, recommended)
Add to your docker run command or Docker Compose service:
docker run -d -p 3000:3000 \
--health-cmd="curl -fsS http://localhost:3000/api/hello || exit 1" \
--health-interval=10s \
--health-timeout=5s \
--health-retries=3 \
--name my-app my-app
Or in docker-compose.yml:
services:
app:
build:
context: dist
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- VURA_REVALIDATE_SECRET=change-me
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/hello"]
interval: 10s
timeout: 5s
retries: 3
Smoke test
# Static page
curl -fsS http://localhost:3000/ | grep -q '<h1'
# API route
curl -fsS http://localhost:3000/api/hello
# WebSocket (requires wscat)
wscat -c ws://localhost:3000/api/chat
CI-tested: this guide is verified by the
dockerjob in.github/workflows/selfhost.yml. The job extracts the Dockerfile from this guide (fenceddockerfileblock), builds the image withdist/as the build context, runs the container, and curls/and/api/hello. It does not test Docker Compose or healthcheck configuration.
Route kind support
All kinds: serverless, hot (WebSocket), task / cron.