Deployment

Deploy your documentation to any hosting platform

Last updated March 18, 2026

Petit builds your docs for any hosting platform. Set the deploy field in your config to target a specific platform, then run the build command.

npx @ephem-sh/petit build
pnpm dlx @ephem-sh/petit build
bun x @ephem-sh/petit build
yarn dlx @ephem-sh/petit build

Petit scaffolds a .petit/ workspace during the build with all required dependencies. You don't need a package.json or node_modules in your project.

Deploy targets

Set the deploy field in petit.config.json to match your hosting platform:

{
  "title": "My Docs",
  "deploy": "cloudflare"
}
Deploy options
PropertyTypeDefaultDescription
nodestring"node"Default. Builds with Nitro for any Node.js server or Docker container
cloudflarestring-Cloudflare Workers via @cloudflare/vite-plugin
netlifystring-Netlify via @netlify/vite-plugin-tanstack-start
vercelstring-Vercel with auto-detection via Nitro
bunstring-Bun runtime via Nitro with the bun preset

Cloudflare Workers

Cloudflare Workers provides edge deployment with global distribution. Set the deploy target in your config:

petit.config.json
{
  "title": "My Docs",
  "deploy": "cloudflare",
  "siteUrl": "https://docs.example.com"
}

Petit generates a wrangler.jsonc and installs @cloudflare/vite-plugin and wrangler automatically inside the .petit/ workspace during the build.

CI/CD with Cloudflare Workers

Create a new Worker in the Cloudflare dashboard and connect your Git repository. Configure the build settings:

Build configuration
PropertyTypeDefaultDescription
Build commandstringnpx @ephem-sh/petit@latest buildScaffolds .petit/ workspace, installs deps, runs vite build
Deploy commandstringcd .petit && npx wrangler deployRuns wrangler from inside .petit/ where node_modules lives
Root directorystring/Your repository root

Add these build environment variables under Settings > Build > Variables and secrets (the build section, not the runtime one):

Build environment variables
PropertyTypeDefaultDescription
SKIP_DEPENDENCY_INSTALLstringtrueSkips Cloudflare's auto-install step (Petit handles its own deps)
NODE_OPTIONSstring--max-old-space-size=4096Increases memory for the vite build process

Note: The SKIP_DEPENDENCY_INSTALL variable must be set in the build variables section. Cloudflare's auto-install runs before the build command and will fail on monorepo lockfiles.

Local deployment

To deploy from your local machine, build first, then run wrangler from the .petit/ directory:

npx @ephem-sh/petit build
pnpm dlx @ephem-sh/petit build
bun x @ephem-sh/petit build
yarn dlx @ephem-sh/petit build
cd .petit && npx wrangler login && npx wrangler deploy

Netlify

Netlify deploys via the @netlify/vite-plugin-tanstack-start plugin, which Petit installs automatically during the build. Set the deploy target in your config:

petit.config.json
{
  "title": "My Docs",
  "deploy": "netlify",
  "siteUrl": "https://docs.example.com"
}

Netlify requires two files in your repository to bypass its automatic dependency installation, which fails on monorepos and projects without a lockfile.

Repository setup

Add these two files to your repository root:

  1. Create a netlify.toml file:
netlify.toml
[build]
  base = ".netlify-skip/"
  command = "cd .. && npx @ephem-sh/petit@latest build --netlify"
  publish = "dist/client/"

[build.environment]
  NODE_OPTIONS = "--max-old-space-size=4096"
  1. Create a a empty .netlify-skip folder in your root and place a package.json inside it .netlify-skip/package.json file:
.netlify-skip/package.json
{"private":true}

The base directory points Netlify's install step at the empty .netlify-skip/ folder, which finishes instantly. The build command changes back to the repository root and runs Petit with the --netlify flag, which moves the SSR function, static assets, and dependencies into the correct locations after the build completes.

Dashboard configuration

