import { containers } from '@fkn/container/vite'
export default {
plugins: [containers()],
}
npm i @fkn/container
Ship a Docker image to the browser.
Then fetch it.
The build step turns your Dockerfile into a WebAssembly artifact. Your app imports it, starts it in a worker, and makes real HTTP requests to the service running inside. No backend, no CORS, no network round trip.
import { createContainer } from '@fkn/container'
import image from './api/Dockerfile?container'
const api = createContainer({ image, ports: [8080] })
const response = await api.fetch('/health')
Live demo
A container is one click away. Send it a request.
The image below is alpine:3.21 with a busybox HTTP service on port 8080,
converted to WebAssembly ahead of time. Everything after the button runs in this tab.
It answers with what it saw, so the JSON below describes your request rather than
repeating a canned string.
- Downloadidle
- Compileidle
- Boot Linuxidle
- First responseidle
The container has not answered yet.
This service forks a busybox shell per connection, which is most of the per-request time under emulation. A process that accepts in a loop is considerably cheaper.
Three files
The whole integration.
The plugin builds the image when the Dockerfile changes and hands your import a URL. It also sets the cross-origin isolation headers the runtime needs, so there is no header archaeology on the first run.
import { createContainer } from '@fkn/container'
import image from './api/Dockerfile?container'
const api = createContainer({
image,
ports: [8080],
})
const res = await api.fetch('/users/42')
console.log(await res.json())
FROM alpine:3.21
EXPOSE 8080
CMD ["/bin/sh", "/srv/serve.sh"]
What is under the fetch
A complete network path, not a fetch shim.
The guest emits Ethernet frames into an in-browser gVisor stack. Your request enters through a loopback listener and is dialled into the guest as real TCP. Outbound traffic leaves over FKN, so the container also gets working DNS, TCP and UDP.
Build one in the browser
Or skip the build step entirely.
The playground boots a builder guest with Buildah inside it, pulls your base image through FKN, and runs the result. Slower than a prebuilt artifact, but nothing is installed and nothing leaves the tab.