--- title: 'Production builds' slug: production-builds description: 'How a Mochi build behaves in production: relocatable output, sub-path hosting, and a persistent image cache.' --- # Production builds `mochi-framework build` writes a self-contained build to `.mochi/` (see the [CLI reference](/docs/cli/)). This page covers what that build gives you once it is deployed: it relocates cleanly from where you built it to where you run it, it can be hosted under a sub-path, and its image cache can survive container restarts. For where to run it, see [Deployment options](/docs/deployment-options/); to containerize it, see [Building a Dockerfile](/docs/docker/). ## Relocatable builds A manifest holds no absolute paths — artifacts are written relative to the out-dir, sources relative to the project root — so you can build in one place and run in another. Build in a CI stage, copy `.mochi/` into the final image, and point the runtime at wherever it landed: ```ts Mochi.serve({ outDir: './.mochi' }); // default — or wherever you copied it ``` Paths resolve against the manifest's own directory, so pointing `manifest` at a relocated build works on its own: ```ts Mochi.serve({ manifest: '/srv/app/build/manifest.json' }); ``` Five things still anchor a prebuilt app to its project: - **Build and serve from the same working directory.** Components are keyed relative to the project root, which both `mochi-framework build` and `Mochi.serve()` take to be the current working directory. Run both from the project root. - **Ship your `public/` directory.** Static files are never copied into the build. The runtime scans `publicDir` (default `./public`) at startup in production exactly as in development. A deploy that ships only `.mochi/` and `src/` 404s every static file. - **Keep the out-dir in the project tree.** The compiled SSR modules resolve `node_modules` from the out-dir's location. - **On-demand server islands need sources.** Islands missing from the manifest are compiled at request time from source paths recorded at build. Prebuilt islands relocate fine. - **Keep email templates in `src/emails/`.** A `Mochi.email({ component })` template is reachable only at send time, so the build walks that directory to find it. See [Svelte templates](/docs/email/#svelte-templates). The manifest records a schema version, and the runtime loads only the exact version it writes. Booting a build made by a different `mochi-framework` version throws at startup. Always run `mochi-framework build` with the same version you serve with. ## Sub-path and static hosting Mochi writes absolute URLs for everything it owns: `/_mochi/client/…`, `/_mochi/css/…`, the island `component-url`, and the `import` specifiers inside the client chunks. To host under a sub-path such as `https://example.com/my-app/`, bake the prefix in at build time: ```sh mochi-framework build --asset-prefix /my-app/_mochi ``` The value lands in `manifest.json` and every emitted URL carries it. `Mochi.serve({ assetPrefix })` sets the same thing for on-demand compilation, but once a manifest exists its value wins and a differing `serve()` value only logs a warning. `assetPrefix` covers framework assets only. Links you write yourself (`href="/about"`) and files in `public/` are served at the paths you give them, so prefix those in your own markup. The same prefix is what makes a hand-rolled static export work: copy `.mochi/svelte-client` to `/my-app/_mochi/client` and `.mochi/svelte-css` to `/my-app/_mochi/css`, save each prerendered page, and nothing needs rewriting. ### Rewriting URLs yourself If you post-process the HTML into page-relative URLs instead, treat `component-url` like any other attribute: it resolves against the page, so `../_mochi/client/…` on `/my-app/writing/` loads from `/my-app/_mochi/client/`, the same place a `` on that page does. ## Persistent image cache Mochi's [image cache](/docs/images/) is written to disk under `cacheDir` (default `./.mochi/image-cache`). In a container that directory is recreated on every restart, so each redeploy starts with a cold cache and re-fetches and re-transforms every image. To keep the transformed bytes across restarts, point `cacheDir` at a dedicated path and mount a volume there: ```ts Mochi.serve({ image: { cacheDir: process.env.MOCHI_IMAGE_CACHE_DIR /* , sizes: … */ }, }); ``` ```yaml services: site: image: your-app environment: MOCHI_IMAGE_CACHE_DIR: /data/image-cache volumes: - image-cache:/data/image-cache volumes: image-cache: ``` A plain `docker run -v image-cache:/data/image-cache your-app` mounts the same volume. Keep the mount off `./.mochi` — its build cache is rebuilt on every boot and must not persist. If the container runs as a non-root user, pre-create the directory owned by that user in your `Dockerfile` so the mounted volume is writable. Keep `MOCHI_KEY` stable across restarts. Image URLs are signed with a key derived from it, so a changed key invalidates already-minted links even though the cached bytes are still on disk.