Index: uspace/srv/bd/vbd/disk.c
===================================================================
--- uspace/srv/bd/vbd/disk.c	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/bd/vbd/disk.c	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2024 Jiri Svoboda
+ * Copyright (c) 2025 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,4 +35,5 @@
 
 #include <adt/list.h>
+#include <bd.h>
 #include <bd_srv.h>
 #include <block.h>
@@ -71,4 +72,5 @@
 static errno_t vbds_bd_get_block_size(bd_srv_t *, size_t *);
 static errno_t vbds_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+static errno_t vbds_bd_eject(bd_srv_t *);
 
 static errno_t vbds_bsa_translate(vbds_part_t *, aoff64_t, size_t, aoff64_t *);
@@ -93,5 +95,6 @@
 	.write_blocks = vbds_bd_write_blocks,
 	.get_block_size = vbds_bd_get_block_size,
-	.get_num_blocks = vbds_bd_get_num_blocks
+	.get_num_blocks = vbds_bd_get_num_blocks,
+	.eject = vbds_bd_eject
 };
 
@@ -1070,4 +1073,41 @@
 
 	return EOK;
+}
+
+static errno_t vbds_bd_eject(bd_srv_t *bd)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+	async_sess_t *sess;
+	bd_t *bdc;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_eject()");
+
+	fibril_rwlock_read_lock(&part->lock);
+
+	sess = loc_service_connect(part->disk->svc_id, INTERFACE_BLOCK, 0);
+	if (sess == NULL) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "vbds_bd_eject() - failed connect");
+		fibril_rwlock_read_unlock(&part->lock);
+		return EIO;
+	}
+
+	rc = bd_open(sess, &bdc);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "vbds_bd_eject() - failed open");
+		async_hangup(sess);
+		fibril_rwlock_read_unlock(&part->lock);
+		return EIO;
+	}
+
+	rc = bd_eject(bdc);
+
+	bd_close(bdc);
+	async_hangup(sess);
+
+	fibril_rwlock_read_unlock(&part->lock);
+	return rc;
 }
 
Index: uspace/srv/system/system.c
===================================================================
--- uspace/srv/system/system.c	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/system/system.c	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -533,5 +533,5 @@
 
 	for (i = 0; i < nparts; i++) {
-		rc = vol_part_eject(vol, part_ids[i]);
+		rc = vol_part_eject(vol, part_ids[i], vef_none);
 		if (rc != EOK) {
 			log_msg(LOG_DEFAULT, LVL_ERROR, "Error ejecting "
Index: uspace/srv/volsrv/meson.build
===================================================================
--- uspace/srv/volsrv/meson.build	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/volsrv/meson.build	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2015 Jiri Svoboda
+# Copyright (c) 2025 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'block', 'label', 'sif' ]
+deps = [ 'block', 'device', 'label', 'sif' ]
 
 src = files(
Index: uspace/srv/volsrv/part.c
===================================================================
--- uspace/srv/volsrv/part.c	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/volsrv/part.c	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2024 Jiri Svoboda
+ * Copyright (c) 2025 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,4 +36,5 @@
 
 #include <adt/list.h>
+#include <bd.h>
 #include <errno.h>
 #include <fibril_synch.h>
@@ -642,5 +643,38 @@
 }
 
-errno_t vol_part_eject_part(vol_part_t *part)
+static errno_t vol_part_eject_device(service_id_t svcid)
+{
+	async_sess_t *sess;
+	errno_t rc;
+	bd_t *bd;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_eject_device(%zu)",
+	    (size_t)svcid);
+
+	sess = loc_service_connect(svcid, INTERFACE_BLOCK, 0);
+	if (sess == NULL)
+		return EIO;
+
+	rc = bd_open(sess, &bd);
+	if (rc != EOK) {
+		async_hangup(sess);
+		return EIO;
+	}
+
+	rc = bd_eject(bd);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "vol_part_eject_device(): "
+		    "eject fail");
+		bd_close(bd);
+		async_hangup(sess);
+		return EIO;
+	}
+
+	bd_close(bd);
+	async_hangup(sess);
+	return EOK;
+}
+
+errno_t vol_part_eject_part(vol_part_t *part, vol_eject_flags_t flags)
 {
 	int rc;
@@ -667,4 +701,12 @@
 			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting "
 			    "mount directory %s.", part->cur_mp);
+		}
+	}
+
+	if ((flags & vef_physical) != 0) {
+		rc = vol_part_eject_device(part->svc_id);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed physically "
+			    "ejecting device %s.", part->svc_name);
 		}
 	}
@@ -819,5 +861,5 @@
 
 	if (part->cur_mp != NULL) {
-		rc = vol_part_eject_part(part);
+		rc = vol_part_eject_part(part, vef_none);
 		if (rc != EOK)
 			return rc;
Index: uspace/srv/volsrv/part.h
===================================================================
--- uspace/srv/volsrv/part.h	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/volsrv/part.h	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2015 Jiri Svoboda
+ * Copyright (c) 2025 Jiri Svoboda
  * All rights reserved.
  *
@@ -55,5 +55,5 @@
     vol_part_t **);
 extern void vol_part_del_ref(vol_part_t *);
-extern errno_t vol_part_eject_part(vol_part_t *);
+extern errno_t vol_part_eject_part(vol_part_t *, vol_eject_flags_t);
 extern errno_t vol_part_empty_part(vol_part_t *);
 extern errno_t vol_part_insert_part(vol_part_t *);
Index: uspace/srv/volsrv/volsrv.c
===================================================================
--- uspace/srv/volsrv/volsrv.c	(revision 32254d6ae36aa180fa53338ca60684a9c87d7947)
+++ uspace/srv/volsrv/volsrv.c	(revision 4285f384e4a854f4435e5f334456da415d9e1134)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2024 Jiri Svoboda
+ * Copyright (c) 2025 Jiri Svoboda
  * All rights reserved.
  *
@@ -205,9 +205,13 @@
 {
 	service_id_t sid;
+	vol_eject_flags_t flags;
 	vol_part_t *part;
 	errno_t rc;
 
 	sid = ipc_get_arg1(icall);
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_eject_srv(%zu)", sid);
+	flags = ipc_get_arg2(icall);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_eject_srv(%zu, %x)",
+	    sid, flags);
 
 	rc = vol_part_find_by_id_ref(parts, sid, &part);
@@ -217,5 +221,5 @@
 	}
 
-	rc = vol_part_eject_part(part);
+	rc = vol_part_eject_part(part, flags);
 	if (rc != EOK) {
 		async_answer_0(icall, EIO);
