diff --git a/Cargo.lock b/Cargo.lock
index a08817c5..e490e736 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3700,7 +3700,9 @@ dependencies = [
  "axum",
  "chrono",
  "clap 4.3.14",
+ "dotenvy",
  "ethers",
+ "hex",
  "hyper",
  "oz-api",
  "serde",
diff --git a/Readme.md b/Readme.md
index 2f2d4139..0ce1bde1 100644
--- a/Readme.md
+++ b/Readme.md
@@ -59,6 +59,22 @@ Fetch the [postgres](https://hub.docker.com/_/postgres) docker image before runn
 docker pull postgres
 ```
 
+## Micro OZ
+Sequencer depends on Openzeppelin Defender. In order to run it locally we provide a mock version of this service under the `micro-oz` crate.
+
+To run it, execute the following command:
+```shell
+cargo run -p micro-oz
+```
+
+By default it'll run on port `9876` and start with default values compatible with anvil.
+Check out
+```shell
+cargo run -p micro-oz -- --help
+```
+
+for more configuration options.
+
 ### Worldcoin id contracts
 Worldcoin id contracts are ethereum smart contracts that are used by the sequencer
 
diff --git a/crates/micro-oz/Cargo.toml b/crates/micro-oz/Cargo.toml
index 5e0a1463..ce284bb8 100644
--- a/crates/micro-oz/Cargo.toml
+++ b/crates/micro-oz/Cargo.toml
@@ -11,7 +11,9 @@ async-trait = "0.1.71"
 axum = "0.6.19"
 chrono = "0.4.26"
 clap = { version = "4.3.14", features = ["env", "derive"] }
+dotenvy = "0.15.0"
 ethers = { version = "1.0.0", features = ["openssl"] }
+hex = "0.4.3"
 hyper = "0.14.27"
 oz-api = { path = "../oz-api" }
 serde = "1.0.171"
diff --git a/crates/micro-oz/src/lib.rs b/crates/micro-oz/src/lib.rs
index c83f7c6d..be35b7b1 100644
--- a/crates/micro-oz/src/lib.rs
+++ b/crates/micro-oz/src/lib.rs
@@ -17,7 +17,7 @@ pub mod server;
 
 const DEFAULT_GAS_LIMIT: u32 = 1_000_000;
 
-pub use self::server::{spawn, ServerHandle};
+pub use self::server::{spawn, spawn_on_random_port, ServerHandle};
 
 type PinheadSigner = SignerMiddleware<Provider<Http>, LocalWallet>;
 
diff --git a/crates/micro-oz/src/main.rs b/crates/micro-oz/src/main.rs
new file mode 100644
index 00000000..97e834c4
--- /dev/null
+++ b/crates/micro-oz/src/main.rs
@@ -0,0 +1,52 @@
+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+
+use clap::Parser;
+use ethers::prelude::k256::ecdsa::SigningKey;
+
+#[derive(Debug, Clone, Parser)]
+#[clap(rename_all = "kebab-case")]
+struct Args {
+    /// The port at which to serve
+    ///
+    /// Set to 0 to use a random port
+    #[clap(short, long, env, default_value = "9876")]
+    port:       u16,
+    /// The RPC url to use
+    ///
+    /// Uses a default value compatible with anvil
+    #[clap(short, long, env, default_value = "http://127.0.0.1:8545")]
+    rpc_url:    String,
+    /// A hex encoded private key
+    ///
+    /// By default uses a private key used by anvil
+    #[clap(
+        short,
+        long,
+        env,
+        default_value = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
+    )]
+    secret_key: String,
+}
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+    dotenvy::dotenv().ok();
+    tracing_subscriber::fmt::init();
+
+    let args = Args::parse();
+
+    let private_key = args.secret_key.trim_start_matches("0x");
+    let private_key = hex::decode(private_key)?;
+
+    let signing_key = SigningKey::from_bytes(&private_key)?;
+
+    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), args.port);
+
+    let handle = micro_oz::spawn(addr, args.rpc_url, signing_key).await?;
+
+    tracing::info!("Micro OZ listening on {}", handle.endpoint());
+
+    handle.wait().await;
+
+    Ok(())
+}
diff --git a/crates/micro-oz/src/server.rs b/crates/micro-oz/src/server.rs
index 76f2c9ef..db603a66 100644
--- a/crates/micro-oz/src/server.rs
+++ b/crates/micro-oz/src/server.rs
@@ -91,6 +91,12 @@ impl ServerHandle {
         format!("http://{}", self.addr)
     }
 
+    pub async fn wait(self) {
+        if let Err(e) = self.server_join_handle.await {
+            tracing::error!("Server error: {:?}", e);
+        }
+    }
+
     pub async fn shutdown(self) {
         self.shutdown_notify.notify_waiters();
 
@@ -100,7 +106,11 @@ impl ServerHandle {
     }
 }
 
-pub async fn spawn(rpc_url: String, secret_key: SigningKey) -> anyhow::Result<ServerHandle> {
+pub async fn spawn(
+    addr: SocketAddr,
+    rpc_url: String,
+    secret_key: SigningKey,
+) -> anyhow::Result<ServerHandle> {
     let pinhead = Pinhead::new(rpc_url, secret_key).await?;
 
     let router = Router::new()
@@ -108,7 +118,6 @@ pub async fn spawn(rpc_url: String, secret_key: SigningKey) -> anyhow::Result<Se
         .route("/txs/:tx_id", get(query_transaction))
         .with_state(pinhead.clone());
 
-    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
     let listener = TcpListener::bind(addr).context("Failed to bind random port")?;
     let local_addr = listener.local_addr()?;
 
@@ -132,3 +141,12 @@ pub async fn spawn(rpc_url: String, secret_key: SigningKey) -> anyhow::Result<Se
         server_join_handle,
     })
 }
+
+pub async fn spawn_on_random_port(
+    rpc_url: String,
+    secret_key: SigningKey,
+) -> anyhow::Result<ServerHandle> {
+    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
+
+    spawn(addr, rpc_url, secret_key).await
+}
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index a530a014..a593a3e9 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -638,7 +638,7 @@ pub async fn spawn_deps(
     let chain = chain?;
 
     let signing_key = SigningKey::from_bytes(chain.private_key.as_bytes())?;
-    let micro_oz = micro_oz::spawn(chain.anvil.endpoint(), signing_key).await?;
+    let micro_oz = micro_oz::spawn_on_random_port(chain.anvil.endpoint(), signing_key).await?;
 
     let insertion_provers = insertion_provers
         .into_iter()