stack.menu

Rust service

Services where runtime control and explicit resource handling matter.

Axum · Tokio · SQLx · PostgreSQL

Complete guide (Markdown) · Structured record (JSON)

Original source review: 2026-10-10. Core guidance rechecked: 2026-10-13. Check scope. Dependency combinations and setup need project-specific qualification.

Use cases

Choose it when

Consider another path when

Working practices

Tools by job

HTTP server: Axum + Tokio + tower-http

Typed extractors/state and Tower middleware compose with Tokio networking.

Configure limits, timeout/error mapping and graceful shutdown. Blocking work must not monopolize async workers.

Database and migrations: PostgreSQL + SQLx; sqlx-cli migrations

SQL-oriented async access with optional checked query macros and migration tooling.

Select runtime/TLS/database features for the resolved release. Checked macros need a database or maintained offline metadata; runtime queries are not compile-time checked.

Configuration: Serde-backed settings + config

Typed settings plus structured events/spans.

Set precedence explicitly; validate semantics; install a tracing subscriber; never log secrets.

Application logging: tracing + configured subscriber

Typed settings plus structured events/spans.

Set precedence explicitly; validate semantics; install a tracing subscriber; never log secrets.

Tests and static checks: cargo test; rustfmt; Clippy; cargo audit

Compiler, style, behavior and known-advisory checks complement each other.

Audit coverage is advisory-driven; compile success does not establish behavior or security. Test supported feature combinations rather than blindly enabling incompatible features.

Durable work: None for request-only service; Restate Rust SDK when durable workflows are required

Durable handlers and persisted workflow progress address work outliving a process.

Requires a Restate service and adoption test. Journal external operations correctly; idempotency and business transactions remain explicit. Tokio tasks alone are not durable.

Deployment: cargo build --release --locked in a target-specific build pipeline

Reproducible dependency resolution from committed lockfile; native executable artifact.

Not universally static: verify libc, TLS/native libraries, target support, certificates and container runtime needs.

API token verification: jsonwebtoken + selected identity provider contract

Typed claims and signature checks fit JWT-bearing service APIs

Configure accepted algorithms, issuer/audience, expiry and trusted key rotation; select required crypto features for resolved release. This is a verifier, not an OIDC login/session solution; opaque tokens need provider introspection.

Transactional email: lettre + project-selected SMTP provider

Typed mail construction and SMTP transport support application messages

Configure TLS, sender credentials and timeouts; send required mail through durable jobs with duplicate protection. Validate chosen async/runtime features in Rust; delivery receipts and bounces need provider handling.

Uploads and object storage: AWS SDK for Rust S3 client

Durable object storage avoids dependence on service-local disk

Configure runtime credentials, region and least-privilege bucket access; authorize object keys and uploads, bound size/concurrency and define retention. Test multipart cleanup and selected provider compatibility.

Realtime transport: Axum WebSocket extractor

WebSocket support fits bidirectional connected clients

Enable the documented module/feature; authorize handshake and every protected message, bound queues/message size and implement heartbeat/shutdown. Socket delivery is not durable history; define reconnect/resync.

Build with the stack’s strengths

Architecture and developer workflow.

Recommendation: Axum/Tower owns request extraction and middleware; domain functions return explicit errors; Tokio drives async I/O; SQLx pools and transactions own database access. Track child tasks and cancellation separately from persisted jobs. A Tokio channel coordinates tasks inside one live process and cannot recover accepted work after a crash.

Version scope: Reviewed docs expose Axum 0.8.9, Tokio 1.53.2 and SQLx 0.9.0 on 11 October 2026; Rust std docs show 1.99.0. These are documentation scopes, not a qualified composition or an automatic upgrade recommendation. Record rust-toolchain.toml, Cargo.lock, enabled features, SQLx CLI compatibility and database version before implementation. · Reviewed 2026-10-11

Architecture boundaries

Tools and feedback

Translations to avoid

For an existing application

Keep the contracts while you change the stack.

These are documented options and proposed checks. Inspect the actual source application before adopting a transition.

Data and schema

Inspect the source

Capture the old SQL schema, transactions, tenant filters, nullable values and database engine behavior.

During coexistence

Recommendation: keep existing schema ownership and backward-compatible columns; run SQLx reads for a selected boundary before transferring its writer. Generate metadata against exactly the reviewed migration state.

Target design

Recommendation: SQLx pooled access with transaction-scoped domain changes; query! checks literals against a live build schema or checked-in .sqlx metadata. Keep database engine choice independent of the Rust rewrite.

Rollback

Recommendation: route writes back while retaining additive schema and a previous compatible binary. Delay destructive migrations; reconcile new-only persisted values before rollback.

Benefits and prerequisites

Compile-time query/type checks can expose SQL/API mismatches before deployment.
Requires: Query macros/features and metadata match the chosen engine and schema; nullable results and dynamic queries receive runtime checks.
Tradeoffs: Dynamic SQL is outside macro checking; stale offline metadata can compile; schema ownership and deployment order still need controls.

Checks before transfer

  • Run an offline locked build with the reviewed metadata and a live prepare --check against the migrated fixture DB.
  • Verify both runtimes read each other's writes, including null/time/ID mappings.
  • Cancel during a transaction and confirm the intended committed/rolled-back state plus pool recovery.
