Oct 23, 20266 min read

Best Rust Framework for Microservices in 2026

Axum, Actix Web and tonic compared for Rust services that call each other and exchange events, plus how Encore adds per-service databases, Pub/Sub and cloud infrastructure to the framework you pick.

Rust services are usually small, fast and long-running, which makes the language a good fit for the parts of a system that sit on the hot path, and the 2024 State of Rust Survey found that 38 percent of respondents now use Rust for the majority of their coding at work, up from 34 percent a year earlier.

The Rust web ecosystem has converged on Tokio, and the main framework choices for services are Axum, Actix Web and tonic for gRPC, although none of them decides how services find each other, how events travel between them, or where each service's database runs.

What to weigh when choosing:

  • Whether services talk over gRPC with Protobuf contracts or over HTTP with JSON.
  • How well the framework fits the Tower middleware ecosystem.
  • How each service gets its own database, broker and secrets locally and in production.
  • How calls are traced across service boundaries.

Rust microservice frameworks at a glance

OptionApproachBest fit
AxumRouter and extractors on Tokio, Tower and HyperMost HTTP services
Actix WebMature framework with its own middleware modelTeams that want a large built-in feature set
tonicgRPC client and server generated from ProtobufServices that share Protobuf contracts across languages
Compare features across all three options
CapabilityAxumActix Webtonic
RoleWeb frameworkWeb frameworkgRPC framework
ContractRust types with serdeRust types with serdeProtobuf
Service discoveryYou configure URLsYou configure URLsYou configure URLs
Events between servicesBring your own brokerBring your own brokerBring your own broker
Database per serviceBring your ownBring your ownBring your own
Local infrastructureYou run itYou run itYou run it
Production infrastructureYou provision itYou provision itYou provision it

Axum: the default for HTTP services

Axum

Axum handlers are async functions whose arguments are extractors, and any Tower layer works as middleware, which includes the same tracing and timeout layers tonic uses.

Key features

  • Extractors for paths, queries, JSON and shared state.
  • Tower and tower-http middleware for tracing, CORS and timeouts.
  • Can serve tonic gRPC services from the same process.

Good to know

Axum is a router, so service discovery, brokers and databases are left to you, and route path syntax changed in 0.8 from /:id to /{id}.

Try Axum →

Actix Web: mature and fast

Actix Web

Actix Web has a long production history and strong benchmark results, with attribute macros for routes and its own middleware model.

Key features

  • Extractors for JSON, forms, paths and shared web::Data state.
  • First-party crates for WebSockets, sessions and multipart.
  • Workers that each run a single-threaded Tokio runtime.

Good to know

Its middleware is not Tower, so fewer crates are shared with Axum and tonic services in the same system.

Try Actix Web →

tonic: gRPC for Rust

tonic

tonic generates clients and servers from .proto files with tonic-build and supports unary and streaming RPCs over HTTP/2.

Key features

  • Protobuf contracts shared with services in other languages.
  • Streaming in both directions, deadlines and interceptors.
  • Built on Tower and Hyper, so Axum-style middleware applies.

Good to know

Code generation adds a build step for each contract change, and browsers need gRPC-Web or a proxy to call tonic services.

Try tonic →

Encore: the infrastructure SDK you add to your framework

Encore

Encore combines each Rust service with its own infrastructure through an infrastructure SDK you add with the encore-dev crate, so Axum keeps the routing while each service declares its Postgres database, Pub/Sub topics, buckets, cron jobs and secrets as static values that Encore finds through static analysis. encore run starts Postgres locally with the topics next to the services, and a deploy provisions the same declarations in your own AWS or GCP account. Whichever option above you pick, Encore can add the databases, topics and buckets to the same services.

An orders service checks the user through the users service, writes the order and publishes an event:

