acton-service
Production-grade Rust microservice framework with type-enforced API versioning
Build microservices that can't ship unversioned APIs. The compiler won't let you.
What is this?
Most microservice frameworks make API versioning optional. You should version your APIs, they say. But when deadlines loom, versioning gets skipped. Six months later, you're maintaining breaking changes in production.
acton-service uses Rust's type system to make unversioned APIs impossible. Your service won't compile without proper versioning. It's opinionated, batteries-included, and designed for teams shipping to production.
Current Status: acton-service is under active development. Core features (HTTP, versioning, health checks, observability) are production-ready. Some advanced features are in progress.
Quick Start
use *;
async
Try to create an unversioned route? Won't compile.
// ❌ This won't compile
let app = new.route;
new.with_routes.build;
// ^^^ expected VersionedRoutes, found Router
Installation
Add to your Cargo.toml:
[]
= { = "0.2", = ["http", "observability"] }
= { = "1", = ["full"] }
Or use the CLI to scaffold a complete service:
&&
Why acton-service?
The Problem
Building production microservices requires solving the same problems over and over:
- API Versioning: Most frameworks make it optional. Teams skip it until it's too late.
- Health Checks: Every orchestrator needs them. Every team implements them differently.
- Observability: Tracing, metrics, and logging should be standard, not afterthoughts.
- Configuration: Environment-based config that doesn't require boilerplate.
- Dual Protocols: HTTP and gRPC on the same port (modern K8s deployments need both).
The Solution
acton-service provides:
- Type-enforced versioning - The compiler prevents unversioned APIs ✅
- Automatic health endpoints - Kubernetes-ready liveness and readiness probes ✅
- Structured logging - JSON logging with distributed request tracing ✅
- Zero-config defaults - XDG-compliant configuration with sensible defaults ✅
- HTTP + gRPC support - Run both protocols (currently on separate ports) ✅
Most importantly: it's designed for teams. Individual contributors can't accidentally break production API contracts.
Core Features
Type-Safe API Versioning
Routes are versioned at compile time. The type system enforces it:
// Define your API versions
let routes = new
.with_base_path
.add_version_deprecated
.add_version
.build_routes;
Deprecated versions automatically include Deprecation, Sunset, and Link headers per RFC 8594.
Automatic Health Checks
Health and readiness endpoints are included automatically and follow Kubernetes best practices:
// Health checks are automatic - no code needed
new
.with_routes
.build
.serve
.await?;
// Endpoints available immediately:
// GET /health - Liveness probe (process alive?)
// GET /ready - Readiness probe (dependencies healthy?)
The readiness endpoint automatically checks configured dependencies:
# config.toml
[]
= "postgres://localhost/mydb"
= false # Readiness fails if DB is down
[]
= "redis://localhost"
= true # Readiness succeeds even if Redis is down
Batteries-Included Middleware
Production-ready middleware stack included:
new
.with_routes
.with_middleware
.build
.serve
.await?;
Available middleware:
- JWT Authentication - Token validation with configurable algorithms
- Rate Limiting - Token bucket and sliding window strategies (governor)
- Request Tracking - Request ID generation and propagation
- Compression - gzip, br, deflate, zstd
- CORS - Configurable cross-origin policies
- Timeouts - Configurable request timeouts
- Body Size Limits - Prevent oversized payloads
- Panic Recovery - Graceful handling of panics
HTTP + gRPC Support
Run HTTP and gRPC services together:
// HTTP handlers
let http_routes = new
.add_version
.build_routes;
// gRPC service
;
// Serve both protocols
// Currently on separate ports (HTTP: 8080, gRPC: 9090)
// Single-port multiplexing coming soon
new
.with_routes
.with_grpc_service
.build
.serve
.await?;
Configure gRPC port in config.toml:
[]
= true
= 9090 # Separate port for gRPC
= true # Currently required
Zero-Configuration Defaults
Configuration follows the XDG Base Directory Specification:
~/.config/acton-service/
├── my-service/
│ └── config.toml
├── auth-service/
│ └── config.toml
└── user-service/
└── config.toml
Services load configuration automatically with environment variable overrides:
# No config file needed for development
# Override specific values
ACTON_SERVICE_PORT=9090
# Production config location
Feature Flags
Enable only what you need:
[]
= { = "0.2", = [
"http", # Axum HTTP framework (default)
"grpc", # Tonic gRPC support
"database", # PostgreSQL via SQLx
"cache", # Redis connection pooling
"events", # NATS JetStream
"observability", # Structured logging (default)
"governor", # Advanced rate limiting
"openapi", # Swagger/OpenAPI documentation
] }
Note: Some feature flags (like resilience, otel-metrics) are defined but not fully implemented yet. See the roadmap below.
Or use full to enable everything:
[]
= { = "0.2", = ["full"] }
Examples
Minimal HTTP Service
use *;
async
async
Production Service with Database
use *;
async
async
Configuration in ~/.config/acton-service/my-service/config.toml:
[]
= "my-service"
= 8080
[]
= "postgres://localhost/mydb"
= 50
Event-Driven Service
use *;
async
async
See the examples/ directory for complete examples including:
- Simple versioned API -
simple-api.rs - User management API with deprecation -
users-api.rs - Dual-protocol HTTP + gRPC -
ping-pong.rs - Event-driven architecture -
event-driven.rs
Run examples:
CLI Tool
The acton CLI scaffolds production-ready services:
# Install the CLI
# Create a new service
# Full-featured service
# Add endpoints to existing service
# Generate Kubernetes manifests
See the CLI documentation for details.
Architecture
acton-service is built on production-proven Rust libraries:
- HTTP: axum - Ergonomic web framework
- gRPC: tonic - Native Rust gRPC
- Database: SQLx - Compile-time checked queries
- Cache: redis-rs - Redis client
- Events: async-nats - NATS client
- Observability: OpenTelemetry - Distributed tracing
Design principles:
- Type safety over runtime checks - Use the compiler to prevent mistakes
- Opinionated defaults - Best practices should be the default path
- Explicit over implicit - No magic, clear code flow
- Production-ready by default - Health checks, config, observability included
- Modular features - Only compile what you need
Documentation
- Configuration Guide - Environment and file-based configuration
- API Versioning - Type-safe versioning patterns
- Health Endpoints - Kubernetes liveness and readiness
- Examples - Complete working examples
API documentation: cargo doc --open
Performance
acton-service is built on tokio and axum, which are known for excellent performance characteristics. The framework adds minimal abstraction overhead beyond the underlying libraries.
Performance benchmarks will be published as the project matures. Performance is primarily determined by your application logic and the underlying libraries (axum for HTTP, tonic for gRPC, sqlx for database operations).
Deployment
Docker
FROM rust:1.84-slim as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/my-service /usr/local/bin/
EXPOSE 8080
CMD ["my-service"]
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
replicas: 3
selector:
matchLabels:
app: my-service
template:
metadata:
labels:
app: my-service
spec:
containers:
- name: my-service
image: my-service:latest
ports:
- containerPort: 8080
env:
- name: ACTON_SERVICE_PORT
value: "8080"
- name: ACTON_DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Generate complete Kubernetes manifests with the CLI:
Migration Guide
From Axum
acton-service is a thin layer over axum. Your existing handlers work unchanged:
// Your existing axum handler
async
// Works directly in acton-service
let routes = new
.add_version
.build_routes;
Main changes:
- Routes must be versioned (wrap in
VersionedApiBuilder) - Use
ServiceBuilderinstead ofaxum::serve() - Configuration loaded automatically (optional)
From Actix-Web
Similar handler patterns, different framework:
// Actix-web
async
// acton-service
async
See the examples directory for complete migration examples.
Roadmap
Implemented ✅
- Type-enforced API versioning with deprecation support
- Automatic health/readiness checks with dependency monitoring
- Structured JSON logging with distributed request tracing
- XDG-compliant configuration
- HTTP + gRPC on separate ports
- Core middleware (JWT, rate limiting, compression, CORS, timeouts)
- CLI scaffolding tool with service generation
- Database (PostgreSQL), Cache (Redis), Events (NATS) support
In Progress 🚧
- Single-port HTTP + gRPC multiplexing
- OpenTelemetry integration (OTLP exporter)
- Circuit breaker, retry, and bulkhead middleware
- HTTP metrics collection
- Enhanced CLI commands (add endpoint, worker, etc.)
Planned 📋
- GraphQL support
- WebSocket support
- Service mesh integration
- Observability dashboards
- Additional database backends
FAQ
Q: Why enforce versioning so strictly?
A: API versioning is critical in production but easy to skip. Making it impossible to bypass ensures consistent team practices. The type system is the enforcement mechanism.
Q: Can I use this without versioning?
A: No. If you need unversioned routes, use axum directly. acton-service is opinionated about API evolution.
Q: Does this work with existing axum middleware?
A: Yes. Tower middleware works unchanged. Use .layer() with any tower middleware.
Q: What about REST vs gRPC?
A: Both are first-class. Run them simultaneously (currently on separate ports; single-port multiplexing coming soon), or choose one.
Q: How does this compare to other frameworks?
A: acton-service is opinionated where others are flexible. We trade flexibility for safety and consistency. If you need maximum control, use axum or tonic directly.
Q: Is this production-ready?
A: Partially. Core features (versioning, health checks, HTTP/gRPC, database support) are production-ready and battle-tested via underlying libraries (axum, tonic, sqlx). Some advanced features (OpenTelemetry integration, resilience patterns) are in progress. Review the roadmap and test thoroughly for your use case.
Contributing
Contributions are welcome! Areas of focus:
- Additional middleware patterns
- More comprehensive examples
- Documentation improvements
- Performance optimizations
- CLI enhancements
See CONTRIBUTING.md for guidelines (coming soon).
Changelog
See CHANGELOG.md for version history (coming soon).
License
Licensed under the MIT License. See LICENSE for details.
Credits
Built with excellent open source libraries:
- tokio - Async runtime
- axum - Web framework
- tonic - gRPC implementation
- tower - Middleware foundation
- SQLx - Database client
Inspired by production challenges at scale. Built by developers who've maintained microservice architectures in production.
Start building production microservices with enforced best practices:
&&