 Autor: [Adam Nadolny](/autorzy/adam-nadolny) Ekspert DevOps i infrastruktury · Zweryfikowano Kwiecień 2026

1.  [Strona główna](/) ›
2.  [Baza wiedzy](/baza-wiedzy/) ›
3.  Rust — deployment na VPS

# Rust na VPS — deployment aplikacji webowej Actix-web i Axum

Opublikowano: 10 kwietnia 2026 · Kategoria: VPS

Rust oferuje wydajność porównywalną z C/C++ z bezpieczeństwem pamięci bez garbage collectora. Dla web API to oznacza: miliony requestów na sekundę, minimalny RAM, brak GC pauses i mały binary (kilka-kilkanaście MB). Actix-web i Axum to dwa główne frameworki — oba async z Tokio, oba w top benchmarkach TechEmpower. Ten artykuł pokazuje jak zbudować, skonfigurować i wdrożyć aplikację Rust na VPS z Nginx, systemd i PostgreSQL.

## Axum — kompletna aplikacja REST API

\# Cargo.toml
\[package\]
name = "my-api"
version = "0.1.0"
edition = "2021"

\[dependencies\]
axum = { version = "0.7", features = \["macros"\] }
tokio = { version = "1", features = \["full"\] }
sqlx = { version = "0.7", features = \["runtime-tokio-rustls", "postgres", "uuid", "chrono"\] }
serde = { version = "1", features = \["derive"\] }
serde\_json = "1"
tower = "0.4"
tower-http = { version = "0.5", features = \["trace", "cors", "compression-gzip"\] }
tracing = "0.1"
tracing-subscriber = "0.3"
uuid = { version = "1", features = \["serde", "v4"\] }
chrono = { version = "0.4", features = \["serde"\] }
anyhow = "1"

\[profile.release\]
opt-level = 3
lto = true          # Link-time optimization (mniejszy i szybszy binary)
codegen-units = 1   # Lepsza optymalizacja (wolniejszy build)
strip = true        # Strip symboli debugowania (mniejszy binary)

// src/main.rs — Axum aplikacja z SQLx
use axum::{
    routing::{get, post},
    Router, Json, Extension, extract::State,
    http::StatusCode,
};
use sqlx::{PgPool, postgres::PgPoolOptions};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tower\_http::{trace::TraceLayer, cors::CorsLayer, compression::CompressionLayer};
use tracing\_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

#\[derive(Clone)\]
struct AppState {
    db: PgPool,
}

#\[derive(Serialize, sqlx::FromRow)\]
struct User {
    id: i32,
    email: String,
    is\_active: bool,
}

#\[derive(Deserialize)\]
struct CreateUser {
    email: String,
    password: String,
}

#\[tokio::main\]
async fn main() -> anyhow::Result<()> {
    // Inicjalizacja tracingu (logi)
    tracing\_subscriber::registry()
        .with(tracing\_subscriber::EnvFilter::new(
            std::env::var("RUST\_LOG").unwrap\_or\_else(|\_| "info".to\_string()),
        ))
        .with(tracing\_subscriber::fmt::layer())
        .init();

    let database\_url = std::env::var("DATABASE\_URL")
        .expect("DATABASE\_URL must be set");

    let pool = PgPoolOptions::new()
        .max\_connections(20)
        .min\_connections(2)
        .connect(&database\_url)
        .await?;

    // Automatyczne migracje (plik migrations/\*.sql)
    sqlx::migrate!("./migrations").run(&pool).await?;

    let state = AppState { db: pool };

    let app = Router::new()
        .route("/health", get(health\_handler))
        .route("/api/users", get(list\_users).post(create\_user))
        .route("/api/users/:id", get(get\_user))
        .with\_state(state)
        .layer(TraceLayer::new\_for\_http())
        .layer(CorsLayer::permissive())   // dostosuj w produkcji
        .layer(CompressionLayer::new());

    let addr = "127.0.0.1:8080";
    tracing::info!("Listening on {}", addr);
    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}

async fn health\_handler() -> Json<serde\_json::Value> {
    Json(serde\_json::json!({"status": "ok"}))
}

async fn list\_users(State(state): State<AppState>) -> Result<Json<Vec<User>>, StatusCode> {
    let users = sqlx::query\_as::<\_, User>(
        "SELECT id, email, is\_active FROM users WHERE is\_active = true ORDER BY id"
    )
    .fetch\_all(&state.db)
    .await
    .map\_err(|\_| StatusCode::INTERNAL\_SERVER\_ERROR)?;

    Ok(Json(users))
}

