Use this file to discover all available pages before exploring further.
Docker Swarm mode turns a group of Docker hosts into a single, fault-tolerant cluster. Bollard provides a fully asynchronous API covering every aspect of Swarm orchestration — from initialising the cluster and deploying replicated services, to managing nodes, rotating secrets, and streaming task logs. All methods are available directly on the Docker client struct and return Futures or Streams that integrate naturally with Tokio.
All Swarm API endpoints require the Docker daemon to be running in Swarm mode. Call init_swarm or run docker swarm init before using any of the APIs described below.
use bollard::Docker;use bollard::models::SwarmInitRequest;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let config = SwarmInitRequest { // Address advertised to other nodes for API access advertise_addr: Some("192.168.1.10".to_string()), // Address and port the manager listens on for intra-swarm traffic listen_addr: Some("0.0.0.0:2377".to_string()), ..Default::default() }; let node_id = docker.init_swarm(config).await.unwrap(); println!("Swarm initialised, node ID: {}", node_id);}
use bollard::Docker;use bollard::models::SwarmJoinRequest;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let config = SwarmJoinRequest { advertise_addr: Some("192.168.1.11".to_string()), // Obtain the token from `inspect_swarm` on the manager join_token: Some("SWMTKN-1-...".to_string()), // Address of one or more manager nodes remote_addrs: Some(vec!["192.168.1.10:2377".to_string()]), ..Default::default() }; docker.join_swarm(config).await.unwrap();}
use bollard::Docker;use bollard::query_parameters::LeaveSwarmOptionsBuilder;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); // force: true is required to remove a manager node let options = LeaveSwarmOptionsBuilder::default() .force(true) .build(); docker.leave_swarm(Some(options)).await.unwrap();}
Update requires the current version index obtained from inspect_swarm:
use bollard::Docker;use bollard::query_parameters::UpdateSwarmOptionsBuilder;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let swarm = docker.inspect_swarm().await.unwrap(); let version = swarm.version.unwrap().index.unwrap(); let spec = swarm.spec.unwrap(); let options = UpdateSwarmOptionsBuilder::default() .version(version as i64) .build(); docker.update_swarm(spec, options).await.unwrap();}
use bollard::Docker;use bollard::query_parameters::ListServicesOptionsBuilder;use std::collections::HashMap;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let mut filters = HashMap::new(); // Filter by scheduling mode filters.insert("mode", vec!["replicated"]); let options = ListServicesOptionsBuilder::default() .filters(&filters) .build(); let services = docker.list_services(Some(options)).await.unwrap(); for service in services { println!("Service: {:?}", service.spec.and_then(|s| s.name)); }}
Use ServiceSpec from bollard::service to describe the desired state. The task_template field holds the ContainerSpec (image, commands, environment, mounts), while mode controls replica count or global scheduling.
use bollard::Docker;use bollard::service::{ ServiceSpec, ServiceSpecMode, ServiceSpecModeReplicated, TaskSpec, TaskSpecContainerSpec,};#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let service = ServiceSpec { name: Some(String::from("my-web-service")), mode: Some(ServiceSpecMode { replicated: Some(ServiceSpecModeReplicated { replicas: Some(3), }), ..Default::default() }), task_template: Some(TaskSpec { container_spec: Some(TaskSpecContainerSpec { image: Some(String::from("nginx:alpine")), ..Default::default() }), ..Default::default() }), ..Default::default() }; // Optional: pass registry credentials for private images let credentials = None; let response = docker.create_service(service, credentials).await.unwrap(); println!("Created service ID: {:?}", response.id);}
use bollard::Docker;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); docker.delete_service("my-web-service").await.unwrap();}
Common use cases: promoting a worker to manager, draining a node for maintenance.
use bollard::Docker;use bollard::query_parameters::UpdateNodeOptionsBuilder;use bollard::models::{NodeSpec, NodeSpecAvailabilityEnum, NodeSpecRoleEnum};#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); // Drain the node so no new tasks are scheduled on it let spec = NodeSpec { availability: Some(NodeSpecAvailabilityEnum::DRAIN), name: Some("worker-01".to_string()), role: Some(NodeSpecRoleEnum::WORKER), ..Default::default() }; // Version must match the current node version let options = UpdateNodeOptionsBuilder::default() .version(2) .build(); docker.update_node("my-node-id", spec, options).await.unwrap();}
A task is a running container scheduled by the Swarm onto a specific node. Tasks are the atomic unit of scheduling — one task per replica. Tasks are read-only through the API; to change task count, update the parent service.
Secrets store sensitive data (TLS certificates, passwords, API keys) and make them available to service tasks inside containers at /run/secrets/<name>. Secret data is encrypted at rest in the Swarm Raft log.
Secret data must be base64-encoded before being passed to create_secret. The data field on SecretSpec expects a standard base64 string.
The data field cannot be changed via update_secret — only metadata such as labels can be updated. To rotate secret data you must create a new secret and update any services referencing the old one.
use bollard::Docker;use bollard::query_parameters::UpdateSecretOptionsBuilder;use std::collections::HashMap;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); let existing = docker.inspect_secret("db-password").await.unwrap(); let version = existing.version.unwrap().index.unwrap(); let mut spec = existing.spec.unwrap(); // Update labels (data cannot be changed) let mut labels = HashMap::new(); labels.insert(String::from("rotated-at"), String::from("2024-01-15")); spec.labels = Some(labels); let options = UpdateSecretOptionsBuilder::default() .version(version as i64) .build(); docker.update_secret("db-password", spec, options).await.unwrap();}
use bollard::Docker;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); docker.delete_secret("db-password").await.unwrap();}
Configs are similar to secrets but store non-sensitive configuration data (nginx config files, application settings). Config data is not encrypted at rest and is visible in plain text via the API.
use bollard::Docker;use bollard::query_parameters::{ListConfigsOptionsBuilder, UpdateConfigOptionsBuilder};use std::collections::HashMap;#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); // List configs with a label filter let mut filters: HashMap<String, Vec<String>> = HashMap::new(); filters.insert("label".into(), vec!["config-label=label-value".into()]); let options = ListConfigsOptionsBuilder::default() .filters(&filters) .build(); let configs = docker.list_configs(Some(options)).await.unwrap(); // Inspect by name or ID let config = docker.inspect_config("nginx-config").await.unwrap(); // Update metadata (labels only — data is immutable) let version = config.version.unwrap().index.unwrap(); let mut spec = config.spec.unwrap(); let mut labels = HashMap::new(); labels.insert(String::from("env"), String::from("production")); spec.labels = Some(labels); let update_opts = UpdateConfigOptionsBuilder::default() .version(version as i64) .build(); docker .update_config("nginx-config", spec, update_opts) .await .unwrap(); // Delete docker.delete_config("nginx-config").await.unwrap();}
The snippet below ties everything together — it initialises a single-node Swarm and immediately deploys a replicated nginx service.
use bollard::Docker;use bollard::models::SwarmInitRequest;use bollard::service::{ ServiceSpec, ServiceSpecMode, ServiceSpecModeReplicated, TaskSpec, TaskSpecContainerSpec,};#[tokio::main]async fn main() { let docker = Docker::connect_with_socket_defaults().unwrap(); // 1. Initialise the Swarm let init_config = SwarmInitRequest { advertise_addr: Some("127.0.0.1".to_string()), listen_addr: Some("0.0.0.0:2377".to_string()), ..Default::default() }; let node_id = docker.init_swarm(init_config).await.unwrap(); println!("Node ID: {}", node_id); // 2. Deploy a replicated nginx service let service_spec = ServiceSpec { name: Some(String::from("web")), mode: Some(ServiceSpecMode { replicated: Some(ServiceSpecModeReplicated { replicas: Some(2), }), ..Default::default() }), task_template: Some(TaskSpec { container_spec: Some(TaskSpecContainerSpec { image: Some(String::from("nginx:alpine")), ..Default::default() }), ..Default::default() }), ..Default::default() }; let response = docker.create_service(service_spec, None).await.unwrap(); println!("Service created: {:?}", response.id); // 3. List all running services let services = docker.list_services(None).await.unwrap(); println!("Running services: {}", services.len());}
When running integration tests against a live daemon, use the test_swarm Cargo feature flag to gate swarm-specific tests: cargo test --features test_swarm.