Import your repository in the Netlify dashboard. The netlify.toml file configures everything automatically. No dashboard overrides are needed.

Vercel

Vercel deploys via Nitro with auto-detection. Set the deploy target:

petit.config.json
{
  "title": "My Docs",
  "deploy": "vercel",
  "siteUrl": "https://docs.example.com"
}

Import your repository in the Vercel dashboard and configure the build settings:

Vercel build settings
PropertyTypeDefaultDescription
Build Commandstringnpx @ephem-sh/petit@latest buildScaffolds .petit/ workspace, installs deps, runs vite build
Output Directorystring(leave empty)Vercel auto-detects .vercel/output/ generated by Nitro
Install Commandstringecho skipPetit handles its own install during the build step
Root Directorystring./Your repository root

Petit scaffolds a .petit/ workspace during the build, installs all dependencies there, and runs the vite build. Nitro outputs to .vercel/output/ which Vercel picks up automatically.

No package.json or node_modules needed in your project root.

Railway

Railway provides instant deployments with zero configuration files. The default node deploy target works with Railway's Railpack builder. Set the deploy target in your config:

petit.config.json
{
  "title": "My Docs",
  "deploy": "node",
  "siteUrl": "https://docs.example.com"
}

Connect your repository in the Railway dashboard and configure the service settings:

Service settings
PropertyTypeDefaultDescription
Build commandstringnpx @ephem-sh/petit@latest buildScaffolds .petit/ workspace, installs deps, runs vite build
Start commandstringnode .petit/.output/server/index.mjsStarts the Nitro server (reads PORT from Railway automatically)

Add this variable under Variables in your service settings:

Environment variables
PropertyTypeDefaultDescription
RAILPACK_INSTALL_CMDstringmkdir -p node_modulesSkips Railpack's auto-install step (Petit handles its own deps)

Nitro reads Railway's PORT environment variable automatically. No additional configuration is needed.

Note: If your repository has a package.json with an engines field, set it to Node 24 or higher. If you don't have a package.json, set the RAILPACK_NODE_VERSION environment variable to 24 in your Railway service settings.

Node.js and Docker

The default node target works for any Node.js server or Docker container. Build and start the server:

npx @ephem-sh/petit build
pnpm dlx @ephem-sh/petit build
bun x @ephem-sh/petit build
yarn dlx @ephem-sh/petit build
node .petit/.output/server/index.mjs

The server starts on port 3000 by default. Static assets are served from .petit/.output/public.

For Docker, use a multi-stage build:

Dockerfile
FROM node:24-slim AS build
WORKDIR /app
COPY . .
RUN npx @ephem-sh/petit build

FROM node:24-slim
WORKDIR /app
COPY --from=build /app/.petit/.output .output
CMD ["node", ".output/server/index.mjs"]

Bun

Bun requires React 19. Set the deploy target:

petit.config.json
{
  "title": "My Docs",
  "deploy": "bun",
  "siteUrl": "https://docs.example.com"
}
npx @ephem-sh/petit build
pnpm dlx @ephem-sh/petit build
bun x @ephem-sh/petit build
yarn dlx @ephem-sh/petit build
bun .petit/.output/server/index.mjs

Static export

For static hosting without a server, the build output at .petit/.output/public contains all static assets. You can serve this directory with any static file server:

npx serve .petit/.output/public

SEO in production

To get full SEO support (sitemap, OG images, robots.txt, and LLM endpoints), set siteUrl in your config before building:

{
  "siteUrl": "https://docs.example.com"
}

See the SEO reference for details on what gets generated.

Image optimization

Petit converts PNG and JPG images to WebP at build time and generates responsive sizes (640px, 1024px, 1920px). This runs automatically during build if sharp is installed.

Offline support

The built site includes a service worker that caches pages after the first visit. Returning visitors can browse cached pages without a network connection. The service worker updates the cache in the background when a connection is available.

Search Documentation

Search for pages and content