Skip to content

Workflow: Deploy & Release ​

Updated Mar 2026

Overview ​

This workflow covers optimizing Rust binaries for production, publishing to crates.io, cross-compilation, Docker containers, and CI/CD release pipelines.

Release Pipeline ​

Release Profile Optimization ​

toml
# Cargo.toml

[profile.release]
opt-level = 3        # Maximum optimization
lto = true           # Link-Time Optimization — slower build, faster binary
strip = true         # Strip debug symbols — smaller binary
codegen-units = 1    # Single codegen unit — better optimization
panic = "abort"      # No unwinding — smaller binary

# For even smaller binaries
[profile.release-small]
inherits = "release"
opt-level = "z"      # Optimize for size
strip = true
lto = true
codegen-units = 1
panic = "abort"

Binary Size Optimization ​

bash
# Check binary size
ls -lh target/release/my_app

# Analyze what's contributing to size
cargo install cargo-bloat
cargo bloat --release --crates   # Size by crate
cargo bloat --release -n 20      # Top 20 functions by size

# Strip symbols manually (if strip = true isn't enough)
strip target/release/my_app

# UPX compression (optional, reduces size further)
upx --best target/release/my_app

Cross-Compilation ​

bash
# Install cross-compilation toolchain
rustup target add x86_64-unknown-linux-musl   # Static Linux
rustup target add x86_64-apple-darwin          # macOS
rustup target add x86_64-pc-windows-msvc       # Windows
rustup target add aarch64-unknown-linux-gnu    # ARM Linux

# Build for specific target
cargo build --release --target x86_64-unknown-linux-musl

# Using cross (easier, uses Docker)
cargo install cross
cross build --release --target x86_64-unknown-linux-musl
cross build --release --target aarch64-unknown-linux-gnu

Docker Deployment ​

dockerfile
# Multi-stage build for minimal image
FROM rust:1.75 as builder

WORKDIR /app
COPY . .
RUN cargo build --release

# Minimal runtime image
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/my_app /usr/local/bin/my_app

EXPOSE 8080
CMD ["my_app"]
dockerfile
# Even smaller: static musl build + scratch
FROM rust:1.75 as builder

RUN rustup target add x86_64-unknown-linux-musl
WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/my_app /my_app
EXPOSE 8080
ENTRYPOINT ["/my_app"]

Publishing to crates.io ​

bash
# Verify everything is ready
cargo package --list    # Check included files
cargo doc --open        # Verify documentation
cargo publish --dry-run # Test the publish

# Publish
cargo login             # Login with API token from crates.io
cargo publish           # Publish the crate!

# Versioning
# In Cargo.toml, bump version:
# 0.1.0 → 0.1.1 (patch: bug fix)
# 0.1.0 → 0.2.0 (minor: new feature, backward compatible)
# 0.1.0 → 1.0.0 (major: breaking change)

GitHub Actions Release Pipeline ​

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

jobs:
  build:
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            ext: ""
          - target: x86_64-apple-darwin
            os: macos-latest
            ext: ""
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            ext: ".exe"
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: my_app-${{ matrix.target }}
          path: target/${{ matrix.target }}/release/my_app${{ matrix.ext }}

  release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            my_app-*/my_app*
          generate_release_notes: true

Systemd Service (Linux Deployment) ​

ini
# /etc/systemd/system/my_app.service
[Unit]
Description=My Rust Application
After=network.target

[Service]
Type=simple
User=appuser
ExecStart=/usr/local/bin/my_app
Restart=on-failure
RestartSec=5
Environment=RUST_LOG=info
Environment=PORT=8080

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable my_app
sudo systemctl start my_app
sudo systemctl status my_app
journalctl -u my_app -f  # View logs

Release Checklist ​

  • [ ] All tests pass (cargo test --release)
  • [ ] No clippy warnings (cargo clippy -- -D warnings)
  • [ ] Code formatted (cargo fmt --check)
  • [ ] CHANGELOG updated
  • [ ] Version bumped in Cargo.toml
  • [ ] Documentation builds (cargo doc)
  • [ ] Release profile optimized
  • [ ] Git tag created (git tag v0.1.0)
  • [ ] CI builds and publishes artifacts
  • [ ] Verify published package / binary works

Built with VitePress | Rust SOP Documentation