
Hono is often associated with Cloudflare Workers, and for good reason. Its small routing model, Web Standard API surface and direct fetch integration make it a natural fit for edge runtimes. That association can obscure a more useful fact: Hono is not tied to Cloudflare. The same application core can run on Azure when the runtime-specific edge is kept separate from routes and business logic.
Azure offers several valid ways to host a Hono application. Azure Container Apps is usually the strongest default for a new HTTP API. Azure Functions is compelling when HTTP endpoints belong next to queue, timer or event handlers. App Service remains a practical managed host for conventional Node.js web applications, while Azure Kubernetes Service is appropriate when Kubernetes is already an intentional platform decision.
The important distinction is not whether Hono can technically start on each service. It is where responsibility for scaling, networking, identity, deployment artifacts and long-running workloads belongs. A small webhook receiver, an API with private data dependencies and a service inside an existing Kubernetes platform have materially different operational needs.
What Hono is
Hono is a lightweight web framework for TypeScript and JavaScript. It provides routing, middleware, response helpers and a strongly typed application model without imposing a specific server runtime. Its central interface is based on the web platform: a request enters through a Fetch-style handler and the application produces a Response.
That design is what makes Hono portable. Routes, middleware, validation and HTTP-level authentication can remain the same while a small adapter decides whether the application is running in a Cloudflare Worker, an Azure Function or a Node.js process inside a container.
Portability does not mean that every dependency works everywhere. A route that directly imports Node.js file system APIs, opens TCP sockets or depends on a Cloudflare-specific binding is coupled to that runtime. The useful boundary is therefore clear: keep the Hono application core focused on HTTP and domain logic, and place infrastructure access behind narrow abstractions.
The following application contains no Azure-specific code:
1// src/app.ts
2import { Hono, type Context } from 'hono'
3import { cors } from 'hono/cors'
4import { secureHeaders } from 'hono/secure-headers'
5
6type HealthResponse = {
7 status: 'ok'
8}
9
10type GreetingResponse = {
11 message: string
12}
13
14const healthResponse: HealthResponse = {
15 status: 'ok',
16}
17
18const allowedOrigins: string[] = ['https://portal.example.com']
19const allowedMethods: string[] = ['GET', 'POST']
20
21export const app: Hono = new Hono()
22
23app.use('*', secureHeaders())
24app.use(
25 '/api/*',
26 cors({
27 origin: allowedOrigins,
28 allowMethods: allowedMethods,
29 }),
30)
31
32app.get('/health', (context: Context) => {
33 return context.json(healthResponse)
34})
35
36app.get('/api/greeting/:name', (context: Context) => {
37 const name: string = context.req.param('name')
38 const response: GreetingResponse = {
39 message: `Hello ${name}`,
40 }
41
42 return context.json(response)
43})
The application can be tested through app.request(...) without starting a local HTTP server or provisioning an Azure resource. That is an important architectural benefit. The host becomes a thin composition concern instead of a prerequisite for testing every route.
The CORS rule is intentionally restrictive. A wildcard origin can be valid for a fully public, unauthenticated API, but it is rarely a safe default for browser clients that use credentials. Likewise, forwarded request headers such as X-Forwarded-For should not become an identity or authorization mechanism merely because the application runs behind a platform proxy.
The Azure hosting decision
For Hono applications, four Azure services cover most scenarios:
| Hosting option | Strong fit | Runtime model | Main trade-off |
|---|---|---|---|
| Azure Container Apps | HTTP APIs, containers, workers and event-driven scale rules | Serverless containers | Image build and container supply chain become part of delivery |
| Azure Functions | Webhooks, queues, timers and event-triggered workloads | Functions and triggers | Hono requires an additional community adapter |
| Azure App Service | Long-running Node.js web applications and established PaaS workloads | Managed web application | Less flexible scale and container model than Container Apps |
| Azure Kubernetes Service | Services on an existing Kubernetes platform | Kubernetes | Highest operational responsibility |
There is no universal winner. The right choice follows the operating model around the code. Still, for a new API without an existing App Service or Kubernetes platform, Azure Container Apps usually offers the cleanest balance. It runs a conventional Node.js process in an OCI container while providing ingress, revisions, secrets, managed identity and event-driven scaling.
Azure Container Apps: the practical default
Azure Container Apps is a strong host for Hono when the API should run as a normal Node.js process. It is especially useful for services that need standard npm packages, WebSocket support, background work, predictable container images or integration with Azure networking and managed identities.
The runtime adapter is small. The application reads the port supplied by the host and passes app.fetch to the Hono Node.js server.
1// src/index.ts
2import { serve } from '@hono/node-server'
3import { app } from './app.js'
4
5const configuredPort: string | undefined = process.env.PORT
6const port: number = configuredPort === undefined
7 ? 3000
8 : Number.parseInt(configuredPort, 10)
9
10if (!Number.isInteger(port) || port < 1 || port > 65535) {
11 throw new Error('PORT must be a valid TCP port.')
12}
13
14serve({
15 fetch: app.fetch,
16 port,
17})
The runtime and build dependencies can be installed normally:
1npm install hono @hono/node-server
2npm install --save-dev typescript
The relevant scripts remain deliberately simple:
1{
2 "type": "module",
3 "scripts": {
4 "build": "tsc -p tsconfig.json",
5 "start": "node dist/index.js"
6 }
7}
A multi-stage Docker build keeps compilers and development dependencies out of the production image.
1FROM node:22-alpine AS build
2
3WORKDIR /app
4COPY package.json package-lock.json ./
5RUN npm ci
6
7COPY tsconfig.json ./
8COPY src ./src
9RUN npm run build && npm prune --omit=dev
10
11FROM node:22-alpine AS runtime
12
13WORKDIR /app
14ENV NODE_ENV=production
15
16COPY --from=build /app/package.json ./
17COPY --from=build /app/node_modules ./node_modules
18COPY --from=build /app/dist ./dist
19
20USER node
21EXPOSE 3000
22CMD ["node", "dist/index.js"]
After the image has been pushed to an accessible registry and the Container Apps environment has been created, the application can be exposed with an HTTP scale rule:
1az containerapp create `
2 --name hono-api `
3 --resource-group rg-hono-prod `
4 --environment cae-hono-prod `
5 --image acrhono.azurecr.io/hono-api:1.0.0 `
6 --target-port 3000 `
7 --ingress external `
8 --min-replicas 0 `
9 --max-replicas 10 `
10 --scale-rule-name http `
11 --scale-rule-type http `
12 --scale-rule-http-concurrency 50
The concurrency value is not a generic performance recommendation. It must reflect request latency, memory consumption and downstream capacity. A route that holds a scarce database connection for every request can overload the database long before Container Apps reaches its replica limit. The goal is controlled end-to-end throughput, not the largest possible number of concurrent requests.
Container Apps can scale HTTP workloads down to zero replicas. That is cost-efficient for intermittent traffic, but it can add latency to the first request after an idle period. Latency-sensitive public APIs often justify minReplicas: 1, while internal tools and infrequent webhooks are usually good candidates for zero-scale operation.
Why Container Apps works well
- The full Node.js process and standard npm ecosystem remain available.
- HTTP, TCP and event-driven scaling can use the same container artifact.
- Revisions allow controlled rollouts and simple rollback paths.
- Managed Identity, Key Vault, Azure Monitor and private networking fit naturally into Azure-centric systems.
- The service model is much smaller than operating a Kubernetes cluster.
Where Container Apps is less attractive
- A container image must be built, versioned, scanned and promoted through delivery environments.
- Cold-start latency needs to be accepted or offset with a minimum replica count.
- A simple, occasional webhook can be more infrastructure than a Function requires.
- Globally distributed edge execution close to every end user is not the primary model.
Production readiness still belongs in the application and delivery process. A health endpoint, structured logs, request size limits, cancellation-aware downstream calls and well-defined timeouts are baseline concerns. Secrets do not belong in Dockerfiles, committed configuration files or plaintext pipeline variables. Managed Identity with narrowly scoped access to Key Vault, storage or data services is generally the more durable Azure design.
Azure Functions: when the trigger is the center of the design
Azure Functions is not only an HTTP platform. Queue, timer and event handlers are first-class parts of its model. That makes it a good fit when a Hono API receives webhooks while the same system also processes messages, reacts to events or runs scheduled work.
Hono can run in Azure Functions through @marplex/hono-azurefunc-adapter. The adapter connects the Hono Fetch handler to an HTTP-triggered function. The current Node.js v4 programming model keeps the function definition in TypeScript or JavaScript code instead of requiring separate function.json files.
1// src/functions/http.ts
2import { app as functionsApp } from '@azure/functions'
3import { azureHonoHandler } from '@marplex/hono-azurefunc-adapter'
4import { app } from '../app.js'
5
6functionsApp.http('hono-api', {
7 methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
8 authLevel: 'anonymous',
9 route: '{*proxy}',
10 handler: azureHonoHandler(app.fetch),
11})
Azure Functions prefixes HTTP routes with /api by default. When Hono owns the complete public route tree, removing the prefix makes the external contract easier to reason about:
1{
2 "version": "2.0",
3 "extensions": {
4 "http": {
5 "routePrefix": ""
6 }
7 }
8}
The project needs Hono, the Azure Functions SDK and the adapter as runtime dependencies:
1npm install hono @azure/functions @marplex/hono-azurefunc-adapter
The Functions v4 runtime discovers the compiled function files through the main entry in package.json:
1{
2 "main": "dist/src/functions/*.js"
3}
The adapter is a deliberate dependency decision. It is neither part of the Hono core nor the Azure Functions SDK, so a production evaluation should include its maintenance state, compatible version range, streaming behavior and test coverage. A direct @azure/functions HTTP handler may be the more conservative option when long-term platform support matters more than reusing a Hono router.
Why Azure Functions works well
- HTTP endpoints, timers, queues and event handlers share one platform model.
- Intermittent and event-driven workloads can avoid paying for idle application capacity.
- Bindings simplify common integrations with services such as storage queues and Event Grid.
- The Hono application core remains independently testable and can still run on another host.
Where Azure Functions is less attractive
- Hono introduces an additional adapter layer.
- The Functions route prefix and Hono catch-all route must be configured together.
- Cold starts, trigger limits and host lifecycle behavior need realistic load validation.
- A large, continuously busy HTTP API is often easier to understand as a normal container process.
authLevel: 'anonymous' does not make an endpoint secure by itself. It is valid for public endpoints or when authentication is enforced by API Management, Front Door or Hono middleware, but it should not become an accidental default for internal APIs. Function keys are useful for simple access control, but they are not a substitute for application or user identity.
Azure App Service: the conventional Node.js PaaS option
Azure App Service remains appropriate for a long-running Node.js web application, especially when the surrounding organization already uses App Service plans, deployment slots, VNET integration and established deployment processes. The src/index.ts process from the Container Apps example can run unchanged. App Service starts the configured start script and provides the HTTP host configuration around it.
Code deployment avoids the requirement to maintain a Dockerfile. A dependency lockfile is still essential. With build automation enabled, App Service installs production npm dependencies during Git or ZIP deployment. A file transfer deployment without build automation has different behavior and requires the dependencies to be deployed explicitly. That distinction is easy to miss in an otherwise successful release pipeline.
Why App Service works well
- It is a mature web PaaS with deployment slots, domains, TLS and familiar diagnostics.
- A custom container is optional rather than mandatory.
- It is well suited to services that intentionally keep at least one instance running.
- Existing App Service plans and operational standards are strong reasons to stay on the platform.
Where App Service is less attractive
- It is less flexible than Container Apps for custom containers, sidecars and event-driven scale rules.
- Scale to zero and KEDA-based event scaling are not central to the service model.
- Capacity and cost are often decided at the App Service plan level rather than per isolated workload.
- For a new containerized API, Container Apps is commonly the clearer runtime model.
App Service is not an outdated fallback. It is a deliberate choice for conventional web workloads. An established plan, working slot strategy and centrally managed networking can outweigh the theoretical appeal of moving every new Node.js service to containers.
Azure Kubernetes Service: use it when Kubernetes is already justified
Hono has no special requirement for Azure Kubernetes Service. The container image used for Container Apps can run unchanged in a Kubernetes Deployment. The meaningful question is therefore not compatibility but platform intent.
AKS fits organizations that already operate multiple services through Kubernetes, with an ingress controller, GitOps, network policies, workload identities, platform ownership and a supported observability model. In that setting, a Hono API is simply another well-contained service.
For one new API, AKS can create far more surface area than it removes. Cluster upgrades, node pools, ingress, certificates, autoscaling, policies, monitoring and cost control all need a clear operating model. Hono makes the service code smaller; it does not reduce the responsibilities of running Kubernetes.
Why AKS works well
- It provides the greatest control over scheduling, networking, ingress, policies and extensions.
- It creates a common platform for many containerized services.
- It imposes no Azure web-host or adapter-specific application model.
Where AKS is less attractive
- Operational effort is materially higher than with Container Apps or App Service.
- Cluster security, updates and cost optimization need explicit ownership.
- It is usually too much platform for a small team or a single API.
AKS should follow from a platform decision, not from the choice of Hono as a web framework.
How Azure compares with Cloudflare Workers
Hono feels particularly direct on Cloudflare Workers because Workers already expects a Fetch handler. A minimal Worker can export app or app.fetch directly. On Azure, the same interface is bound to the platform through the Node.js server or the Azure Functions adapter.
The reusable part remains substantial: routes, middleware, Request and Response handling, JSON contracts and most validation logic can remain unchanged. Infrastructure should stop at that boundary. An OrderRepository, EmailSender or FeatureFlags interface can have a Cloudflare implementation using platform bindings and an Azure implementation using the appropriate Azure service.
| Concern | Cloudflare Workers | Azure Container Apps and Functions |
|---|---|---|
| Primary runtime model | Edge isolates with a Fetch handler | Container process or event-triggered function |
| Location model | Execution across the Cloudflare network | Azure region selection; global patterns use additional Azure services when required |
| Node.js compatibility | Selected Node.js APIs and compatibility polyfills | Full Node.js process in Container Apps and App Service; Functions follows its host model |
| Resource model | Explicit CPU, memory, connection and subrequest limits | Container resources or Functions limits, scaling rules and downstream service capacity |
| Identity and data integration | Cloudflare bindings and developer-platform services | Managed Identity, Key Vault, VNET and Azure data services |
| Typical strength | Edge APIs, caching, global latency-sensitive traffic | APIs close to Azure data, private networks, containers and event-driven workloads |
Cloudflare Workers does not provide a complete Node.js runtime. The nodejs_compat flag expands the available surface, but some modules are polyfilled and unsupported methods can fail at runtime. A portable Hono core should therefore not import Node-specific packages that only happen to work in the Azure container host.
Workers are also not simply too short-lived for serious APIs. HTTP requests can continue while the client remains connected. The relevant limits are CPU time, memory, subrequests and the isolate model. A Node.js container offers a more familiar runtime, the full Node package ecosystem and deeper Azure network integration. Workers offers execution closer to the network edge with a tighter resource model. Neither is universally better; they favor different application boundaries.
A practical selection rule
For a new Hono application, the decision usually becomes clear when the workload is described precisely:
- Azure Container Apps is the default for a containerized HTTP API that uses Azure services or private networking.
- Azure Functions is stronger when queue, event or timer triggers matter as much as HTTP and the adapter is an accepted dependency.
- Azure App Service fits an established App Service platform or a conventional long-running web application.
- Azure Kubernetes Service belongs to an existing Kubernetes platform with explicit operational ownership.
- Cloudflare Workers can be the better choice when globally distributed edge latency, caching and the Cloudflare platform itself are the central requirements.
The decision should not be driven by a router throughput benchmark. In a real API, database access, authentication, network paths, cache behavior and downstream rate limits generally dominate latency and capacity. A production-like load test with measured service-level objectives is more useful than a synthetic requests-per-second result.
Conclusion
Hono runs well on Azure when the application core is separated from its host. app.fetch is the useful seam: Azure Container Apps and App Service attach it to a Node.js server, while Azure Functions attaches it through an adapter. That keeps routes and middleware reusable without pretending that all runtimes have identical constraints.
For most new Hono APIs on Azure, Azure Container Apps is the balanced choice. It combines an ordinary Node.js container process with HTTP scaling, revisions and Azure-native integration. Azure Functions complements that model for trigger-centered workloads, App Service remains a strong conventional web host and AKS belongs to established Kubernetes platforms.
Cloudflare Workers and Azure are not interchangeable labels for the same deployment. Hono can preserve the same HTTP core across both, but edge isolates, Node.js containers, data access, identity and operational limits remain architecture decisions. Keeping those decisions outside the Hono application core is what makes the code portable without making the system vague.
Related articles

Jul 10, 2026 - 11 min read
Top 10 recommendations for .NET developers working with Azure Cosmos DB
Azure Cosmos DB has a habit of exposing architectural weaknesses very early. Classic data-access patterns that work perfectly fine with …

Jun 24, 2026 - 14 min read
Local Aspire Development with Azure Cosmos DB and the Preview Emulator
Distributed applications tend to feel straightforward until a real cloud dependency enters the picture. Azure Cosmos DB is a good example. …

May 06, 2026 - 13 min read
Azure Static Web Apps with GitHub Actions: Pull Request Preview Environments, automatic teardown and stable production URLs
Azure Static Web Apps has one feature that is easy to underestimate until a team starts using it seriously: pull requests can behave like …
Let's Work Together
Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.
