This article will guide you through deploying a Bun + Hono + Vite application to an Azure Linux Web App.
TOC
- Introduction
- Local Environment
- Deployment
- Conclusion
1. Introduction
Anthropic, the company behind Claude, recently acquired the JavaScript runtime startup Bun, marking one of the most significant shifts in the modern JavaScript ecosystem since the arrival of Node.js and Deno. This acquisition signals more than a business move, it represents a strategic consolidation of performance-oriented tooling, developer ergonomics, and the future of AI-accelerated software development. At the center of this momentum lies a powerful trio: Bun, Hono, and Vite.
Bun is a next-generation JavaScript runtime that reimagines everything from dependency installation to HTTP servers, bundling, and execution speed.
On top of Bun, frameworks like Hono provide an elegant, lightweight approach to building APIs and full web applications. Hono embraces the Web Standard API model while optimizing for speed and minimal footprint.
For front-end development, Vite completes the trio. Vite provides lightning-fast local development through native ES modules and an optimized build pipeline. When paired with Bun, the developer experience becomes even smoother, as Bun accelerates not only the dev server but also the entire build process. The result is a full-stack workflow where front-end and back-end both benefit from a consistent, high-performance environment.
This article will guide you through deploying a Bun + Hono + Vite application to an Azure Linux Web App.
2. Local Environment
The development environment used in this example is a Docker container. All project creation, modification, and testing will take place inside this environment.
We will build an application using Bun + Hono + Vite, containing three endpoints:
- / : The root endpoint, which displays Hello Bun Hono Vite
- /api/hello : A static endpoint
- /api/backend : A runtime endpoint executed on the server, returning computed results for the frontend to display
Create the Docker development environment
This command generates a Bun + Hono + Vite project.
docker run --rm -it -v "$PWD":/app -w /app oven/bun:latest bunx create-vite . --template vanilla-ts
Follow the prompts as shown in the image and make the appropriate selections. After completing the prompts, the corresponding project files will be created.
At this point, you may press Ctrl + C to stop the running Bun server.
Next, install Hono:
docker run --rm -it -v "$PWD":/app -w /app oven/bun:latest bun add hono
Create 4 files and edit 2 files
Create .vscode/settings.json
This file serves two purposes:
- To prevent the large node_modules folder from being uploaded during deployment. Uploading it wastes bandwidth and may cause compatibility issues between local and production environments.
- To disable ORYX BUILD from interfering in the deployment process. We will use a custom startup script to handle all build tasks instead.
{
"appService.zipIgnorePattern": [
"node_modules{,/**}",
".git{,/**}",
".vscode{,/**}"
],
"appService.showBuildDuringDeployPrompt": false
}
Create vite.config.ts
This file configures Vite’s base path to use relative paths instead of absolute paths.
Although this does not affect the production environment, it is essential locally when URLs and ports may differ.
import { defineConfig } from 'vite';
export default defineConfig({
base: './',
});
Create server.ts
This file configures backend routing using Hono and sets up the Bun server.
It includes several test endpoints, some static, and some executed at runtime.
import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';
const app = new Hono();
app.get('/api/hello', (c) => {
return c.text('this is api/hello');
});
app.get('/api/backend', (c) => {
const result = 1 + 1;
return c.json({
message: "this is /api/backend",
calc: `1 + 1 = ${result}`,
value: result,
});
});
app.use('/assets/*', serveStatic({ root: './dist' }));
app.use('/*', serveStatic({ root: './dist' }));
app.get('/', serveStatic({ path: './dist/index.html' }));
const port = Number(process.env.PORT ?? 3000);
export default {
port,
fetch: app.fetch,
};
Create startup.sh
This script serves several important roles:
- Remove any node_modules folders or tar archives created by ORYX during deployment
- Install the Bun runtime
- Fully take over the ORYX build process
- Start the Bun server as the final step
#!/bin/bash
set -e
echo "===== Startup script running ====="
cd /home/site/wwwroot
echo "Cleaning up Oryx-generated node_modules..."
if [ -d /node_modules ]; then
echo "Removing /node_modules ..."
rm -rf /node_modules
fi
if [ -f node_modules.tar.gz ]; then
echo "Removing node_modules.tar.gz ..."
rm -f node_modules.tar.gz
fi
echo "Oryx cleanup complete."
export BUN_INSTALL=/home/site/wwwroot/.bun
export PATH="$BUN_INSTALL/bin:/home/site/wwwroot/node_modules/.bin:$PATH"
export NODE_PATH="/home/site/wwwroot/node_modules"
if [ ! -f "$BUN_INSTALL/bin/bun" ]; then
echo "Bun not found. Installing..."
curl -fsSL https://bun.sh/install | bash
else
echo "Bun already installed at $BUN_INSTALL"
fi
echo "Using Bun version:"
bun --version
echo "Running bun install ..."
bun install
echo "Running bun run build ..."
bun run build
echo "Starting server with bun run start ..."
bun run start
Modify src/main.ts
Simplify the default welcome page so it only displays our test text.
// src/main.ts
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
<h1>Hello Bun Hono Vite</h1>
`;
Modify package.json
Update the scripts section so that the project uses the newly created server.ts as the server entry point.
{
"name": "app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev:vite": "vite",
"build": "vite build",
"preview": "vite preview",
"start": "bun server.ts"
},
"devDependencies": {
"typescript": "~5.9.3",
"vite": "^7.2.4"
},
"dependencies": {
"hono": "^4.10.7"
}
}
Build the project locally
This command generates a dist directory containing all Vite-built static assets.
docker run --rm -it -v "$PWD":/app -w /app oven/bun:latest bun run build
Run the server locally for testing
docker run --rm -it -v "$PWD":/app -w /app -p 3000:3000 -e PORT=3000 oven/bun:latest bun run start
You can press Ctrl + C to stop the server when finished.
3. Deployment
We create a Linux Web App with a minimum SKU of Premium.
Add Environment Variables
- SCM_DO_BUILD_DURING_DEPLOYMENT=false
Purpose: Prevents the deployment environment from packaging during publish. This must also be set in the deployment environment itself. - WEBSITE_RUN_FROM_PACKAGE=false
Purpose: Instructs Azure Web App not to run the app from a prepackaged file. - ENABLE_ORYX_BUILD=false
Purpose: Prevents Azure Web App from building after deployment. All build tasks will instead execute during the startup script.
Add Startup Command
bash /home/site/wwwroot/startup.sh
After that, we can return to VS Code and deploy the project.
Once the deployment is complete, wait about five minutes for the build process to finish, and then you can begin testing.
|
/ | |
|
/api/hello | |
|
/api/backend |
4. Conclusion
Bun + Hono + Vite form a cohesive ecosystem that embodies the next era of JavaScript development: fast, compact, ergonomic, and deeply aligned with modern infrastructure needs. It is particularly well-suited for AI applications, where latency, concurrency, and rapid iteration matter more than ever. From streaming inference endpoints to vector database integrations, this stack offers the responsiveness and scalability essential for AI-powered systems.