Last updated: April 13, 2025
Table of Contents
1. Introduction: Automating Kubernetes with Rust
Kubernetes (k8s) has become the standard for container orchestration, but managing its resources
programmatically often involves interacting with its extensive REST API. While kubectl
is
excellent for manual operations, building controllers, operators, or custom automation tools requires a client
library.
Rust, with its performance, safety, and strong typing, is a great choice for building reliable Kubernetes
tooling. The kube-rs
crate provides a powerful and idiomatic Rust client for the Kubernetes API.
This guide introduces kube-rs
and demonstrates how to set up a client, list resources (like
Pods), and understand the basics of interacting with the Kubernetes API from a Rust application.
2. The kube-rs
Crate
kube-rs
is the predominant Rust client for Kubernetes. Key features include:
- Asynchronous API built on Tokio.
- Strongly typed representations of Kubernetes API objects (Pods, Deployments, Services, etc.) generated from the official OpenAPI spec.
- Helper functions for common operations (list, get, create, update, delete, watch).
- Automatic discovery of cluster configuration (from
~/.kube/config
or in-cluster service account). - Support for Custom Resource Definitions (CRDs).
It aims to provide a safe, ergonomic, and efficient way to interact with Kubernetes from Rust.
3. Prerequisites
- Rust and Cargo installed (see Getting Started with Rust).
- Access to a Kubernetes cluster. This can be:
- A local cluster via Minikube, Kind, k3d, or Docker Desktop.
- A managed cloud cluster (EKS, GKE, AKS).
kubectl
configured to communicate with your cluster (kube-rs
uses the same configuration). Verify withkubectl cluster-info
.- Basic understanding of Kubernetes concepts (Pods, Deployments, etc. - see Getting Started with Kubernetes).
- Familiarity with Rust's
async/await
and Tokio.
4. Project Setup
Create a new Rust binary project:
cargo new rust-k8s-client
cd rust-k8s-client
Add the necessary dependencies to your Cargo.toml
:
[package]
name = "rust-k8s-client"
version = "0.1.0"
edition = "2021"
[dependencies]
kube = { version = "0.88", features = ["runtime", "derive"] } # Check crates.io for latest version
k8s-openapi = { version = "0.20.0", features = ["v1_28"] } # Match your cluster API version
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0" }
serde_json = "1.0"
anyhow = "1.0" # For easier error handling in examples
Important:
- Adjust the
kube
version as needed. - The
k8s-openapi
version and feature flag (e.g.,"v1_28"
) should ideally match the API version of the Kubernetes cluster you are targeting. Usekubectl version
to check your cluster version. - We add
anyhow
for convenient error handling in these examples.
Alternatively, use cargo add
:
cargo add kube --features runtime,derive
cargo add k8s-openapi --features v1_28 # Adjust version feature
cargo add tokio --features full
cargo add serde serde_json anyhow
5. Creating a Kubernetes Client
The first step in any interaction is creating a Client
instance. kube-rs
makes this
easy by automatically inferring the configuration.
Modify src/main.rs
:
use kube::{Client, Config};
use anyhow::Result; // Using anyhow for convenient Result type
#[tokio::main]
async fn main() -> Result<()> { // Return anyhow::Result
println!("Attempting to create Kubernetes client...");
// Infer the runtime environment and try to create a Kubernetes Client
let client = Client::try_default().await?; // The '?' propagates errors
println!("Successfully created Kubernetes client.");
// We'll add more logic here later...
Ok(())
}
Client::try_default().await
attempts to load configuration from:
- In-cluster service account (if running inside a Kubernetes Pod).
- Your local kubeconfig file (typically
~/.kube/config
).
Run this with cargo run
. If your kubectl
is configured correctly, it should print
the success message.
6. Listing Resources (Pods Example)
To work with specific Kubernetes resources (like Pods, Deployments, etc.), you use the Api
type
from kube-rs
, parameterized with the corresponding struct from k8s-openapi
.
Let's list all Pods in the default namespace:
use kube::{Client, Api};
use k8s_openapi::api::core::v1::Pod; // Import the Pod struct definition
use anyhow::Result;
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::try_default().await?;
println!("Created Kubernetes client.");
// Create an API handle for Pods in the default namespace
let pods: Api = Api::default_namespaced(client.clone()); // Use client.clone() if client is used later
println!("\nListing Pods in default namespace:");
// List pods using ListParams (can be customized for label selectors etc.)
let pod_list = pods.list(&Default::default()).await?;
if pod_list.items.is_empty() {
println!("No pods found in the default namespace.");
} else {
for p in pod_list.items {
let pod_name = p.metadata.name.as_deref().unwrap_or("");
let phase = p.status.and_then(|s| s.phase).unwrap_or_else(|| "Unknown".to_string());
println!(" - Pod: {}, Status: {}", pod_name, phase);
}
}
Ok(())
}
Key points:
use k8s_openapi::api::core::v1::Pod;
: Imports the struct representing a Pod.Api::
: Creates an API handle specifically for Pod objects within the client's default namespace (as configured in your kubeconfig). Use::default_namespaced(client) Api::namespaced(client, "my-namespace")
for specific namespaces orApi::all(client)
for cluster-scoped resources.pods.list(&Default::default()).await?
: Lists the resources.&Default::default()
provides default listing parameters (no filters).- The result is an
ObjectList
which contains a vectoritems
ofPod
structs. - We access pod metadata (like
name
) and status (likephase
). Note the use of.as_deref().unwrap_or(...)
and.and_then(...).unwrap_or_else(...)
for safe handling of optional fields.
Run cargo run
. It should list the pods running in your default namespace (if any).
7. Basic Operations (Conceptual)
kube-rs
provides methods on the Api
handle for common CRUD (Create, Read, Update,
Delete) operations.
7.1 Getting a Specific Resource
// Assuming 'pods' is Api for the correct namespace
let pod_name = "my-specific-pod";
match pods.get(pod_name).await {
Ok(pod) => println!("Found pod: {:?}", pod.spec),
Err(e) => eprintln!("Error getting pod {}: {}", pod_name, e),
}
7.2 Creating Resources
You typically define the resource using structs from k8s-openapi
and then use
api.create()
.
// Conceptual - Requires more setup with PodSpec, metadata etc.
use k8s_openapi::api::core::v1::PodSpec;
let new_pod: Pod = Pod {
metadata: ObjectMeta { name: Some("my-new-rust-pod".to_string()), ..Default::default() },
spec: Some(PodSpec {
containers: vec![Container {
name: "nginx".to_string(),
image: Some("nginx:alpine".to_string()),
..Default::default()
}],
..Default::default()
}),
..Default::default()
};
let pp = PostParams::default();
match pods.create(&pp, &new_pod).await {
Ok(created_pod) => println!("Created pod: {}", created_pod.metadata.name.unwrap()),
Err(e) => eprintln!("Error creating pod: {}", e),
}
7.3 Updating Resources
Usually involves getting the resource, modifying its spec, and using api.replace()
or
api.patch()
.
7.4 Deleting Resources
// Assuming 'pods' is Api for the correct namespace
let pod_to_delete = "my-new-rust-pod";
let dp = DeleteParams::default();
match pods.delete(pod_to_delete, &dp).await {
Ok(_) => println!("Pod {} deleted (or deletion started)", pod_to_delete),
Err(e) => eprintln!("Error deleting pod {}: {}", pod_to_delete, e),
}
8. Error Handling
Most kube-rs
API methods return kube::Result
(which is an alias for
Result
). You should handle these results appropriately using match
,
?
, or libraries like anyhow
.
kube::Error
provides detailed information about API errors, including HTTP status codes and
Kubernetes API error responses.
9. Conclusion
kube-rs
provides a type-safe, asynchronous, and idiomatic way to interact with the Kubernetes
API from Rust applications. By leveraging the Client
for connection and the generic
Api<T>
handle for specific resource types defined in k8s-openapi
, you can
build powerful controllers, operators, CLIs, and other tools that manage Kubernetes resources reliably.
This guide covered the essentials of setting up a client and listing resources. Exploring creating, updating, deleting, and watching resources are the next logical steps in building more sophisticated Kubernetes integrations with Rust.
10. Related Articles
- Getting Started with Kubernetes
- Getting Started with Minikube
- Getting Started with Rust
- Writing Concurrent Applications in Rust
- Error Handling in Rust
- Docker Commands Guide
- Docker Compose Guide
- Infrastructure as Code Explained
- Common Rust Crates for Web Development