Blog Post

Apps on Azure Blog
5 MIN READ

Deploying a Bun + Hono + Vite app to Azure App Service

theringe's avatar
theringe
Icon for Microsoft rankMicrosoft
Dec 05, 2025

This article will guide you through deploying a Bun + Hono + Vite application to an Azure Linux Web App.

TOC

  1. Introduction
  2. Local Environment
  3. Deployment
  4. 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:

  1. 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.
  2. 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.

Updated Dec 05, 2025
Version 1.0
No CommentsBe the first to comment