The Problem
The imouto frontend is a statically prerendered SvelteKit app -- no Node.js process needed at runtime, just a folder of HTML, CSS, and JS files. But I wanted it running on my local home server, accessible at http://localhost/imouto, routed by Caddy alongside other services. That meant solving three things: configuring SvelteKit to know it lives at a sub-path, building a Docker image that produces only the static output, and setting up Caddy to handle the prefix-stripping correctly.
What I Built
SvelteKit Base Path
The first step was telling SvelteKit it is being served from /imouto, not /. Without paths.base set in svelte.config.js, every asset reference in the built HTML points to /_app/... instead of /imouto/_app/.... A one-line config change, but it affects every internal link and asset URL in the build output.
Multi-Stage Dockerfile
The Docker build uses two stages:
- Stage 1 (builder): Uses
oven/bun:1-alpineto match the project's existingbun.lock. Installs dependencies, copies indataset.jsonandmatches.json(generated by the Python pipeline -- not committed to git), and runsbun run build. The SvelteKit build imports these JSON files at build time via static imports indata.ts. - Stage 2 (serve): Uses
caddy:2-alpine. Copies only the built static files from stage 1. No Node, no Bun, no source code in the final image -- roughly 50MB.
Caddy Routing
The Caddyfile uses handle_path /imouto/* which strips the prefix before looking up files. A request for /imouto/occupation/US:29-1021.00 maps to /srv/imouto/occupation/US:29-1021.00/index.html. The try_files directive handles the SvelteKit prerendered routes and falls back to index.html for edge cases.
Binding to :80 rather than localhost means the server is accessible from other devices on the same LAN -- useful for testing on a phone without deploying anywhere.
Build Context Hygiene
A .dockerignore keeps the build fast and avoids leaking anything sensitive. It excludes the Python virtual environment, node_modules (rebuilt inside Docker), the cache/ directory with confidential AU data files, .env files, and the .plans/ documentation.
What is Next
The container runs and serves the static site, but it is on its own Docker network. The next step is wiring it into the Motivka Caddy reverse proxy so https://localhost/imouto resolves through the shared TLS endpoint alongside all other local services.