Docs / Programming & Development / Deploy Rust with Actix-Web

Deploy Rust with Actix-Web

By Admin · Mar 15, 2026 · Updated Apr 24, 2026 · 99 views · 3 min read

Actix-Web is one of the fastest web frameworks in any language, consistently topping benchmarks. Combined with Rust's memory safety and zero-cost abstractions, it's ideal for high-performance APIs and web services. This guide covers building an Actix-Web application, deploying it on a VPS, and configuring production infrastructure.

Project Setup

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

# Create new project
cargo new myapp && cd myapp

# Add dependencies to Cargo.toml
cat >> Cargo.toml  HttpResponse {
    HttpResponse::Ok().json(serde_json::json!({"status": "healthy"}))
}

async fn get_users(data: web::Data) -> HttpResponse {
    match sqlx::query_as!(User, "SELECT id, name, email FROM users ORDER BY id")
        .fetch_all(&data.db)
        .await
    {
        Ok(users) => HttpResponse::Ok().json(users),
        Err(e) => {
            log::error!("Database error: {}", e);
            HttpResponse::InternalServerError().json(
                serde_json::json!({"error": "Internal server error"})
            )
        }
    }
}

async fn create_user(
    data: web::Data,
    body: web::Json,
) -> HttpResponse {
    match sqlx::query_as!(
        User,
        "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email",
        body.name, body.email
    )
    .fetch_one(&data.db)
    .await
    {
        Ok(user) => HttpResponse::Created().json(user),
        Err(e) => {
            log::error!("Failed to create user: {}", e);
            HttpResponse::BadRequest().json(
                serde_json::json!({"error": e.to_string()})
            )
        }
    }
}

#[actix_web::main]
async fn main() -> std::io::Result {
    dotenvy::dotenv().ok();
    env_logger::init();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    let pool = PgPoolOptions::new()
        .max_connections(20)
        .connect(&database_url)
        .await
        .expect("Failed to create pool");

    let bind_addr = env::var("BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:8080".to_string());
    log::info!("Starting server on {}", bind_addr);

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(AppState { db: pool.clone() }))
            .wrap(middleware::Logger::default())
            .wrap(middleware::Compress::default())
            .route("/health", web::get().to(health))
            .route("/api/users", web::get().to(get_users))
            .route("/api/users", web::post().to(create_user))
    })
    .bind(&bind_addr)?
    .workers(num_cpus::get())
    .run()
    .await
}

Building for Production

# Release build with optimizations
cargo build --release

# Binary is at target/release/myapp
ls -lh target/release/myapp
# Typically 5-15MB — single static binary, no runtime needed

# Strip debug symbols for smaller binary
strip target/release/myapp

Cross-Compilation

# Build on your local machine, deploy binary to VPS
# Install cross-compilation target
rustup target add x86_64-unknown-linux-musl

# Build statically linked binary
cargo build --release --target x86_64-unknown-linux-musl

# Copy to VPS
scp target/x86_64-unknown-linux-musl/release/myapp user@vps:/opt/myapp/

Systemd Service

# /etc/systemd/system/myapp.service
[Unit]
Description=My Actix-Web Application
After=network.target postgresql.service

[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
EnvironmentFile=/opt/myapp/.env
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/data

# Resource limits
LimitNOFILE=65535
MemoryMax=512M

[Install]
WantedBy=multi-user.target
# Create app user and deploy
sudo useradd -r -s /bin/false appuser
sudo mkdir -p /opt/myapp
sudo cp target/release/myapp /opt/myapp/

# Environment file
sudo tee /opt/myapp/.env         

Was this article helpful?