async fn create\_user(
    State(state): State<AppState>,
    Json(payload): Json<CreateUser>,
) -> Result<(StatusCode, Json<User>), StatusCode> {
    let hashed = bcrypt\_hash(&payload.password);  // pseudokod
    let user = sqlx::query\_as::<\_, User>(
        "INSERT INTO users (email, hashed\_password) VALUES ($1, $2) RETURNING id, email, is\_active"
    )
    .bind(&payload.email)
    .bind(&hashed)
    .fetch\_one(&state.db)
    .await
    .map\_err(|\_| StatusCode::INTERNAL\_SERVER\_ERROR)?;

    Ok((StatusCode::CREATED, Json(user)))
}

## Build release i cross-compilation

\# Build release na serwerze VPS (prosta metoda)
# Zainstaluj Rust (rustup)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

cargo build --release
# Binary: target/release/my-api (~5-15 MB po strip)
ls -lh target/release/my-api

# Cross-compilation z macOS/Linux na Linux musl (statyczny binary)
# Instalacja cross
cargo install cross
# Build dla Linux x86\_64 z musl (jeden plik, zero zależności systemowych)
cross build --release --target x86\_64-unknown-linux-musl

# Wyslij binary na serwer przez SCP
scp target/x86\_64-unknown-linux-musl/release/my-api user@vps:/srv/myapi/

# Sprawdzenie binarnego (czy nie ma broken deps)
ldd target/release/my-api
# musl: "not a dynamic executable" = OK, statyczny binary

## Docker multi-stage build

\# Dockerfile — multi-stage: build + minimalny runtime
FROM rust:1.77-slim-bookworm AS builder
WORKDIR /app

# Cache dependencies (osobna warstwa — speedup rebuild)
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm src/main.rs

# Teraz build wlasciwej aplikacji
COPY src ./src
COPY migrations ./migrations
RUN touch src/main.rs && cargo build --release

# Runtime image — ultra-maly (distroless lub alpine)
FROM gcr.io/distroless/cc-debian12
COPY --from=builder /app/target/release/my-api /usr/local/bin/my-api
COPY --from=builder /app/migrations /migrations

EXPOSE 8080
ENV RUST\_LOG=info
CMD \["/usr/local/bin/my-api"\]

# Rozmiar image: ~15-20 MB (vs 1.77 GB image buildowy!)

# docker-compose.yml
# services:
#   api:
#     build: .
#     restart: unless-stopped
#     ports:
#       - "127.0.0.1:8080:8080"
#     environment:
#       - DATABASE\_URL=${DATABASE\_URL}
#       - RUST\_LOG=info

## Systemd service i Nginx

\# /etc/systemd/system/rust-api.service
\[Unit\]
Description=Rust API (Axum)
After=network.target postgresql.service

\[Service\]
Type=simple
User=rustapi
Group=rustapi
WorkingDirectory=/srv/myapi
EnvironmentFile=/srv/myapi/.env
Environment=RUST\_LOG=info
ExecStart=/srv/myapi/my-api
Restart=on-failure
RestartSec=5
# Bezpieczenstwo
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/srv/myapi/logs

\[Install\]
WantedBy=multi-user.target

# /etc/nginx/sites-available/rust-api
server {
    listen 443 ssl http2;
    server\_name api.example.com;

    ssl\_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl\_certificate\_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy\_pass http://127.0.0.1:8080;
        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 X-Forwarded-Proto $scheme;
        proxy\_read\_timeout 30s;
    }
}

# Start
sudo systemctl daemon-reload && sudo systemctl enable --now rust-api
sudo nginx -t && sudo systemctl reload nginx

## Benchmarki — Rust vs Node.js vs Go

Framework

Req/s (JSON)

Latency P99

RAM (idle)

Binary size

Rust Actix-web

~1 200 000

<1 ms

~8 MB

~8 MB

Rust Axum

~950 000

<1 ms

~10 MB

~10 MB

Go (Gin / Fiber)

~700 000

~1 ms

~15 MB

~12 MB

Node.js Fastify

~350 000

~3 ms

~50 MB

N/A (runtime)

Python FastAPI

~50 000

