Back to all posts

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

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.toml and runs cargo 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

AspectTempsFly.ioRenderRailway
Free tierSelf-host free (Apache 2.0)Paid plans only (free tier discontinued Oct 2024)Free tier with limitsStarter plan available
Cloud costFree to self-host; Temps Cloud (coming soon) unpriced hereSee fly.io/pricingSee render.com/pricingSee railway.app/pricing
Cold startsNone (persistent containers)None (persistent VMs)Zero on paid plansNone on paid plans
Rust detectionAutomatic (Cargo.toml)Requires DockerfileAutomatic (Nixpacks)Automatic (Nixpacks)
cargo-chef cachingSupported via DockerfileSupported via DockerfileSupported via DockerfileSupported via Dockerfile
Managed PostgreSQLBuilt-in, same CLISeparate provisioningSeparate provisioningSeparate provisioning
Error trackingBuilt-inNot includedNot includedNot included
Session replayBuilt-inNot includedNot includedNot included
Self-host optionYes (Apache 2.0)NoNoNo
Vendor lock-inNone — runs on any Linux serverProprietary platformProprietary platformProprietary platform

Prerequisites

  • A Rust web application (Axum, Actix-web, Rocket, or any HTTP framework)
  • Git repository (GitHub, GitLab, or Bitbucket)
  • Cargo.toml at 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:

  1. Detect your Rust project (or use your Dockerfile)
  2. Build the release binary with cargo build --release
  3. Package it in a minimal Alpine container
  4. Deploy to production
  5. 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 planner stage computes a dependency fingerprint (recipe.json)
  • The builder stage runs cargo chef cook which restores pre-built dependencies from cache
  • If only src/ changes (not Cargo.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:

TypeNameValue
AapiYOUR_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 install lines to your Dockerfile runtime stage
  • Binary name mismatch — ensure --bin my-rust-app matches [package] name in Cargo.toml
  • Incorrect EXPOSE port — 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
  1. Verify your app binds on 0.0.0.0 not 127.0.0.1
  2. Confirm the PORT matches the EXPOSE directive in your Dockerfile
  3. 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
#rust#deployment#axum#actix#rocket#self-hosted#tutorial