use axum::{extract::Path, http::StatusCode, routing::post, Json, Router}; use encore::pubsub::Topic; use encore::storage::postgres::Database; use serde::{Deserialize, Serialize}; static DB: Database = Database::new("orders").migrations("./migrations"); static ORDER_PLACED: Topic<OrderPlaced> = Topic::new("order-placed"); #[derive(Debug, Clone, Serialize, Deserialize)] struct OrderPlaced { order_id: i64, user_id: i64, } async fn place_order(Path(user_id): Path<i64>) -> Result<Json<OrderPlaced>, StatusCode> { // Encore resolves the users service's address in every environment. let users = encore::services::base_url("users"); reqwest::get(format!("{users}/users/{user_id}")) .await .and_then(|r| r.error_for_status()) .map_err(|_| StatusCode::BAD_GATEWAY)?; let row = DB .write() .query_one("INSERT INTO orders (user_id) VALUES ($1) RETURNING id", &[&user_id]) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let event = OrderPlaced { order_id: row.get(0), user_id }; ORDER_PLACED .publish(&event) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(event)) } #[tokio::main] async fn main() { encore::initialize(); let app = Router::new().route("/orders/:user_id", post(place_order)); let addr = std::env::var("ENCORE_LISTEN_ADDR").unwrap_or("0.0.0.0:8080".into()); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app) .with_graceful_shutdown(encore::shutdown::signal()) .await .unwrap(); }
Axum serves the route, while the database, topic and the users address come from Encore. The route syntax is for Axum 0.7.

A notifications service references the topic by name and registers a subscription that starts with the service:

use encore::pubsub::{Message, Subscription, Topic}; static ORDER_PLACED: Topic<OrderPlaced> = Topic::named("order-placed"); #[encore::pubsub::register] static RECEIPTS: Subscription<OrderPlaced> = Subscription::new(&ORDER_PLACED, "send-receipt", send_receipt).max_retries(3); async fn send_receipt(msg: Message<OrderPlaced>) -> Result<(), encore::Error> { println!("sending receipt for order {}", msg.data.order_id); Ok(()) }

Key features

  • Each service declares its own database, so schemas stay inside service boundaries.
  • Calls through base_url are read from the code, which gives Encore the dependency graph between services.
  • Subscriptions take retry, backoff and concurrency options in the declaration.
  • A deploy provisions the declarations in your own AWS or GCP account.
  • An AI coding agent adding a new service writes a new crate with its statics and can run the full system with encore run before the change reaches review.

Good to know

Encore leaves HTTP and gRPC to your framework, so the comparison below still applies. When Encore deploys to your cloud, the cloud access sits with Encore's IAM role, not the agent or the developer. Rust support is in beta, and you can sign up for access.

Try Encore →

How to choose

  • Axum for most HTTP services, especially next to tonic.
  • Actix Web when you want a mature framework with more built in.
  • tonic when services share Protobuf contracts across languages.

The Twelve-Factor App advice to treat databases and brokers as attached resources, swappable through config, applies to Rust services as much as any. For calls between services, tonic runs over HTTP/2, whose streams let many requests share one connection. Encore's preview environments give each pull request its own environment with real infrastructure on paid plans. Rust support in Encore is in beta, and you can sign up for access.

Frequently asked questions

What is the best Rust framework for microservices?

Axum is the common default for HTTP services because it builds on Tokio, Tower and Hyper, and tonic is the standard choice for gRPC and shares the same Tower middleware. Actix Web is a mature alternative with its own ecosystem. Encore is not a competing framework but an infrastructure SDK you add to the framework you pick, such as Axum, giving each service its own Postgres database and Pub/Sub topics.

Should Rust microservices use gRPC or HTTP?

Use tonic and gRPC when services in several languages share Protobuf contracts or need streaming RPCs. Use Axum with JSON when the services are called from browsers or other teams prefer plain HTTP. Axum and tonic can be served from the same process.

How do I call another Rust service with Encore?

Call encore::services::base_url with the other service's name as a string literal and send the request with an HTTP client such as reqwest. Encore reads the call from the code to record the dependency, and resolves the URL under encore run and in each deployed environment.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders