diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index c2575267c..de32edc2f 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -172,6 +172,7 @@ impl Config {
     ///
     /// # Example
     /// ```rust
+    /// # use embassy_net::Config;
     /// let _cfg = Config::dhcpv4(Default::default());
     /// ```
     #[cfg(feature = "dhcpv4")]
@@ -226,6 +227,7 @@ struct Inner<D: Driver> {
     static_v6: Option<StaticConfigV6>,
     #[cfg(feature = "dhcpv4")]
     dhcp_socket: Option<SocketHandle>,
+    config_waker: WakerRegistration,
     #[cfg(feature = "dns")]
     dns_socket: SocketHandle,
     #[cfg(feature = "dns")]
@@ -297,6 +299,7 @@ impl<D: Driver + 'static> Stack<D> {
             static_v6: None,
             #[cfg(feature = "dhcpv4")]
             dhcp_socket: None,
+            config_waker: WakerRegistration::new(),
             #[cfg(feature = "dns")]
             dns_socket: socket.sockets.add(dns::Socket::new(
                 &[],
@@ -363,6 +366,55 @@ impl<D: Driver + 'static> Stack<D> {
         v4_up || v6_up
     }
 
+    /// Wait for the network stack to obtain a valid IP configuration.
+    ///
+    /// ## Notes:
+    /// - Ensure [`Stack::run`] has been called before using this function.
+    ///
+    /// - This function may never return (e.g. if no configuration is obtained through DHCP).
+    /// The caller is supposed to handle a timeout for this case.
+    ///
+    /// ## Example
+    /// ```ignore
+    /// let config = embassy_net::Config::dhcpv4(Default::default());
+    ///// Init network stack
+    /// let stack = &*make_static!(embassy_net::Stack::new(
+    ///    device,
+    ///    config,
+    ///    make_static!(embassy_net::StackResources::<2>::new()),
+    ///    seed
+    /// ));
+    /// // Launch network task that runs `stack.run().await`
+    /// spawner.spawn(net_task(stack)).unwrap();
+    /// // Wait for DHCP config
+    /// stack.wait_config_up().await;
+    /// // use the network stack
+    /// // ...
+    /// ```
+    pub async fn wait_config_up(&self) {
+        // If the config is up already, we can return immediately.
+        if self.is_config_up() {
+            return;
+        }
+
+        poll_fn(|cx| {
+            if self.is_config_up() {
+                Poll::Ready(())
+            } else {
+                // If the config is not up, we register a waker that is woken up
+                // when a config is applied (static or DHCP).
+                trace!("Waiting for config up");
+
+                self.with_mut(|_, i| {
+                    i.config_waker.register(cx.waker());
+                });
+
+                Poll::Pending
+            }
+        })
+        .await;
+    }
+
     /// Get the current IPv4 configuration.
     ///
     /// If using DHCP, this will be None if DHCP hasn't been able to
@@ -706,6 +758,8 @@ impl<D: Driver + 'static> Inner<D> {
         s.sockets
             .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
             .update_servers(&dns_servers[..]);
+
+        self.config_waker.wake();
     }
 
     fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs
index 393e60b73..5f1e62d0a 100644
--- a/examples/stm32f4/src/bin/eth.rs
+++ b/examples/stm32f4/src/bin/eth.rs
@@ -79,7 +79,10 @@ async fn main(spawner: Spawner) -> ! {
     ));
 
     // Launch network task
-    unwrap!(spawner.spawn(net_task(&stack)));
+    unwrap!(spawner.spawn(net_task(stack)));
+
+    // Ensure DHCP configuration is up before trying connect
+    stack.wait_config_up().await;
 
     info!("Network task initialized");
 
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index f0e280c35..01c38106e 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -80,7 +80,10 @@ async fn main(spawner: Spawner) -> ! {
     ));
 
     // Launch network task
-    unwrap!(spawner.spawn(net_task(&stack)));
+    unwrap!(spawner.spawn(net_task(stack)));
+
+    // Ensure DHCP configuration is up before trying connect
+    stack.wait_config_up().await;
 
     info!("Network task initialized");
 
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs
index 763520ab8..c32e0fdb5 100644
--- a/examples/stm32h5/src/bin/eth.rs
+++ b/examples/stm32h5/src/bin/eth.rs
@@ -101,6 +101,9 @@ async fn main(spawner: Spawner) -> ! {
     // Launch network task
     unwrap!(spawner.spawn(net_task(&stack)));
 
+    // Ensure DHCP configuration is up before trying connect
+    stack.wait_config_up().await;
+
     info!("Network task initialized");
 
     // Then we can use it!
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs
index 26a386e49..e691c6d06 100644
--- a/examples/stm32h7/src/bin/eth.rs
+++ b/examples/stm32h7/src/bin/eth.rs
@@ -83,6 +83,9 @@ async fn main(spawner: Spawner) -> ! {
     // Launch network task
     unwrap!(spawner.spawn(net_task(&stack)));
 
+    // Ensure DHCP configuration is up before trying connect
+    stack.wait_config_up().await;
+
     info!("Network task initialized");
 
     // Then we can use it!
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs
index 6664410c8..ebef54c3c 100644
--- a/examples/stm32h7/src/bin/eth_client.rs
+++ b/examples/stm32h7/src/bin/eth_client.rs
@@ -82,13 +82,13 @@ async fn main(spawner: Spawner) -> ! {
     ));
 
     // Launch network task
-    unwrap!(spawner.spawn(net_task(&stack)));
+    unwrap!(spawner.spawn(net_task(stack)));
+
+    // Ensure DHCP configuration is up before trying connect
+    stack.wait_config_up().await;
 
     info!("Network task initialized");
 
-    // To ensure DHCP configuration before trying connect
-    Timer::after(Duration::from_secs(20)).await;
-
     static STATE: TcpClientState<1, 1024, 1024> = TcpClientState::new();
     let client = TcpClient::new(&stack, &STATE);