Skip to main content

Bun servers

adapter-bun builds a SvelteKit application into a standalone Bun server. The generated server uses Bun.serve for requests and Bun.file responses for client assets, prerendered output, and files read with read from $app/server.

Usage

Install the adapter:

bun add -D @sveltejs/adapter-bun

Configure it in vite.config.js:

vite.config
import import adapteradapter from '@sveltejs/adapter-bun';
import { function sveltekit(config?: Config): Promise<Plugin[]>

The SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project’s configuration:

vite.config
import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	plugins: [
		sveltekit({
			adapter: adapter(),
			compilerOptions: {
				experimental: {
					async: true
				}
			},
			experimental: {
				remoteFunctions: true
			}
		})
	]
});

As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions.

Any options that don’t belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

Legacy mode

Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

sveltekit
} from '@sveltejs/kit/vite';
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct {@link UserConfig } object, or a function that returns it. The function receives a {@link ConfigEnv } object.

defineConfig
} from 'vite';
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct {@link UserConfig } object, or a function that returns it. The function receives a {@link ConfigEnv } object.

defineConfig
({
UserConfig.plugins?: PluginOption[] | undefined

Array of vite plugins to use.

plugins
: [
function sveltekit(config?: Config): Promise<Plugin[]>

The SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project’s configuration:

vite.config
import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	plugins: [
		sveltekit({
			adapter: adapter(),
			compilerOptions: {
				experimental: {
					async: true
				}
			},
			experimental: {
				remoteFunctions: true
			}
		})
	]
});

As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions.

Any options that don’t belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

Legacy mode

Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

sveltekit
({
Config.adapter?: Adapter | undefined

Your adapter is run when executing vite build. It determines how the output is converted for different platforms.

@default
undefined
adapter
: import adapteradapter()
}) ] });

The adapter calls Bun’s build API, so the production build itself must run in Bun. The --bun flag overrides Vite’s Node.js shebang:

bun run --bun build

The default build is written to build. Start it with:

bun ./build

The JavaScript server, client files, and prerendered files in the output directory are all required at runtime. Application imports are processed according to Bun’s bundler behavior.

Client assets and prerendered output are registered as native Bun routes. Only GET and HEAD requests are served by those routes; other methods continue to SvelteKit. Every asset carries an ETag computed during the build, so conditional requests revalidate with an empty 304 response. Bun supplies MIME types, byte ranges for filesystem-backed files, and streaming without buffering every asset in memory. Files below SvelteKit’s immutable directory receive Cache-Control: public,max-age=31536000,immutable.

Bun treats * in a route pathname as a wildcard. The adapter rejects client and prerendered filenames that contain a literal *; rename those files before building.

Options

vite.config
import import adapteradapter from '@sveltejs/adapter-bun';
import { function sveltekit(config?: Config): Promise<Plugin[]>

The SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project’s configuration:

vite.config
import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	plugins: [
		sveltekit({
			adapter: adapter(),
			compilerOptions: {
				experimental: {
					async: true
				}
			},
			experimental: {
				remoteFunctions: true
			}
		})
	]
});

As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions.

Any options that don’t belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

Legacy mode

Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

sveltekit
} from '@sveltejs/kit/vite';
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct {@link UserConfig } object, or a function that returns it. The function receives a {@link ConfigEnv } object.

defineConfig
} from 'vite';
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)

Type helper to make it easier to use vite.config.ts accepts a direct {@link UserConfig } object, or a function that returns it. The function receives a {@link ConfigEnv } object.

defineConfig
({
UserConfig.plugins?: PluginOption[] | undefined

Array of vite plugins to use.

plugins
: [
function sveltekit(config?: Config): Promise<Plugin[]>

The SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project’s configuration:

vite.config
import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	plugins: [
		sveltekit({
			adapter: adapter(),
			compilerOptions: {
				experimental: {
					async: true
				}
			},
			experimental: {
				remoteFunctions: true
			}
		})
	]
});

As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions.

Any options that don’t belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

Legacy mode

Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

sveltekit
({
Config.adapter?: Adapter | undefined

Your adapter is run when executing vite build. It determines how the output is converted for different platforms.

@default
undefined
adapter
: import adapteradapter({
out: stringout: 'build', envPrefix: stringenvPrefix: '',
serverOptions: {
    idleTimeout: number;
}
serverOptions
: {
idleTimeout: numberidleTimeout: 30 },
buildOptions: {
    sourcemap: string;
}
buildOptions
: {
sourcemap: stringsourcemap: 'external' } }) }) ] });

