diff --git a/services/blob/src/config.rs b/services/blob/src/config.rs
--- a/services/blob/src/config.rs
+++ b/services/blob/src/config.rs
@@ -24,6 +24,15 @@
   #[arg(env = "IDENTITY_SERVICE_ENDPOINT")]
   #[arg(long, default_value = "http://localhost:50054")]
   pub identity_endpoint: String,
+
+  #[clap(subcommand)]
+  pub command: Option<Command>,
+}
+
+#[derive(clap::Subcommand)]
+pub enum Command {
+  Server,
+  Cleanup,
 }
 
 /// Stores configuration parsed from command-line arguments
diff --git a/services/blob/src/main.rs b/services/blob/src/main.rs
--- a/services/blob/src/main.rs
+++ b/services/blob/src/main.rs
@@ -8,6 +8,7 @@
 
 use anyhow::Result;
 use comm_services_lib::auth::AuthService;
+use config::Command;
 use tracing_subscriber::filter::{EnvFilter, LevelFilter};
 
 use crate::service::BlobServiceConfig;
@@ -33,14 +34,15 @@
   let s3 = s3::S3Client::new(&aws_config);
   let auth_service = AuthService::new(&aws_config, &config.identity_endpoint);
 
-  let blob_service = service::BlobService::new(
-    db,
-    s3,
-    BlobServiceConfig {
-      instant_delete_orphaned_blobs: true,
-      ..Default::default()
-    },
-  );
+  let blob_service =
+    service::BlobService::new(db, s3, BlobServiceConfig::default());
 
-  crate::http::run_http_server(blob_service, auth_service).await
+  match &config.command {
+    Some(Command::Cleanup) => blob_service.perform_cleanup().await?,
+    None | Some(Command::Server) => {
+      crate::http::run_http_server(blob_service, auth_service).await?
+    }
+  };
+
+  Ok(())
 }