Configure Bun application for Railway using Railpack
Railway automatically detects Bun from bun.lock and installs the latest Bun version by default. To pin a specific Bun version, use the engines.bun or packageManager field in package.json. Railpack is the default builder for Bun applications on Railway. If a service is still building with Nixpacks (the previous builder, now in maintenance mode), switch to Railpack by adding a railway.json file with the configuration: {"$schema": "https://railway.com/railway.schema.json", "build": {"builder": "RAILPACK"}}
Deploy to Railway via Dashboard step sequence
To deploy to Railway using the Dashboard: (1) Go to Railway Dashboard, click "+ New" → "GitHub repo", and choose your repository, (2) Add PostgreSQL database by clicking "+ New" → "Database" → "Add PostgreSQL", then select your service (not the database), go to "Variables" tab, click "+ New Variable" → "Add Reference", and select DATABASE_URL from postgres (if database is needed), (3) Generate a public domain by selecting your service, going to "Settings" tab, and clicking "Generate Domain" under "Networking". Railway auto-deploys on every GitHub push.
Railway CLI auto-deployment from GitHub
After deploying with railway up, you can configure Railway to auto-deploy on every GitHub push by connecting the service to your repository with the command: railway service source connect --repo <owner>/<repo> --branch <branch>
Install Railway CLI with Bun
Install the Railway CLI using the command: bun install -g @railway/cli
Deploy to Railway via CLI step sequence
To deploy to Railway using the CLI: (1) Install Railway CLI with bun install -g @railway/cli, (2) Log in with railway login, (3) Initialize project with railway init, (4) Add PostgreSQL database with railway add --database postgres (if needed), (5) Add application service with railway add --service bun-react-db --variables DATABASE_URL=\${{Postgres.DATABASE_URL}}, (6) Deploy with railway up, (7) Generate public domain with railway domain.
.dockerignore contents for Bun Lambda deployment
For a Bun application deployed to AWS Lambda, create a .dockerignore file in the project root that excludes: node_modules, Dockerfile*, .dockerignore, .git, .gitignore, README.md, LICENSE, .vscode, .env, and any other files or directories not needed in the container image. This keeps builds faster and smaller.
Docker build command for Bun Lambda image
Build the Docker image with the command: `docker build --provenance=false --platform linux/amd64 -t bun-lambda-demo:latest .` The --platform linux/amd64 flag is required for AWS Lambda compatibility.
Create ECR repository for Bun Lambda
Create an ECR repository with the command: `aws ecr create-repository --repository-name bun-lambda-demo --region us-east-1` Optionally export the repository URI: `export ECR_URI=$(aws ecr create-repository --repository-name bun-lambda-demo --region us-east-1 --query 'repository.repositoryUri' --output text)`. If using AWS CLI with profiles or IAM Identity Center (SSO), add the `--profile` flag with your profile name.
Authenticate Docker with ECR repository
Log in to the ECR repository with the command: `aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_URI`. If using an AWS CLI profile, add the `--profile` flag: `aws ecr get-login-password --region us-east-1 --profile my-sso-app | docker login --username AWS --password-stdin $ECR_URI`.
Tag and push Docker image to ECR
Tag the Docker image with the ECR repository URI using: `docker tag bun-lambda-demo:latest ${ECR_URI}:latest`. Then push the image with: `docker push ${ECR_URI}:latest`.
Create AWS Lambda function from container image
In the AWS Console, go to Lambda > Create Function and select Container image as the source. Choose a name for the function (e.g., my-bun-function). Select the appropriate region (the guide uses us-east-1). Select the container image URI from the ECR repository by clicking Browse images, then select the latest image.
Enable public Function URL for AWS Lambda
To get a public URL for the Lambda function, go to Additional configurations > Networking > Function URL. Set Function URL to Enable with Auth Type set to NONE.
Test AWS Lambda function URL
After the Lambda function is created, the function URL is displayed in the Function URL section of the function page. Test the function by calling it directly with curl using the format: `curl -X GET https://[your-function-id].lambda-url.us-east-1.on.aws/`.
AWS Lambda Dockerfile for Bun
To deploy a Bun HTTP server to AWS Lambda, use a multi-stage Dockerfile. Start with the official AWS Lambda adapter image (public.ecr.aws/awsguru/aws-lambda-adapter:1.0.1) as the first stage, copy the lambda-adapter to /opt/extensions/lambda-adapter. Then use the oven/bun:debian image as the main stage. Set PORT environment variable to 8080 (required for AWS Lambda adapter). Set WORKDIR to /var/task (the default work directory for Lambda). Copy package.json and bun.lock, then run `bun install --production --frozen-lockfile`. Finally, copy the rest of the application and set CMD to execute the entry point, such as `CMD ["bun", "index.ts"]` or `CMD ["bun", "run", "start"]` if using a start script. If the app has no dependencies, omit the package.json copy and install steps, as Bun does not write a bun.lock for projects with no dependencies.
Deploy Bun HTTP server to DigitalOcean with Docker
This guide shows how to deploy a Bun HTTP server to DigitalOcean using a Dockerfile. The process involves creating a DigitalOcean Container Registry, creating a Dockerfile, authenticating Docker with the registry, building and pushing the Docker image, creating a DigitalOcean App Platform project, and deploying the application.
Bun Dockerfile for DigitalOcean deployment
A Dockerfile for deploying a Bun application to DigitalOcean should: use `FROM oven/bun:debian` as the base image, set WORKDIR to `/app`, copy `package.json` and `bun.lock`, run `bun install --production --frozen-lockfile`, copy the rest of the application, expose port 8080, and run the application with `CMD ["bun", "index.ts"]` or `CMD ["bun", "run", "start"]` if using a start script in package.json.
Docker authentication with DigitalOcean registry
Authenticate Docker with DigitalOcean's Container Registry using `doctl registry login` before building and pushing images. This command logs in using your DigitalOcean credentials and generates an authentication token valid for 30 days by default.
Docker buildx command for building Bun images on ARM Mac
When building on an ARM Mac (M1/M2), use `docker buildx build --platform=linux/amd64 -t registry.digitalocean.com/bun-digitalocean-demo/bun-digitalocean-demo:latest --push .` to ensure compatibility with DigitalOcean's linux/amd64 infrastructure. Using `docker build` without the platform flag creates an ARM64 image that won't run on DigitalOcean.
DigitalOcean CLI command to create Container Registry
Create a DigitalOcean Container Registry using `doctl registry create bun-digitalocean-demo`. This command outputs the registry name, endpoint (registry.digitalocean.com/bun-digitalocean-demo), and region slug.
Prerequisites for deploying to Google Cloud Run
Before deploying, ensure: a Bun application is ready for deployment, a Google Cloud account exists with billing enabled, and the Google Cloud CLI is installed and configured.
Initialize gcloud CLI for Google Cloud Run
Run `gcloud init` to initialize the Google Cloud CLI. This logs you in and prompts you to select an existing project or create a new one.
Enable APIs and IAM roles for Cloud Run deployment
Run `gcloud services enable run.googleapis.com cloudbuild.googleapis.com` to activate Cloud Run and Cloud Build. Then run `gcloud projects add-iam-policy-binding $PROJECT_ID --member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com --role=roles/run.builder` to grant the Compute Engine service account permission to build and deploy images.
Link billing account to Google Cloud project
List available billing accounts with `gcloud billing accounts list`. Link one to the project using `gcloud billing projects link $PROJECT_ID --billing-account=[BILLING_ACCOUNT_ID]`, replacing [BILLING_ACCOUNT_ID] with the actual billing account ID.
Deploy Bun app to Google Cloud Run with Dockerfile
Google Cloud Run is a managed serverless platform for deploying and scaling applications. To deploy a Bun HTTP server to Cloud Run, create a Dockerfile in the project root, enable the necessary APIs (run.googleapis.com and cloudbuild.googleapis.com), configure IAM roles, and use the gcloud run deploy command.
Dockerfile for Bun application
Use the official Bun image (oven/bun:latest) as the base. Copy package.json and bun.lock, run `bun install --production --frozen-lockfile`, copy the rest of the application, and set the start command with `CMD ["bun", "index.ts"]` or `CMD ["bun", "run", "start"]` depending on the entry point. If the app has no dependencies, omit the COPY and RUN install commands.
Google Cloud Run deployment command syntax
Use `gcloud run deploy <service-name> --source . --region=<region> --allow-unauthenticated` to deploy a Bun application from local source. The --region flag can be omitted to select a region interactively. Example: `gcloud run deploy my-bun-app --source . --region=us-west1 --allow-unauthenticated`
.dockerignore file for Bun projects
Create a .dockerignore file to exclude files and directories from the container image. Typically exclude: node_modules, Dockerfile*, .dockerignore, .git, .gitignore, README.md, LICENSE, .vscode, .env, and other unnecessary files. This keeps builds faster and smaller.
Render deployment runtime and build/start commands
When deploying a Bun application on Render, configure the web service with: Runtime set to `Node`, Build Command set to `bun install`, and Start Command set to `bun app.ts` (or the appropriate entry point file).
Render supports Bun natively
Render is a cloud platform that supports Bun natively. You can deploy Bun apps as web services, background workers, cron jobs, and more on Render.
Express HTTP server example on Render
Example Express application deployed on Render:
```ts
import express from "express";
const app = express();
const port = process.env.PORT || 3001;
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
```
This example uses `process.env.PORT` for the port to support environment variable configuration on Render, defaulting to 3001.
Routing Middleware runtime configuration on Vercel
To run Routing Middleware with Bun on Vercel, set the runtime to nodejs in the config export:
export const config = { runtime: "nodejs" };
bunVersion configuration for Vercel deployment
To run Bun Functions on Vercel, add a bunVersion field to vercel.json with the value "1.x". Vercel manages the minor and patch versions automatically.
Vercel Bun framework preset requirements
Vercel's Bun framework preset detects and routes requests to a Bun.serve() server when three conditions are met: the project sets bunVersion, has a bun.lock file, and has a server entrypoint at one of these paths: server.js, server.cjs, server.mjs, server.ts, server.cts, server.mts, src/server.js, src/server.cjs, src/server.mjs, src/server.ts, src/server.cts, or src/server.mts.
Create bun.lock file for Vercel preset detection
On Bun 1.2 or later, bun install creates bun.lock automatically. On older versions, run bun install --save-text-lockfile. The preset does not detect the binary bun.lockb format.
Vercel Bun.serve() supported options
Vercel supports the fetch, routes, error, and websocket options for Bun.serve(). The port and hostname options only apply when running the server locally; they do not configure the deployed endpoint.
Bun.serve() limitations on Vercel
Unix sockets and HTML imports in routes are not supported on Vercel when using Bun.serve().
Bun.serve() under /api on Vercel
To add a Bun server to a project with a frontend, create api/server.ts and call Bun.serve() while the module loads. Vercel deploys it as a single Function at /api/server, and only requests for /api/server reach this server. This setup only requires bunVersion in vercel.json and does not need a bun.lock file.
Bun.serve() example for Vercel
Example of a Bun.serve() server for Vercel deployment:
Bun.serve({
routes: {
"/health": () => Response.json({ status: "ok" }),
},
fetch() {
return new Response("Hello from Bun on Vercel");
},
});
Bun.serve() under /api example for Vercel
Example of a Bun server deployed under /api on Vercel:
Bun.serve({
fetch(request) {
const url = new URL(request.url);
return Response.json({
message: "Hello from Bun on Vercel",
pathname: url.pathname,
});
},
});
Frameworks on Vercel with Bun
Frameworks that Vercel supports, such as Next.js, Express, Hono, and Nitro, run on Bun once you set bunVersion.
Next.js package.json scripts for Bun on Vercel
For Next.js projects on Vercel with Bun, update package.json scripts so the Next.js CLI runs under Bun:
"dev": "bun --bun next dev"
"build": "bun --bun next build"
The --bun flag runs the Next.js CLI under Bun. Bundling with Turbopack or Webpack is unchanged.
Deploy Bun app to Vercel using CLI
To deploy a Bun app to Vercel from the CLI, use bunx vercel login and bunx vercel deploy without global install, or install the Vercel CLI globally with bun i -g vercel, then run vercel login and vercel deploy.
Verify Bun runtime on Vercel
To confirm a deployment uses Bun, log process.versions.bun in the server code. Example:
console.log("runtime", process.versions.bun);
This outputs the Bun version string like "1.3.14".
Vercel Bun runtime limitations in Beta
The Bun runtime on Vercel is in Beta. Automatic source maps, bytecode caching, and request metrics for node:http and node:https are not supported yet. Request metrics for fetch are supported.
Bun Docker build example with multi-stage
Example multi-stage Dockerfile for Bun that installs dependencies separately for dev and production, runs tests and build, then copies only production dependencies into the final image. The example uses `FROM oven/bun:1 AS base`, installs with `bun install --frozen-lockfile` and `bun install --frozen-lockfile --production`, sets `ENV NODE_ENV=production`, runs `bun test` and `bun run build`, and starts the container with `ENTRYPOINT [ "bun", "run", "index.ts" ]` as user `bun` on port 3000.
Official Bun Docker image
The official Bun Docker image is `oven/bun:1`. All available versions can be found at https://hub.docker.com/r/oven/bun/tags.
.dockerignore file usage with Bun
A `.dockerignore` file uses syntax similar to `.gitignore` and lists files and directories to exclude from every stage of the Docker build. Common entries for Bun projects include: node_modules, Dockerfile*, docker-compose*, .dockerignore, .git, .gitignore, README.md, LICENSE, .vscode, Makefile, helm-charts, .env, .editorconfig, .idea, and coverage*.
Building a Bun Docker image
Use `docker build --pull -t bun-hello-world .` to build a Docker image from a Dockerfile. The `-t` flag names the image, and `--pull` tells Docker to download the latest version of the base image.
Running a Bun Docker container
Use `docker run -d -p 3000:3000 bun-hello-world` to start a container from a Bun image. The `-d` flag runs it in detached mode, and `-p 3000:3000` maps the container's port 3000 to port 3000 on the host machine.
Stopping and listing Docker containers
Use `docker stop <container-id>` to stop a running container. Use `docker ps` to list all running containers and find their container IDs.
Run Bun as PM2 daemon with --interpreter flag
To start a Bun application as a daemon with PM2 using the --interpreter option, run: pm2 start --interpreter ~/.bun/bin/bun index.ts
PM2 configuration file for Bun
Create a pm2.config.cjs file in your project directory with the following structure:
module.exports = {
name: "app",
script: "index.ts",
interpreter: "bun",
env: {
PATH: `${process.env.HOME}/.bun/bin:${process.env.PATH}`,
},
};
The name field specifies the application name, script is the entry point, interpreter is set to "bun", and the env object adds ~/.bun/bin to the PATH.
Start PM2 with Bun using configuration file
After creating a pm2.config.cjs file, start the application with PM2 by running: pm2 start pm2.config.cjs
PM2 process manager capabilities
PM2 is a process manager that runs applications as daemons (background processes). It offers process monitoring, automatic restarts, and scaling. It keeps applications running when deployed to cloud-hosted virtual private servers (VPS).
Configure Nitro plugin in vite.config.ts
Import nitro from 'nitro/vite' and add `nitro({ preset: 'bun' })` to the plugins array in defineConfig. The bun preset configures the build output specifically for Bun's runtime.
Install Nitro for TanStack Start deployment
Run `bun add nitro` to add Nitro to your TanStack Start project for deploying to different platforms.
Nitro Vercel deployment configuration
When deploying to Vercel, do not use the `bun` Nitro preset. Instead, either add `"bunVersion": "1.x"` to vercel.json or set the Nitro config with `preset: 'vercel'` and `vercel: { functions: { runtime: 'bun1.x' } }` in vite.config.ts.
Build and start TanStack Start with Nitro
In package.json, set build script to `bun --bun vite build` and start script to `bun run .output/server/index.mjs`. The .output files are created by Nitro when running build. This start script is not necessary when deploying to Vercel.
Custom Bun server for TanStack Start
Create a server.ts file in the project root to implement a custom production server. The server handles static asset serving with a hybrid loading strategy: preloading small files into memory and serving larger files on-demand. It supports ETag generation, Gzip compression, and configurable file filtering.
Custom TanStack Start server environment variables
Configuration via environment variables: PORT (default 3000) sets server port; ASSET_PRELOAD_MAX_SIZE (default 5242880 bytes/5MB) sets maximum file size to preload; ASSET_PRELOAD_INCLUDE_PATTERNS specifies comma-separated glob patterns for files to include in preloading; ASSET_PRELOAD_EXCLUDE_PATTERNS specifies comma-separated glob patterns for files to exclude; ASSET_PRELOAD_VERBOSE_LOGGING (default false) enables detailed logging; ASSET_PRELOAD_ENABLE_ETAG (default true) enables ETag generation; ASSET_PRELOAD_ENABLE_GZIP (default true) enables Gzip compression; ASSET_PRELOAD_GZIP_MIN_SIZE (default 1024 bytes) sets minimum file size for Gzip compression; ASSET_PRELOAD_GZIP_MIME_TYPES specifies comma-separated MIME types eligible for compression with default 'text/,application/javascript,application/json,application/xml,image/svg+xml'.