out

The output directory. It defaults to build.

precompress

Set precompress: true to generate .br and .gz variants of client and prerendered assets during the build. The generated routes negotiate Accept-Encoding per request, preferring brotli over gzip, and each variant carries its own ETag. The option is ignored when buildOptions.compile is set, because embedded assets are imported by identity path.

envPrefix

A prefix for every deployment environment variable documented below. This is useful when the unprefixed names conflict with variables managed by your host:

import adapteradapter({ envPrefix: stringenvPrefix: 'MY_APP_' });
MY_APP_HOST=127.0.0.1 MY_APP_PORT=4000 bun ./build

When a prefix is configured, the server fails at startup if it finds an unknown environment variable with that prefix. This catches collisions and misspellings.

serverOptions

JSON-serializable defaults passed to Bun.serve. The supported properties are:

  • hostname
  • port
  • unix
  • reusePort
  • ipv6Only
  • idleTimeout
  • maxRequestBodySize
  • development

Environment variables take precedence over these defaults. A configured Unix socket takes precedence over hostname, port, reusePort, and ipv6Only.

The generated server owns fetch and routes. It does not expose websocket, error, TLS, HTTP/3, or HTTP/1 configuration through serverOptions. Use a custom Bun integration if your application requires those Bun.serve options.

buildOptions

Advanced Bun build settings can be supplied with buildOptions. The adapter currently accepts sourcemap, minify, bytecode, banner, footer, drop, features, optimizeImports, splitting, and compile. Code splitting is enabled by default; splitting: false bundles the server into a single file, which works around Bun.build output path collisions on applications whose module graph produces identically-hashed chunks.

The generated entrypoint, output directory, top-level target, and module format are reserved. Generated servers target Bun and use ESM. Source maps default to external; set sourcemap: 'none' to disable them.

Compiled executables

Set compile: true to generate a single executable at <out>/server:

import adapteradapter({
	
buildOptions: {
    compile: boolean;
}
buildOptions
: {
compile: booleancompile: true } });

Build and run it without a separately installed Bun runtime:

bun run --bun build
./build/server

The executable embeds the server code, client assets, prerendered output, and Bun runtime. compile can also be a Bun target string, which keeps the default server filename, or an options object. To change the executable name or cross-compile, provide an options object:

import adapteradapter({
	out: stringout: 'dist',
	
buildOptions: {
    compile: {
        outfile: string;
        target: string;
    };
    minify: boolean;
    bytecode: boolean;
    sourcemap: string;
}
buildOptions
: {
compile: {
    outfile: string;
    target: string;
}
compile
: {
outfile: stringoutfile: 'application', target: stringtarget: 'bun-linux-x64' }, minify: booleanminify: true, bytecode: booleanbytecode: true, sourcemap: stringsourcemap: 'linked' } });

The result in this example is dist/application. Platform targets, native dependencies, and other limitations follow Bun’s executable compilation rules.

Environment variables

Bun loads .env files automatically. If envPrefix is set, add that prefix to each name in this section.

Listener

HOST and PORT configure the TCP listener. Without either value or a serverOptions default, the server listens on port 3000.

HOST=127.0.0.1 PORT=4000 bun ./build

SOCKET_PATH selects a Unix domain socket instead. When it is present, TCP-only options are ignored:

SOCKET_PATH=/tmp/sveltekit.sock bun ./build

REUSE_PORT enables Bun’s reusePort option and IPV6_ONLY enables ipv6Only. Boolean variables accept 1, true, yes, and on, or 0, false, no, and off, without regard to letter case.

Request limits and diagnostics

BODY_SIZE_LIMIT controls Bun.serve’s maxRequestBodySize. It defaults to 512K. The value must resolve to a whole number of bytes and may use a case-insensitive binary K, M, or G suffix, such as 768K or 1.5M. Infinity disables the limit.

CONNECTION_IDLE_TIMEOUT sets Bun’s per-request inactivity timeout in seconds. It must be an integer from 0 through 255; 0 disables the timeout. The generated handler disables the timeout for responses whose content type starts with text/event-stream and also adds X-Accel-Buffering: no. It is deliberately not called IDLE_TIMEOUT, which on adapter-node means something different (shut the server down after N seconds without requests).

