Moonbeam
A single-threaded-first async HTTP/1.1 server written in Rust.
Moonbeam is designed to be simple, efficient, and free of synchronization overhead by running on a single thread. It leverages the async-io and smol ecosystem to handle concurrent connections asynchronously. By default, it uses a "share-nothing" architecture, avoiding the need for Arc, Mutex, or Send/Sync bounds on your state, though it can easily be extended to multiple threads if desired.
Motivation
Modern web applications often spend most of their time waiting on I/O (databases, network requests, etc.) rather than performing heavy CPU computation. Moonbeam embraces this by running your application logic on a single thread, utilizing a local executor. This means you can use simple RefCell and Cell primitives for state management, drastically reducing the cognitive overhead and boilerplate often associated with multi-threaded Rust web frameworks.
Critical Considerations
Before building with Moonbeam, it's essential to understand its execution model:
- No Tokio: Moonbeam is built on
async-ioand thesmolecosystem. It does not usetokiodependencies. This means notokio::spawn, no#[tokio::main], and no tokio-specific database drivers (unless they supportasync-ioorsmol). - Blocking I/O: Because Moonbeam runs handlers on a
LocalExecutoron the main thread, any CPU-heavy computation or blocking I/O (like reading a large file synchronously) will block the entire server.- Solution:
smolsupports async I/O via theblocking::unblockprimitive for offloading heavy tasks to a background thread pool, or you can use theasync_iocrate for native non-blocking operations.
- Solution:
- Static Lifetimes & State: To satisfy the executor's requirements, the server instance and its state are typically leaked to the
'staticlifetime usingBox::leak(which is whatmoonbeam::servedoes internally). This is a safe and common pattern for long-lived server processes, and Moonbeam offers a function (moonbeam::Server::destroy) to handle dropping the state object when it is no longer needed.
Features
- Single-threaded by default: No
ArcorMutexneeded for shared state. - Multi-threaded support: The
mtfeature spawns worker threads, each with its own state copy. - Simple API: Use the
#[server]macro to turn functions into server handlers. - Routing: The
router!macro provides a clean DSL and efficient implementation for nested groups, middleware, path parameters, and wildcards. - Typed Body Extractors: Use
FromRequestandFromBodytraits for zero-copy, asynchronous body parsing (e.g., JSON). - Static Assets: Built-in
assetshelper for serving files with ETags and MIME type detection. - HTTP/1.1: Persistent connections, chunked transfer encoding, and standard header parsing.
- Zero-cost extractions: Efficient parsing of Cookies, Query Parameters, and Bodies.
- Panic Handling: Optional
catchpanicfeature safely catches panics and returns a 500 error. - Response Compression: On-the-fly
compresssupport (Gzip, Brotli, Zlib). - Graceful Shutdown: Intercepts
signalsfor clean exit.
Is it fast?
Yes. Moonbeam is designed for high performance with minimal overhead. In simple benchmarks using wrk (4 threads, 100 connections, 5 seconds), Moonbeam shows competitive performance for both simple responses and static file serving.
The below benchmarks were performed on a MacBook Pro (M3 Pro). While these simple tests don't represent real-world application complexity, they demonstrate the efficiency of Moonbeam's core request/response loop.
Hello World (Plain Text)
| Framework | Architecture | Requests/sec |
|---|---|---|
| Moonbeam | Multi-Threaded (4 cores) | ~214,000 |
| Moonbeam | Single-Threaded | ~211,000 |
| Node.js | Single-Threaded | ~117,000 |
| Rouille | Thread-per-connection | ~93,000 |
Static File Serving (4KB file)
| Framework | Architecture | Requests/sec |
|---|---|---|
| Moonbeam | Multi-Threaded (4 cores) | ~73,000 |
| Moonbeam | Single-Threaded | ~66,000 |
| Rouille | Thread-per-connection | ~56,000 |
| Node.js | Single-Threaded | ~51,000 |
Installation
Add moonbeam to your Cargo.toml:
[]
= "0.5"
Feature Flags
Moonbeam is configurable via Cargo features. Most users will want the default features.
default: Enablesmacros,assets,catchpanic,signals, androuter.macros: Enables the#[server]attribute macro to easily createServertrait implementations.assets: Exposes themoonbeam::assetsmodule for serving static files.signals: Hooks into OS signals (SIGINT, SIGTERM) to trigger graceful server shutdown.catchpanic: Wraps your handlers to catch panics gracefully and return500 Internal Server Error.tracing: Instruments the core server loop withtracingspans and events.compress: Enables automatic response compression. (Depends onflate2andbrotli).router: Enables the routing macros (#[route],#[middleware], androuter!).mt: Exposesserve_multito run multiple independent server isolates across available CPU cores.
Configuration
Moonbeam honors the following environment variables:
MOONBEAM_MAX_BODY_SIZE: Maximum size (in Kilobytes) of an incoming HTTP request body. Defaults to1024(1MB). Exceeding this returns a413 Content Too Large.
Examples
1. Stateless Server
The simplest way to use Moonbeam.
use ;
async
2. Stateful Server (Interior Mutability)
Because the executor runs locally, you can use std::cell::Cell without Mutex.
use Cell;
use ;
async
3. Multi-threaded "Share-Nothing" Server
Use the mt feature flag to scale across multiple CPU cores.
use ;
use ;
async
4. Advanced Routing
The router! macro provides a clean domain-specific language for nesting routes and middleware.
use ;
use PathParams;
// Global Middleware
async
// Scoped Middleware
async
// Extractor Handler
async
async
5. JSON Parsing (Typed Body Extraction)
Use the moonbeam-serde crate for flexible, typed body extraction. This supports zero-copy deserialization by borrowing directly from the request buffer.
use ;
use Json;
use ;
async
6. HTML Forms (URL-Encoded and Multipart)
Use the moonbeam-forms crate to parse incoming form data, including file uploads.
use ;
use ;
async
Serving Static Files
use ;
async
License
This project is licensed under the MIT License.