Deploy a Rust Web App to Production with Temps
Deploy Axum, Actix, or Rocket apps with Temps: automatic release builds in a minimal Alpine container, automatic HTTPS via Pingora, and managed PostgreSQL.
Temps Team
June 7, 2026 · 2mo ago
Deploying a Rust web app to production means one command: bunx @temps-sdk/cli deploy my-rust-app -b main -e production -y. Temps detects your Rust project, builds an optimized release binary inside a minimal Alpine container, provisions automatic HTTPS via Pingora (Cloudflare's open-source Rust proxy), and starts routing traffic — no DevOps required.
TL;DR:
bunx @temps-sdk/cli deploy my-rust-app -b main -e production -y— your Axum, Actix, or Rocket app is live with HTTPS, managed PostgreSQL, and built-in monitoring in under 5 minutes. Temps is Apache 2.0 and free to self-host, with an optional Temps Cloud managed layer (telemetry retention, offsite backups, and AI credits) coming soon.
How Do I Deploy a Rust Web App to Production?
The fastest path: push your Rust project to a git repository, connect it to Temps, and run one CLI command. Temps detects your Cargo.toml, builds a release binary with cargo build --release, packages it in a minimal Alpine container, and provisions a Let's Encrypt certificate — all without you writing a Dockerfile or configuring Nginx.
If you want full control over the build (recommended for production), supply a multi-stage Dockerfile using the cargo-chef pattern — covered in the Dockerfile section below.
What You'll Get
After this tutorial, your Rust web app will have:
- Production deployment with automatic HTTPS (Let's Encrypt via Pingora proxy)
- Zero-config Rust detection — Temps detects
Cargo.tomland runscargo build --release - Optimized release builds with minimal Alpine-based container images
- Dependency caching with cargo-chef — dramatically faster rebuilds (only changed code recompiled)
- Encrypted environment variable management — variables injected at runtime, never baked into images
- Built-in error tracking — no Sentry subscription needed
- Request analytics — latency percentiles, error rates, geographic distribution in your dashboard
- Auto-rollback — health checks every 5 seconds, 2 consecutive failures trigger rollback within 60 seconds
- Managed PostgreSQL — provision a database alongside your app from the same CLI
Temps vs Other Rust Deployment Options
| Aspect | Temps | Fly.io | Render | Railway |
|---|---|---|---|---|
| Free tier | Self-host free (Apache 2.0) | Paid plans only (free tier discontinued Oct 2024) | Free tier with limits | Starter plan available |
| Cloud cost | Free to self-host; Temps Cloud (coming soon) unpriced here | See fly.io/pricing | See render.com/pricing | See railway.app/pricing |
| Cold starts | None (persistent containers) | None (persistent VMs) | Zero on paid plans | None on paid plans |
| Rust detection | Automatic (Cargo.toml) | Requires Dockerfile | Automatic (Nixpacks) | Automatic (Nixpacks) |
| cargo-chef caching | Supported via Dockerfile | Supported via Dockerfile | Supported via Dockerfile | Supported via Dockerfile |
| Managed PostgreSQL | Built-in, same CLI | Separate provisioning | Separate provisioning | Separate provisioning |
| Error tracking | Built-in | Not included | Not included | Not included |
| Session replay | Built-in | Not included | Not included | Not included |
| Self-host option | Yes (Apache 2.0) | No | No | No |
| Vendor lock-in | None — runs on any Linux server | Proprietary platform | Proprietary platform | Proprietary platform |
Prerequisites
- A Rust web application (Axum, Actix-web, Rocket, or any HTTP framework)
- Git repository (GitHub, GitLab, or Bitbucket)
Cargo.tomlat the repo root (or in a workspace crate)
Project Structure
Temps works with any Rust project layout. Here's a minimal Axum example:
my-rust-app/
├── Cargo.toml
├── Cargo.lock # commit this for reproducible builds
├── Dockerfile # optional but recommended (cargo-chef pattern)
└── src/
└── main.rs
Minimal Axum app (src/main.rs):
use axum::{routing::get, Router};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(root))
.route("/health", get(health));
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
println!("Listening on {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn root() -> &'static str {
"Hello from Rust on Temps"
}
async fn health() -> &'static str {
"ok"
}
Cargo.toml:
[package]
name = "my-rust-app"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
Temps expects your app to bind on 0.0.0.0:8080 by default. Configure the port via the PORT environment variable for flexibility.
Deploy Rust with the Temps CLI
Step 1: Install Temps CLI
# macOS / Linux
curl -fsSL https://temps.sh/install.sh | bash
Step 2: Login and Create Project
bunx @temps-sdk/cli login
# Create project and connect your git repository
# Temps detects Cargo.toml automatically (use --preset docker if you have a Dockerfile)
bunx @temps-sdk/cli projects create \
-n "My Rust App" \
-d "Axum web application" \
--repo your-org/your-rust-app \
--branch main \
--preset docker
Use --preset docker when you supply your own Dockerfile (recommended). Temps also auto-detects Rust projects without a preset via Nixpacks — but providing a Dockerfile with the cargo-chef pattern gives you faster, reproducible builds.
Step 3: Configure Environment Variables
# Set your app's environment variables
bunx @temps-sdk/cli environments vars set RUST_LOG "info" -e production
bunx @temps-sdk/cli environments vars set APP_ENV "production" -e production
# Import from file
bunx @temps-sdk/cli environments vars import .env.production -e production
Step 4: (Optional) Provision Managed PostgreSQL
# Create a managed PostgreSQL service
bunx @temps-sdk/cli services create postgres \
-n "my-rust-db" \
-e production
# The DATABASE_URL is automatically injected into your app
Step 5: Deploy
bunx @temps-sdk/cli deploy my-rust-app -b main -e production -y
Temps will:
- Detect your Rust project (or use your Dockerfile)
- Build the release binary with
cargo build --release - Package it in a minimal Alpine container
- Deploy to production
- Provision an SSL certificate via Let's Encrypt
Your app is live at your-app.temps.sh within minutes.
Provide a Dockerfile for Best Build Performance
Rust compile times can be long. The cargo-chef pattern caches dependencies separately from your application code — only changed source files trigger a recompile. This typically reduces incremental build times from minutes to seconds.
Multi-stage Dockerfile with cargo-chef:
# Stage 1: Dependency planner
FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
WORKDIR /app
# Stage 2: Cache dependencies
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
# Build and cache dependencies only (this layer is reused across rebuilds)
RUN cargo chef cook --release --recipe-path recipe.json
# Stage 3: Build application
COPY . .
RUN cargo build --release --bin my-rust-app
# Stage 4: Minimal runtime image
FROM debian:bookworm-slim AS runtime
RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/my-rust-app /app/my-rust-app
EXPOSE 8080
CMD ["/app/my-rust-app"]
Replace my-rust-app in --bin my-rust-app and target/release/my-rust-app with your actual binary name from Cargo.toml.
Why cargo-chef?
- The
plannerstage computes a dependency fingerprint (recipe.json) - The
builderstage runscargo chef cookwhich restores pre-built dependencies from cache - If only
src/changes (notCargo.toml/Cargo.lock), Docker reuses the dependency cache layer - Result: incremental rebuilds skip the longest step (compiling crates)
Health Check Endpoint
Temps polls your health endpoint every 5 seconds. Two consecutive failures within a 60-second window trigger automatic rollback to the last healthy deployment.
Axum:
use axum::{routing::get, Router, http::StatusCode};
async fn health() -> StatusCode {
StatusCode::OK
}
let app = Router::new()
.route("/health", get(health));
Actix-web:
use actix_web::{get, HttpResponse, Responder};
#[get("/health")]
async fn health() -> impl Responder {
HttpResponse::Ok().body("ok")
}
Rocket:
#[rocket::get("/health")]
fn health() -> &'static str {
"ok"
}
Keep health checks fast and dependency-free. If your database is down, return a degraded status rather than a 500 — let Temps know your app is running, but surface DB issues through error tracking.
Database Connections
Managed PostgreSQL
Provision a PostgreSQL instance alongside your app:
bunx @temps-sdk/cli services create postgres \
-n "my-rust-db" \
-e production
Temps injects DATABASE_URL automatically. Use with sqlx or tokio-postgres:
// sqlx example
use sqlx::PgPool;
let pool = PgPool::connect(&std::env::var("DATABASE_URL").unwrap())
.await
.expect("Failed to connect to database");
Or connect an external database manually:
bunx @temps-sdk/cli environments vars set DATABASE_URL "postgresql://user:pass@host:5432/db" -e production
Redis
bunx @temps-sdk/cli environments vars set REDIS_URL "redis://host:6379" -e production
use redis::AsyncCommands;
let client = redis::Client::open(std::env::var("REDIS_URL").unwrap()).unwrap();
let mut con = client.get_async_connection().await.unwrap();
Environment Variables in Rust
use std::env;
// Read at startup
let port: u16 = env::var("PORT")
.unwrap_or_else(|_| "8080".to_string())
.parse()
.expect("PORT must be a number");
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
For structured config, use the config crate or dotenvy for local development:
[dependencies]
dotenvy = "0.15" # loads .env in development only
Environment variables are encrypted at rest in Temps and injected at runtime — they never appear in your Docker image layers.
Built-in Monitoring
After deployment, your Temps dashboard includes monitoring with no extra subscriptions:
Request Analytics
- Request count and latency percentiles (p50, p95, p99)
- Endpoint breakdown with per-route timing
- Error rates by route
- Geographic distribution of traffic
Error Tracking
- Panic capture with full stack traces
- Request context (headers, environment)
- Error grouping and trend analysis
No Sentry, no Datadog, no additional setup — it's built into the same binary that runs your proxy.
Scaling Your Application
Horizontal Scaling
bunx @temps-sdk/cli environments scale -e production -r 3
Temps handles load balancing automatically via Pingora, Cloudflare's open-source Rust proxy. Rust's low memory footprint means you can run many replicas on modest hardware.
Update Resource Allocation
bunx @temps-sdk/cli projects config -p my-rust-app \
--cpu-limit 2 \
--memory-limit 512 \
-y
bunx @temps-sdk/cli deploy my-rust-app -b main -e production -y
Custom Domains
DNS Configuration
Add an A record pointing to your Temps server IP:
| Type | Name | Value |
|---|---|---|
| A | api | YOUR_SERVER_IP |
Adding Your Domain
# HTTP-01 challenge (default, works for most domains)
bunx @temps-sdk/cli domains add -d api.yourdomain.com
# DNS-01 challenge (required for wildcard domains)
bunx @temps-sdk/cli domains add -d "*.yourdomain.com" --challenge dns-01
Troubleshooting
Build Fails
Stream build logs via CLI:
bunx @temps-sdk/cli deploy my-rust-app -b main -e production
Common issues:
- Missing system dependencies — add
apt-get installlines to your Dockerfile runtime stage - Binary name mismatch — ensure
--bin my-rust-appmatches[package] nameinCargo.toml - Incorrect
EXPOSEport — must match what your app binds on
App Crashes on Start
# Stream live logs
bunx @temps-sdk/cli runtime-logs -p my-rust-app -f
- Verify your app binds on
0.0.0.0not127.0.0.1 - Confirm the PORT matches the
EXPOSEdirective in your Dockerfile - Check all required environment variables are set:
bunx @temps-sdk/cli environments vars list -e production
Health Check Failures
Temps checks your /health endpoint every 5 seconds. If the endpoint returns non-2xx or times out for two consecutive checks within a 60-second window, Temps auto-rolls back. Ensure /health responds quickly regardless of downstream service state.
FAQ
Do I need a Dockerfile?
No. Temps auto-detects Rust projects via Cargo.toml and builds with Nixpacks. However, providing a Dockerfile using the cargo-chef pattern (shown above) dramatically improves build cache utilization — especially important for Rust's long compile times.
How do I use cargo-chef for caching?
Copy the multi-stage Dockerfile from the Dockerfile section above. The key insight: cargo chef cook pre-builds all dependencies before your source code is copied in. If your Cargo.toml and Cargo.lock haven't changed, Docker reuses the cached layer — skipping the slow dependency compilation step entirely.
Does Temps support Axum, Actix-web, and Rocket?
Yes — Temps is framework-agnostic. Any Rust HTTP server that binds a TCP port works. Axum, Actix-web, and Rocket are all supported and commonly deployed.
Is this free?
Self-hosting is completely free (Apache 2.0). Temps Cloud, a managed add-on for telemetry retention, offsite backups, and AI credits, is coming soon — pricing hasn't been announced yet. See temps.sh/pricing for updates.
How do I handle database migrations?
Run migrations as a startup step or use Temps' managed environment to run them before deployment. With sqlx, compile-time verification catches migration issues early:
// Run pending migrations at startup
sqlx::migrate!("./migrations").run(&pool).await.expect("Migration failed");
Does Temps support WebSocket connections?
Yes — Pingora (the underlying proxy) supports WebSocket and SSE connections natively. No additional configuration required.
Quick Reference
# Install CLI
curl -fsSL https://temps.sh/install.sh | bash
# Login
bunx @temps-sdk/cli login
# Create project and connect repo
bunx @temps-sdk/cli projects create \
-n "My Rust App" \
-d "Axum web application" \
--repo myorg/my-rust-app \
--branch main \
--preset docker
# Deploy
bunx @temps-sdk/cli deploy my-rust-app -b main -e production -y
# Stream logs
bunx @temps-sdk/cli runtime-logs -p my-rust-app -f
# Set environment variable
bunx @temps-sdk/cli environments vars set RUST_LOG "info" -e production
# Scale replicas
bunx @temps-sdk/cli environments scale -e production -r 3
# Add domain (HTTP-01 by default, DNS-01 for wildcards)
bunx @temps-sdk/cli domains add -d api.example.com
# Provision managed PostgreSQL
bunx @temps-sdk/cli services create postgres -n "my-rust-db" -e production
Ready to deploy your Rust app? Get started at temps.sh — self-host for free (Apache 2.0), with Temps Cloud, a managed add-on for telemetry retention, backups, and AI credits, coming soon:
curl -fsSL https://temps.sh/install.sh | bash && bunx @temps-sdk/cli login
Get weekly updates