~8 ms

~80 MB

N/A (runtime)

Wyniki orientacyjne z benchmarkow TechEmpower dla prostego endpointu JSON. W praktycznych aplikacjach z bazą danych różnica maleje — bottleneck przenosi się na I/O. Rust wciąż wygrywa przy niskim RAM i braku GC pauses.

## Actix-web vs Axum — porównanie

Cecha

Actix-web

Axum

Ekosystem

Dojrzały, bogate middleware

Rośnie (tokio team)

Middleware

Własny system

Tower middleware (share z Hyper)

Routing

Makra (get!, post!)

Chainable Router API

Error handling

ResponseError trait

IntoResponse trait

WebSocket

Wbudowany

Via axum::extract::ws

Zalecany dla

Dojrzałe projekty, bogatszy ekosystem

Nowe projekty, integracja z Tower

## Najczęstsze pytania

Actix-web vs Axum — który framework Rust wybrać? +

Actix-web jest starszy, ma więcej dokumentacji i zasobów — był długo numerem jeden w TechEmpower benchmarkach. Axum pochodzi z ekosystemu Tokio i jest bardziej "idiomatic Rust" — lepiej integruje się z Tower middleware i Hyper. Axum jest młodszy, ale dynamicznie rośnie i jest oficjalnie wspierany przez Tokio team. Dla nowych projektów Axum jest rekomendowanym wyborem. Actix-web wciąż jest świetnym wyborem jeśli potrzebujesz dojrzałego ekosystemu.

Jak duże są binarne pliki Rust i jak długo trwa kompilacja? +

Bez optymalizacji (debug build): kilkaset MB, kompilacja 1-3 minuty. Z optymalizacją (release build): 5-15 MB po stripping, kompilacja 5-15 minut dla średniego projektu. Na VPS kompilacja może być wolna przy małym CPU/RAM — lepiej kompilować lokalnie lub w CI i deployować gotowy binary. Cross-compilation (build na macOS/Windows, deploy na Linux) jest możliwa z cargo-cross lub GitHub Actions z target x86\_64-unknown-linux-musl.

Czy Rust web API jest rzeczywiście szybszy od Node.js? +

Tak, szczególnie przy wysokim obciążeniu. W benchmarkach TechEmpower Actix-web obsługuje miliony requestów na sekundę przy minimalnym CPU i RAM. Node.js z Fastify osiąga 200-400 tys. req/s. Różnica jest najbardziej widoczna przy: CPU-bound workloads (parsowanie, obliczenia), niskim latency (brak GC pause), dużej liczbie równoczesnych połączeń. Dla prostych CRUD API z bazą danych praktyczna różnica jest mniejsza — wąskim gardłem jest baza, nie aplikacja.

SQLx vs Diesel vs SeaORM — który wybrać dla Rust? +

Diesel jest synchroniczny, ale bezpieczny typowo — SQL jest weryfikowany w czasie kompilacji. SQLx jest async, pisz surowy SQL ale z compile-time sprawdzaniem zapytań. SeaORM to async ORM z query builderem podobnym do Eloquent/Django. Dla projektów async (Axum, Actix-web) SQLx lub SeaORM. SQLx daje więcej kontroli nad SQL, SeaORM jest wygodniejszy ale mniej kontrolowalny. Unikaj Diesel w async kontekście — blokuje event loop.

## Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.

Contabo

VPS pod Rust API — aplikacja skompilowana do 10 MB, mały RAM

VPS wydajny

[Aktywuj rabat →](/out/contabo)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/contabo)

Mikr.us

Rust jest tak wydajny, że uruchomisz go nawet na 512 MB RAM VPS

Nano VPS

[Aktywuj rabat →](/out/mikrus)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/mikrus)

ProSerwer.pl

Polski VPS pod serwisy Rust z niskim latency

Polski VPS

[Aktywuj rabat →](/out/proserwer-pl)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/proserwer)

## Powiązane strony

-   [FastAPI — deployment na VPS z Uvicorn](/baza-wiedzy/fastapi-python-produkcja)
-   [Docker na VPS — instalacja i Docker Compose](/baza-wiedzy/docker-na-vps)
-   [PostgreSQL na VPS — instalacja i konfiguracja](/baza-wiedzy/postgresql-vps-instalacja-konfiguracja)
-   [Wszystkie artykuły](/baza-wiedzy/)