Jobs and schedules

Inspect the source

Preserve old broker serialization, acknowledgement, delayed/retry states, concurrency and effect IDs. Durable Redis workers cannot be replaced transparently by tokio::spawn.

During coexistence

Recommendation: Rust commits an outbox intent in its domain transaction; a replay-safe relay submits to the retained queue/worker interface. Existing workers retain queue ownership while new job kinds are qualified. This outbox design is inferred, not a shipped Axum feature.

Target design

Recommendation: keep proven durable workers until a chosen Rust queue implementation is qualified for retries, scheduling, leases and crashes. River current Ruby docs describe a shared schema with Rust; that statement alone does not qualify a Rust worker API or migration.

Rollback

Recommendation: stop the new producer, let accepted outbox entries settle and retain the old worker. Roll back traffic only after ambiguous effects are reconciled; avoid consuming a queue with incompatible handlers.

Benefits and prerequisites

A retained worker boundary allows the HTTP layer to change without dropping backlog or broker guarantees.
Requires: Same-transaction outbox intent; stable relay/effect IDs; explicit payload translator; observed delivery and backlog ownership.
Tradeoffs: Two runtimes and a relay add operations; delivery may repeat; a SQLx transaction alone does not atomically submit to Redis.

Checks before transfer

  • Crash after the domain commit but before relay delivery; an intent remains recoverable.
  • Crash the relay after submission before marking sent; duplicate delivery produces one logical effect.
  • Reconcile ready, scheduled, retry, failed and in-flight old jobs before any worker replacement.
Delivery and recovery

Inspect the source

Define HTTP admission/error behavior, task ownership, shutdown deadline and behavior for unavoidable CPU/blocking operations.

During coexistence

Recommendation: retain old routing while introducing Tower admission bounds and CancellationToken/TaskTracker-based ownership for a coherent endpoint cohort. Benchmark equivalent resource limits and durability before attributing gains to Rust.

Target design

Recommendation: bounded async work, cooperative cancellation, explicit shutdown notification plus tracked task completion, and SQLx pool close. For untrusted/hard-deadline native work, isolate it in a process whose termination contract is tested.

Rollback

Recommendation: return the cohort to the previous service and reconcile persisted outcomes. Do not simply retry every cancelled request; its external effect may already have occurred.

Benefits and prerequisites

Tracked tasks and explicit admission limits make resource use and planned termination easier to reason about.
Requires: Every child task is tracked; limits include waiting work; cancellation-safe I/O and shutdown budget are verified.
Tradeoffs: spawn_blocking closures may continue after timeout; killing a process still requires durable recovery and external-effect reconciliation.

Checks before transfer

  • Cancel an overloaded request and verify queue permits/resources are released.
  • Send termination during async and blocking work; document tasks that drain and work that cannot be aborted.
  • Force a crash after acceptance; verify recovery of persisted intents and honest failure of memory-only work.
Realtime transport

Inspect the source

Preserve subscriptions, authorization revocation, event versions, reconnect behavior and cross-instance fan-out.

During coexistence

Recommendation: keep the existing shared broker while a Rust gateway serves entire room/tenant cohorts; adapters translate existing messages without dual-emitting user effects. Test cross-runtime publication in both directions.

Target design

Recommendation: Axum WebSocket transport and bounded per-client queues. Tokio broadcast is appropriate only for local ephemeral fan-out with explicit Lagged handling; use durable state/event history to resync clients when loss is unacceptable.

Rollback

Recommendation: route the whole cohort back and retain the shared broker/protocol. Resynchronize from authoritative durable records; local channel state cannot be restored from a previous binary.

Benefits and prerequisites

Local bounded fan-out can prevent slow receivers from retaining unbounded memory.
Requires: Overflow/disconnect/resync policy exists; replica routing and shared fan-out remain covered; every broadcast error is handled.
Tradeoffs: Tokio broadcast is process-local; old messages are evicted when capacity is exceeded; it is not a durable cross-node broker.

Checks before transfer

  • Force receiver lag and verify Lagged triggers resync or the documented disconnect policy.
  • Publish from each runtime and confirm the opposite runtime's subscribers receive the intended event.
  • Reconnect after deployment/crash and rebuild state without trusting missing ephemeral frames.

Read the migration workflow →

Setup

  1. Create source in a local development workspace: cargo new service; enter service and commit the generated application Cargo.lock after dependency resolution.
  2. Run cargo add axum; cargo add tokio --features macros,rt-multi-thread; add required tower-http middleware after selecting compatible releases.
  3. Add SQLx selecting PostgreSQL, Tokio and a supported TLS configuration from that release; install matching sqlx-cli. Create config validation and tracing subscriber.
  4. Use a disposable database in DATABASE_URL. Create migrations with sqlx migrate add initial; write SQL and run sqlx migrate run.
  5. For checked query macros, run cargo sqlx prepare after schema/query changes and commit offline metadata. Implement HTTP, real-database and cancellation tests.
  6. Introduce Restate only after a separate restart/retry/idempotency proof. Pin toolchain and release targets. These instructions were not executed.

Verification

Run these in the configured application; they are instructions, not recorded passes.

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
cargo audit
sqlx migrate info
cargo sqlx prepare --check
cargo build --release --locked

Open questions

Sources