Ever wondered what is parallel about Axum?
Axum is often described as a "fast" or "high-performance" Rust web framework. That description frequently leads to an implicit assumption: that Axum is somehow parallel by default.
That assumption is understandable, but it is also slightly misleading.
To understand what is actually parallel about Axum, you have to separate async, concurrency, and parallelism, and understand where Axum fits into that picture.
Axum itself is not parallel
Axum does not execute your request handlers in parallel by itself.
Axum is a request routing and extraction framework. Its responsibility is to map incoming HTTP requests to handler functions, extract parameters, and compose middleware. It does not schedule threads, it does not spawn workers, and it does not decide how code runs concurrently.
If you stripped Axum down to its core, what you would have is a collection of async functions and traits. Nothing parallel happens there.
So where does the parallelism come from?
The executor is where concurrency starts
Axum runs on top of an async runtime, typically Tokio. The runtime provides:
- An executor that polls futures
- A task scheduler
- A thread pool (in the multi-threaded runtime)
When a request arrives, the runtime schedules the corresponding future. Each request handler runs as its own async task.
This is the first important distinction: Axum gives you concurrent request handling, not parallel execution by default.
Each request is handled independently, and progress is interleaved at await points. That allows thousands of requests to be in flight at the same time, even if only a few threads are available.
When requests actually run in parallel
Parallelism only happens when multiple executor threads are available.
In Tokio's multi-threaded runtime, tasks can be polled on different OS threads. If two requests are both ready to run at the same time, they may execute simultaneously on different cores.
That is real parallelism, but it is provided by the runtime, not Axum.
If you run Axum on a single-threaded runtime, all request handlers still work correctly, but none of them execute in parallel. They are simply interleaved cooperatively.
async does not mean parallel
A common misconception is that writing an async handler automatically makes it parallel.
It does not.
Consider this handler:
async fn handler() {
do_cpu_heavy_work();
}
This handler is async, but it never yields. If do_cpu_heavy_work runs for 200 ms, it blocks the executor thread for 200 ms. No other tasks make progress during that time on that thread.
Async code becomes cooperative only at await points. Parallelism only appears when there are multiple executor threads and runnable tasks.
Where Axum scales well
Axum scales well in IO-bound workloads:
- Database calls
- Network calls
- File IO
- Timers
At every await, the current task yields. While it waits, other request tasks can run. That is why Axum can handle large numbers of concurrent requests efficiently without spawning a thread per request.
This is concurrency, not guaranteed parallelism.
When you must be explicit about parallelism
If you want true parallel execution inside a handler, you must opt into it explicitly.
Examples include:
- Spawning independent async tasks
- Using
tokio::join!to poll multiple futures concurrently - Offloading CPU-bound work to a blocking thread pool
Axum does not do this for you, because doing so implicitly would break correctness, error handling, and backpressure.
The correct mental model
A precise and safe mental model is this:
- Axum provides structured, concurrent request handling.
- The async runtime provides scheduling.
- Parallelism only happens when multiple threads are available and runnable tasks exist.
Or more succinctly: Axum is concurrent by design; parallelism is a runtime and workload property.
Why this design is a good thing
By not hiding concurrency behind abstractions, Axum forces you to be explicit about:
- Which operations must complete before a response is sent
- Which work is independent
- Where CPU-bound work belongs
This leads to systems that are easier to reason about, easier to test, and less likely to fail under load in unpredictable ways.
Final takeaway
If you ever wonder "what is parallel about Axum?", the honest answer is: Nothing by itself.
Axum composes async tasks. The runtime schedules them. The hardware executes them in parallel when possible.
Understanding that separation is the key to writing fast, correct, and predictable Axum applications.