# Rust Web Applications on DomainIndia VPS: Actix-web and Axum
TL;DR
Rust produces small, fast, memory-safe binaries — ideal for high-throughput APIs on a DomainIndia VPS. This guide covers picking Actix-web vs Axum, compiling for Linux, running under systemd, and fronting with nginx + SSL.
## Why Rust for web apps
- **Performance** — often 2–5× throughput of Node.js, 10× of Python for CPU-bound work
- **Memory** — a typical Rust API uses 20–80 MB RAM (a Spring Boot equivalent: 300–800 MB)
- **Safety** — no null pointer dereferences, no data races (compile-time guarantees)
- **Ecosystem (2026)** — mature web frameworks, async runtime (tokio) is stable
Trade-offs:
- Steep learning curve — borrow checker takes weeks to get comfortable with
- Longer compile times than Go (~10× for a typical API)
- Smaller talent pool compared to PHP/Node/Python
Good fit: performance-critical APIs, system tools, data processing pipelines. Questionable fit: CRUD apps under 100 req/sec (Node or Laravel are faster to ship).
## Actix-web vs Axum
| Framework | Async runtime | Style | Best for |
| Actix-web | tokio | Actor model, middleware chains | High-throughput APIs, raw performance |
| Axum | tokio | Tower-based, type-safe handlers | Modern idiomatic Rust, composable middleware |
For new projects in 2026, **Axum** is the default recommendation (maintained by the tokio team, simpler mental model). Actix-web is still the fastest but slightly heavier API surface.
## Step 1 — Install Rust (on your laptop)
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustc --version
```
## Step 2 — Create a minimal Axum API
```bash
cargo new --bin api && cd api
```
`Cargo.toml`:
```toml
[package]
name = "api"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1.36", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.5", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = "0.3"
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono"] }
```
`src/main.rs`:
```rust
use axum::{routing::get, Json, Router};
use serde::Serialize;
use std::net::SocketAddr;
#[derive(Serialize)]
struct Health { status: &'static str, version: &'static str }
async fn health() -> Json {
Json(Health { status: "ok", version: env!("CARGO_PKG_VERSION") })
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let app = Router::new()
.route("/health", get(health))
.layer(tower_http::trace::TraceLayer::new_for_http());
let addr: SocketAddr = "0.0.0.0:8080".parse().unwrap();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
tracing::info!("listening on {}", addr);
axum::serve(listener, app).await.unwrap();
}
```
Run locally:
```bash
RUST_LOG=info cargo run
curl http://localhost:8080/health
```
## Step 3 — Cross-compile for Linux
On macOS/Windows:
```bash
# Add target
rustup target add x86_64-unknown-linux-musl
# Ubuntu/Debian laptop:
sudo apt install musl-tools
# macOS:
brew install FiloSottile/musl-cross/musl-cross
```
Build static binary (works on any Linux without glibc fuss):
```bash
cargo build --release --target x86_64-unknown-linux-musl
# Output: target/x86_64-unknown-linux-musl/release/api
```
Typical binary size: 5–15 MB. `strip` it further:
```bash
strip target/x86_64-unknown-linux-musl/release/api
```
Upload:
```bash
scp target/x86_64-unknown-linux-musl/release/api root@vps:/opt/rust-app/api
```
## Step 4 — systemd on VPS
`/etc/systemd/system/rust-api.service`:
```ini
[Unit]
Description=Rust API (Axum)
After=network.target
[Service]
Type=simple
User=rustapp
Group=rustapp
WorkingDirectory=/opt/rust-app
ExecStart=/opt/rust-app/api
Restart=on-failure
RestartSec=3
Environment="RUST_LOG=info"
Environment="DATABASE_URL=postgres://rustapp:pass@localhost/myapp"
EnvironmentFile=-/opt/rust-app/.env
# Hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/rust-app/data
ProtectHome=true
# Logging
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
Create user + start:
```bash
sudo useradd -r -s /bin/false -d /opt/rust-app rustapp
sudo chown -R rustapp:rustapp /opt/rust-app
sudo systemctl daemon-reload
sudo systemctl enable --now rust-api
sudo journalctl -u rust-api -f
```
## Step 5 — nginx + Let's Encrypt
Same pattern as Go/Java:
```nginx
upstream rust_api { server 127.0.0.1:8080; keepalive 64; }
server {
listen 80;
server_name api.yourcompany.com;
location / {
proxy_pass http://rust_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection "";
}
}
```
```bash
sudo certbot --nginx -d api.yourcompany.com
```
## Database with sqlx (compile-time SQL)
`sqlx` validates SQL queries at compile time against your actual schema.
```rust
use sqlx::postgres::PgPoolOptions;
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(&std::env::var("DATABASE_URL").unwrap())
.await?;
let users = sqlx::query!("SELECT id, email FROM users WHERE active = true")
.fetch_all(&pool)
.await?;
```
If the schema doesn't match, compilation fails. No runtime surprises.
## Async runtimes — Tokio is the answer
Rust has multiple async runtimes (tokio, async-std, smol). Tokio is the de-facto standard; both Axum and Actix-web use it. Don't pick anything else for web.
Configure tokio's threads:
```rust
#[tokio::main(worker_threads = 4)]
async fn main() { ... }
```
Default is "one worker per CPU core" — usually fine.
## Zero-downtime deployments
Rust binaries upgrade well with a blue-green pattern (same as Go):
```bash
# Build new
scp new-api root@vps:/opt/rust-app/api-new
# Swap + restart
ssh root@vps "mv /opt/rust-app/api /opt/rust-app/api-old &&
mv /opt/rust-app/api-new /opt/rust-app/api &&
systemctl restart rust-api"
```
Systemd's `Restart=on-failure` kicks in if the new binary crashes — revert manually.
Or use `systemd-notify` + `sd_notify_with_fds` for true zero-downtime (complex; usually overkill).
## Common pitfalls
## FAQ
Q
Is Rust production-ready for web apps?
Yes — Cloudflare, Discord, Amazon, Meta, and many others use Rust in production web stacks. Actix-web and Axum are battle-tested.
Q
Rust vs Go for APIs?
Go compiles 10× faster and has a gentler learning curve. Rust has stronger type safety, slightly better throughput, and lower RAM. For a team learning one language: Go. For a performance-critical service: Rust.
Q
Do I need Tokio? Can't I use sync Rust?
You can write blocking HTTP servers (e.g. with hyper's sync API) but modern Rust web ecosystem is async-first. Embrace it.
Q
How much RAM does a Rust API need?
50–200 MB for a typical CRUD API. Our 2 GB VPS Starter comfortably runs 5+ Rust services.
Q
Rust compilation is slow — how to speed up dev loop?
Use cargo watch -x run for auto-rebuild. Install the mold linker (10× faster link step). On laptops, enable rust-analyzer for fast IDE feedback without full compiles.
Rust loves a small, fast VPS. Start with a VPS Starter plan.
Pick a VPS