Rusty SSR
The fastest SSR engine for Rust. Period.
Render 95,000+ pages per second with sub-millisecond latency. Drop-in replacement for Node.js SSR that's 50x faster.
Benchmarks (Apple M4, 10 cores)
┌─────────────────────────────────────────────────────────────┐
│ STRESS TEST (30 seconds) │
├─────────────────────────────────────────────────────────────┤
│ Requests/sec: 95,363 RPS │
│ Total requests: 2,869,878 │
│ Data transferred: 171 GB │
├─────────────────────────────────────────────────────────────┤
│ Latency p50: 0.46ms │
│ Latency p99: 4.60ms │
│ Max latency: 45.7ms │
└─────────────────────────────────────────────────────────────┘
vs Competition
| Engine | RPS | p99 Latency | Memory |
|---|---|---|---|
| Rusty SSR | 95,363 | 4.6ms | ~200MB |
| Next.js (Node) | 500-2,000 | 50-200ms | ~500MB+ |
| Nuxt (Node) | 500-1,500 | 40-150ms | ~500MB+ |
50x faster throughput. 40x lower latency. 60% less memory.
Why Rusty SSR?
The Problem with Node.js SSR
Node.js Cluster Mode Rusty SSR
┌─────────────────────┐ ┌─────────────────────┐
│ Process 1 │ │ 1 Process │
│ └─ V8 + 512MB heap │ │ ├─ V8 isolate 1 │
├─────────────────────┤ │ ├─ V8 isolate 2 │
│ Process 2 │ │ ├─ V8 isolate 3 │
│ └─ V8 + 512MB heap │ │ ├─ ... │
├─────────────────────┤ │ └─ V8 isolate 10 │
│ ... × 10 │ │ │
├─────────────────────┤ │ Shared L1/L2 Cache │
│ ~5GB RAM total │ │ ~200MB RAM total │
│ No shared cache │ │ Zero-copy Arc<str> │
└─────────────────────┘ └─────────────────────┘
- Node.js: 10 processes × 512MB = 5GB RAM, no shared cache
- Rusty SSR: 1 process, 10 V8 isolates, shared cache, 200MB RAM
The Solution
Rusty SSR runs V8 isolates in a thread pool managed by Rust. Each CPU core gets its own V8 instance, but they share a common cache. Zero-copy Arc<str> means no memory duplication.
Quick Start
[]
= "0.1"
= { = "1", = ["full"] }
= "0.7"
1. Create SSR Bundle
// ssr-bundle.js
globalThis ;
2. Use with Axum
use ;
use *;
use Arc;
async
async
That's it. Your SSR is now 50x faster.
Features
Built-in Browser Polyfills
No more "window is not defined" errors. Rusty SSR automatically injects polyfills for:
window,document,navigator,locationlocalStorage,sessionStoragerequestAnimationFrame,cancelAnimationFrameMutationObserver,ResizeObserver,IntersectionObservermatchMedia,Image,performance
Just load your bundle — it works.
Multi-tier Cache
Request → L1/L2 Hot Cache (1-3ns) → Cold Cache (100ns) → V8 Render
↑ ↑ ↓
└──────────────────────────┴──── cache result ┘
- Hot cache: Thread-local, L1/L2 CPU cache speed
- Cold cache: DashMap with LRU eviction
- Automatic: No configuration needed
Framework Agnostic
Works with any JavaScript framework that supports SSR:
- React / Preact
- Vue 3 / Nuxt
- Solid
- Svelte / SvelteKit
- Vanilla JS
See examples/bundles/ for complete examples.
API Reference
Basic Render
// Simple render
let html = engine.render.await?;
// With JSON data
use json;
let html = engine.render_json.await?;
// With string data
let html = engine.render_with_data.await?;
// Skip cache (always render fresh)
let html = engine.render_uncached.await?;
Configuration
let engine = builder
.bundle_path // Path to JS bundle
.pool_size // V8 workers (default: CPU count)
.queue_capacity // Task queue size
.pin_threads // Pin workers to CPU cores
.cache_size // Number of cached entries
.cache_ttl_secs // Cache TTL (0 = forever)
.render_function // JS function name
.build_engine?;
Cache Metrics
let metrics = engine.cache_metrics;
println!;
println!;
println!;
println!;
Building SSR Bundles
Option 1: Vite (Recommended)
// vite.config.ts
export default defineConfig({
build: {
ssr: true,
rollupOptions: {
input: 'src/entry-server.tsx',
output: {
format: 'iife',
name: 'SSRBundle',
inlineDynamicImports: true
},
},
},
});
# Build SSR bundle
# Wrap for Rusty SSR
Option 2: Direct
Write your bundle with globalThis.renderPage directly:
import from 'preact-render-to-string';
import App from './App';
globalThis ;
Feature Flags
| Feature | Default | Description |
|---|---|---|
v8-pool |
✅ | V8 thread pool |
cache |
✅ | Multi-tier caching |
axum-integration |
✅ | Axum middleware |
brotli-compression |
❌ | Brotli middleware |
full |
❌ | All features |
# Minimal (just V8 pool)
= { = "0.1", = false, = ["v8-pool"] }
# Full (everything)
= { = "0.1", = ["full"] }
Testing & Benchmarks
Running Tests
# Run all tests
# Run integration tests only
# Run with verbose output
Integration tests cover:
- V8 pool configuration
- DashMap concurrent cache operations
- LRU cache eviction behavior
- Async patterns (tokio channels, timeouts)
- Thread safety (Arc, Mutex, mpsc)
- URL parsing and JSON serialization
Running Benchmarks
# Run all benchmarks
# Run SSR benchmarks only
# Run cache benchmarks only
SSR Benchmarks (ssr_benchmark):
- Pool config creation overhead
- String operations (small/medium/large HTML)
- JSON serialization performance
- Channel throughput (request queue simulation)
Cache Benchmarks (cache_benchmark):
- DashMap concurrent read/write (1, 2, 4, 8 threads)
- DashMap sharding (sequential vs random keys)
- L1/L2 cache hit performance
- LRU eviction overhead (128, 512, 2048 entries)
- Arc vs String cloning
Results are saved to target/criterion/ with HTML reports.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ SsrEngine │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ SSR Cache │ │ V8 Pool │ │
│ │ │ miss │ │ │
│ │ ┌───────────┐ │ ───────► │ ┌────┐ ┌────┐ ┌────┐ │ │
│ │ │ Hot (L1) │ │ │ │ V8 │ │ V8 │ │ V8 │ │ │
│ │ └───────────┘ │ │ └────┘ └────┘ └────┘ │ │
│ │ ┌───────────┐ │ result │ ... │ │
│ │ │ Cold (RAM)│ │ ◄─────── │ ┌────┐ ┌────┐ ┌────┐ │ │
│ │ └───────────┘ │ │ │ V8 │ │ V8 │ │ V8 │ │ │
│ │ LRU eviction │ │ └────┘ └────┘ └────┘ │ │
│ └─────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Deployment
Docker
FROM rust:1.75-slim-bookworm 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/your-app /app/server
COPY ssr-bundle.js /app/
WORKDIR /app
CMD ["./server"]
Railway / Fly.io
Just push your code — Rusty SSR works with any platform that supports Rust.
Troubleshooting
"window is not defined"
This shouldn't happen with v0.1+ — browser polyfills are automatic. If it does:
- Check your bundle doesn't run browser code at module load time
- Use
typeof window !== 'undefined'guards if needed
"renderPage is not a function"
Your bundle must expose globalThis.renderPage:
// Correct
globalThis ;
// Wrong
export // ESM export won't work
Memory usage grows
Set a cache TTL to prevent unbounded growth:
.cache_ttl_secs // Expire after 5 minutes
License
MIT — use it however you want.
Contributing
Issues and PRs welcome! See CONTRIBUTING.md.