Introduction
Rust has emerged as a premier language for systems programming, web backends, and cloud-native applications. Its memory safety guarantees without garbage collection, combined with performance comparable to C/C++, make it an attractive choice for developers across domains.
This guide covers the complete toolchain for building, packaging, and deploying Rust applications—from basic compilation to production-ready deployments across multiple platforms.
Understanding cargo
Cargo is Rust’s package manager and build system rolled into one. It handles:
- Dependency management via
Cargo.toml configuration - Build automation with
cargo build, cargo test, and cargo run - Package publishing to crates.io
- Project scaffolding with
cargo new and cargo init
rustup: The Rust installer and version manager
rustup manages multiple Rust toolchains and ensures you have the right compiler (rustc) and standard library for your target platform.
1
2
3
4
5
6
7
8
9
10
11
| # Install Rust (includes cargo and rustc)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# View installed toolchains
rustup show
# Add a new toolchain
rustup toolchain install nightly
# Switch between versions
rustup default stable
|
Basic cargo.toml structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| [package]
name = "my-app"
version = "0.1.0"
edition = "2021"
description = "A sample Rust application"
license = "MIT"
[dependencies]
# Production dependencies
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
[dev-dependencies]
# Development-only dependencies
criterion = "0.5"
mockall = "0.12"
[profile.release]
opt-level = 3
lto = true
|
Compilation Targets and Cross-Compilation
Target triples in Rust
Rust uses target triples to identify platforms: arch-vendor-os-abi
Common targets:
x86_64-unknown-linux-gnu (Linux x86_64)aarch64-apple-darwin (macOS Apple Silicon)x86_64-pc-windows-msvc (Windows)wasm32-wasi (WebAssembly)
Listing available targets
1
2
3
4
5
| # List installed target toolchains
rustup target list --installed
# View all available targets
rustup target list
|
Installing a target
1
2
3
4
5
| # Install Linux ARM64 target
rustup target add aarch64-unknown-linux-gnu
# Install WebAssembly target
rustup target add wasm32-wasi
|
Cross-compilation examples
Building for ARM on x86_64 Linux
1
2
3
4
5
6
7
8
| # Install the target
rustup target add aarch64-unknown-linux-gnu
# Build with explicit target
cargo build --target aarch64-unknown-linux-gnu
# Or use cargo's default target (set via RUSTUP_TARGET or .cargo/config.toml)
cargo build
|
Cross-compiling for Windows
1
2
3
4
5
| # Add the Windows target
rustup target add x86_64-pc-windows-msvc
# Build the application
cargo build --target x86_64-pc-windows-msvc --release
|
Target configuration file
Create .cargo/config.toml in your project to set a default target:
1
2
3
4
5
| [target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[build]
target = "aarch64-unknown-linux-gnu"
|
Packaging: crates.io and cargo publish
Creating a crate
1
2
3
4
5
| # Create a new library crate
cargo new my-library --lib
# Create a new binary crate
cargo new my-app
|
1
2
3
4
5
6
7
8
9
10
11
12
| [package]
name = "my-crate"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
description = "A brief description of your crate"
documentation = "https://docs.rs/my-crate"
repository = "https://github.com/username/my-crate"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["crates", "rust", "example"]
categories = ["development-tools"]
|
Publishing to crates.io
1
2
3
4
5
6
7
8
| # Login to crates.io (requires account)
cargo login
# Verify your package metadata
cargo publish --dry-run
# Publish the crate
cargo publish
|
Version management
Cargo uses Semantic Versioning (SemVer):
0.x.y - Initial development (breaking changes allowed)x.y.z where x > 0 - Stable releases (backward compatibility required)
1
2
3
4
| [dependencies]
my-crate = "1.0" # ^1.0.0: >= 1.0.0, < 2.0.0
other-crate = "~1.4.5" # >= 1.4.5, < 1.5.0
third-crate = "=1.2.3" # exactly 1.2.3
|
Deployment Patterns
Static Binaries for Portability
Rust’s ability to produce statically linked binaries makes deployment simple—just copy the binary to any compatible system.
Producing a static binary
1
2
3
4
5
| [profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
panic = "abort" # Remove unwinding machinery
codegen-units = 1 # Better optimization across crates
|
Build with musl for static linking on Linux:
1
2
3
4
5
| # Install musl target
rustup target add x86_64-unknown-linux-musl
# Build static binary
cargo build --target x86_64-unknown-linux-musl --release
|
The resulting binary has no external dependencies and can run on any Linux system with the same architecture.
Docker-based static compilation
Use a multi-stage Dockerfile to build static binaries:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| FROM rust:1-alpine AS builder
# Install musl tools
RUN apk add --no-cache musl-dev
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo 'fn main() {}' > src/main.rs
RUN cargo build --release --target x86_64-unknown-linux-musl
# Copy source and rebuild properly
COPY src ./src
RUN cargo build --release --target x86_64-unknown-linux-musl
FROM scratch
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/my-app /my-app
ENTRYPOINT ["/my-app"]
|
Containerized Deployments with Docker
Rust applications work well in containers, especially for complex deployments.
Standard Rust Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| FROM rust:1-slim AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN cargo fetch
COPY src ./src
RUN cargo build --release
FROM debian:bookworm-slim
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-app .
CMD ["./my-app"]
|
Multi-stage optimized Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Stage 1: Build
FROM rust:1-slim AS builder
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
RUN cargo fetch
COPY src ./src
RUN cargo build --release --locked
# Stage 2: Runtime
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
libssl3 \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /build/target/release/my-app .
CMD ["./my-app"]
|
Serverless Deployments
Rust is excellent for serverless functions due to fast startup times and low memory usage.
AWS Lambda with Rust
- Install the
cargo-lambda tool:
1
| cargo install cargo-lambda cargo-lambda-builders
|
- Create a new Lambda function:
1
| cargo lambda new my-function --runtime rust
|
- Build for AWS Lambda:
1
| cargo lambda build --release --output-format zip
|
- Deploy:
1
| cargo lambda deploy my-function
|
Example Lambda handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| use lambda_runtime::{lambda, error::HandlerError, context::Context};
use serde_json::Value;
async fn my_handler(
event: Value,
_context: Context,
) -> Result<Value, HandlerError> {
Ok(serde_json::json!({
"message": "Hello from Rust Lambda!",
"input": event
}))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
lambda!(my_handler);
Ok(())
}
|
Cloudflare Workers
1
2
3
4
5
6
7
8
9
| # Install Wrangler CLI
cargo install wrangler
# Create a new worker
wrangler generate my-worker
cd my-worker
# Build for WebAssembly target
cargo build --target wasm32-wasi --release
|
| Deployment Scenario | Recommended Approach | Rationale |
|---|
| Embedded systems | Static binary with musl | No dynamic library dependencies, minimal size |
| Linux containers | Standard Docker image with slim base | Smaller images, faster pull times |
| Windows deployment | Native Windows build + installer | Best integration with Windows ecosystem |
| Cross-platform desktop | Multiple static binaries per target | Single file distribution, no runtime setup |
| AWS Lambda | cargo-lambda + musl target | Optimized for cold start and size limits |
| Azure Functions | Standard Rust compilation + Azure CLI | Native support for .NET-hosted Rust WASM |
| Kubernetes | Multi-stage Docker build | Reproducible builds, container orchestration |
Size optimization tips
1
2
3
4
5
6
| [profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
panic = "abort" # Remove unwinding stack traces
codegen-units = 1 # Single codegen unit for better optimizations
strip = true # Strip debug symbols (use strip command after build)
|
After building, manually strip the binary:
1
2
| cargo build --release
strip target/release/my-app
|
Memory-constrained environments
For embedded or low-memory deployments:
- Use
panic = "abort" to eliminate unwinding code - Disable default features when possible:
1
2
3
4
| [dependencies.tokio]
version = "1.0"
default-features = false
features = ["rt", "macros"]
|
- Use
--release --no-default-features when appropriate
CI/CD Integration
GitHub Actions example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| name: Rust CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
target/
key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-rust-
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
cross-compile:
needs: build-and-test
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu]
steps:
- uses: actions/checkout@v4
- name: Install target
run: rustup target add ${{ matrix.target }}
- name: Build release binary
run: cargo build --target ${{ matrix.target }} --release
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: my-app-${{ matrix.target }}
path: target/${{ matrix.target }}/release/my-app
publish-crates:
needs: cross-compile
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }}
|
GitLab CI example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| stages:
- build
- test
- deploy
variables:
CARGO_INCREMENTAL: "0"
CARGO_NET_RETRY: "10"
RUSTUP_MAX_RETRIES: "10"
build:
stage: build
image: rust:1-slim
script:
- cargo fetch
- cargo build --release
artifacts:
paths:
- target/release/my-app
test:
stage: test
image: rust:1-slim
script:
- cargo test
coverage: /cov: \d+%/
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- curl -X POST $DEPLOY_WEBHOOK -d 'status=running'
- ./target/release/my-app &
- sleep 5
- curl -f http://localhost:$PORT/health || exit 1
environment:
name: production
|
Conclusion
Rust’s toolchain provides powerful capabilities for building and deploying applications across diverse platforms. By understanding cargo, rustup, and the various deployment patterns, you can create efficient, portable, and maintainable Rust applications.
Key takeaways:
- Build tooling: Use
cargo for all build-related tasks and rustup for managing toolchains - Cross-compilation: Install targets with
rustup target add and build with --target - Packaging: Follow SemVer and publish to crates.io for library distribution
- Deployment patterns: Choose based on your platform—static binaries for portability, containers for consistency, serverless for scalability
What deployment scenario are you building for? Share your experiences in the comments!
Related posts