CI/CD for Rust: Automating Build and Deployment from Scratch

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Your Rust Project Deserves a Real CI/CD?

\n\n

Imagine: you're writing in Rust, the compiler is your best friend, the borrow checker keeps you on your toes, and the code runs like lightning. But when it comes to deployment, the magic ends. Running tests manually, building on your local machine, uploading the binary to a server via SCP — that's a road to nowhere. Especially when the project grows and a second developer joins the team.

\n\n

CI/CD (Continuous Integration / Continuous Deployment) is not just a buzzword. It's a system that takes over the routine: run tests, check code style, build a release version, and send it where it needs to work. For Rust, with its emphasis on reliability and performance, build automation is a natural extension of the language's philosophy.

\n\n

In this article, we'll set up a CI/CD pipeline for a Rust application from scratch using GitHub Actions. You'll learn how to organize testing, linting, building binaries for different platforms, and deployment. All with real code examples you can copy into your project.

\n\n

What Will We Automate?

\n\n

Before writing code, let's define our goals. Our pipeline will do four things:

\n\n
    \n
  • Linting and Formatting — to ensure the code meets Rust standards and contains no common errors.
  • \n
  • Testing — running all unit tests and, if any, integration tests.
  • \n
  • Building the Binary — compiling an optimized (release) version.
  • \n
  • Deployment — uploading the built artifact to a server or to GitHub Releases.
  • \n
\n\n

All of this will happen automatically on every push to the main branch or when a pull request is created. Sounds like magic? It's actually just YAML files and a couple of Cargo commands.

\n\n

Step 1: Project Preparation

\n\n

For the example, we'll take a simple web server using Actix-web. But the pipeline is universal: it's suitable for any Rust project, be it a CLI utility, library, or microservice.

\n\n

Let's create a new project:

\n\n
cargo new rust-ci-cd-demo --bin\ncd rust-ci-cd-demo
\n\n

Add dependencies to Cargo.toml:

\n\n
[dependencies]\nactix-web = "4"\nserde = { version = "1", features = ["derive"] }
\n\n

And the simplest code in src/main.rs:

\n\n
use actix_web::{get, App, HttpResponse, HttpServer, Responder};\n\n#[get("/")]\nasync fn hello() -> impl Responder {\n    HttpResponse::Ok().body("Hello from Rust CI/CD!")\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    HttpServer::new(|| {\n        App::new().service(hello)\n    })\n    .bind(("0.0.0.0", 8080))?\n    .run()\n    .await\n}
\n\n

Add a simple test in src/main.rs (at the bottom of the file):

\n\n
#[cfg(test)]\nmod tests {\n    use actix_web::{test, App, web};\n    use crate::hello;\n\n    #[actix_web::test]\n    async fn test_hello_endpoint() {\n        let app = test::init_service(App::new().service(hello)).await;\n        let req = test::TestRequest::get().uri("/").to_request();\n        let resp = test::call_service(&app, req).await;\n        assert!(resp.status().is_success());\n    }\n}
\n\n

Everything is ready. Now for the interesting part — setting up CI/CD.

\n\n

Step 2: Creating a GitHub Actions Workflow

\n\n

GitHub Actions is a built-in CI/CD tool for GitHub. It works based on YAML files located in the .github/workflows/ directory. Let's create it:

\n\n
mkdir -p .github/workflows\ntouch .github/workflows/ci.yml
\n\n

Open ci.yml and start writing the pipeline. Let's call it "Rust CI/CD Pipeline".

\n\n

Basic Trigger and Environment

\n\n
name: Rust CI/CD Pipeline\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\nenv:\n  CARGO_TERM_CO                

Blogs

Book Recommendations