DEVELOPMENT enables Bun’s development-mode error pages. It defaults to false for the generated server.

Public origin behind a proxy

If paths.origin is configured, that value is the trusted origin for every request. Otherwise, the adapter derives the host from the Host header and assumes the scheme is https, since production deployments usually terminate TLS upstream. Configure paths.origin or PROTOCOL_HEADER if that assumption is wrong, for example when serving plain HTTP directly.

Behind a trusted reverse proxy, PROTOCOL_HEADER, HOST_HEADER, and PORT_HEADER name headers that contain the public scheme, host, and port:

PROTOCOL_HEADER=x-forwarded-proto \
HOST_HEADER=x-forwarded-host \
PORT_HEADER=x-forwarded-port \
bun ./build

The protocol header must contain http or https, without a colon. The port header must contain a number. Invalid values produce a 400 Bad Request response. A header that is present but empty is ignored in favour of the fallback.

[!CAUTION] Only trust forwarded headers when requests can reach the server through a proxy you control. A direct client can spoof these headers.

Client addresses behind a proxy

event.getClientAddress() uses server.requestIP(request).address by default. Set ADDRESS_HEADER to the name of a trusted proxy header when the direct peer is a proxy:

ADDRESS_HEADER=true-client-ip bun ./build

For x-forwarded-for, also set XFF_DEPTH to the number of trusted proxies. The default depth is 1, and the adapter selects from the right side of the comma-separated list so client-supplied entries to the left cannot change the trusted result:

ADDRESS_HEADER=x-forwarded-for XFF_DEPTH=2 bun ./build

XFF_DEPTH must be an integer of at least 1. getClientAddress() throws if the configured header is absent or contains fewer addresses than the configured depth.

Platform API

The request event’s platform property exposes the Bun server instance:

/** @type {import('./$types').RequestHandler} */
export function 
function GET({ getClientAddress, platform }: {
    getClientAddress: any;
    platform: any;
}): Response
GET
({ getClientAddress: anygetClientAddress, platform: anyplatform }) {
return
var Response: {
    new (body?: BodyInit | null, init?: ResponseInit): Response;
    prototype: Response;
    error(): Response;
    json(data: any, init?: ResponseInit): Response;
    redirect(url: string | URL, status?: number): Response;
}

The Response interface of the Fetch API represents the response to a request.

MDN Reference

Response
.function json(data: any, init?: ResponseInit): Response

The json() static method of the Response interface returns a Response that contains the provided JSON data as body, and a Content-Type header which is set to application/json. The response status, status message, and additional headers can also be set.

MDN Reference

json
({
address: anyaddress: getClientAddress: anygetClientAddress(), serverId: anyserverId: platform: anyplatform.server.id, pendingRequests: anypendingRequests: platform: anyplatform.server.pendingRequests, pendingWebSockets: anypendingWebSockets: platform: anyplatform.server.pendingWebSockets }); }

Graceful shutdown

On SIGINT or SIGTERM, the generated server calls server.stop(). Bun stops accepting new connections and the adapter waits for pending requests before emitting a sveltekit:shutdown process event with the signal name:

var process: NodeJS.Processprocess.NodeJS.Process.on(event: string | symbol, listener: (...args: any[]) => void): NodeJS.Process (+13 overloads)

Adds the listener function to the end of the listeners array for the event named eventName. No checks are made to see if the listener has already been added. Multiple calls passing the same combination of eventName and listener will result in the listener being added, and called, multiple times.

server.on('connection', (stream) => {
  console.log('someone connected!');
});

Returns a reference to the EventEmitter, so that calls can be chained.

By default, event listeners are invoked in the order they are added. The emitter.prependListener() method can be used as an alternative to add the event listener to the beginning of the listeners array.

import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
//   b
//   a
@since
v0.1.101
@param
eventName The name of the event.
@param
listener The callback function
on
('sveltekit:shutdown', async (reason: anyreason) => {
await jobs.stop(); await db.close(); });

Connections that are still open after SHUTDOWN_TIMEOUT seconds are closed forcefully, so idle connections such as open event streams cannot delay the shutdown indefinitely. The value must be a non-negative integer and defaults to 30.

Sending a second shutdown signal forces the process to exit with status 1.

Edit this page on GitHub llms.txt