diff --git a/doc/apk-add.8.scd b/doc/apk-add.8.scd
index 1586f7e7c1643059a392e05ca8ecb4978a0193b5..db6214efb3bab525815f8cdc2ee934c0504f655d 100644
--- a/doc/apk-add.8.scd
+++ b/doc/apk-add.8.scd
@@ -42,7 +42,9 @@ following options:
 *-t, --virtual* _NAME_
 	Create virtual package _NAME_ with given dependencies. This new package
 	will get the _packages_ as dependencies instead of _world_. Finally the
-	_NAME_ is added to _world_.
+	_NAME_ is added to _world_. An optional version specifier for the virtual
+	package can be given via syntax _NAME_=_VERSION_. The version defaults
+	to synthesized version based on time.
 
 	One can use this to ensure that selected set of packages are installed,
 	and later the temporary modification be undone with *apk-del*(8) _NAME_
diff --git a/src/app_add.c b/src/app_add.c
index 147250673cd130510bcfe65ccbfcad9abb89cad8..dac85c9a5c3593f503662280ff368c017286ee24 100644
--- a/src/app_add.c
+++ b/src/app_add.c
@@ -77,32 +77,26 @@ static int non_repository_check(struct apk_database *db)
 	return 1;
 }
 
-static struct apk_package *create_virtual_package(struct apk_database *db, struct apk_name *name)
+static struct apk_package *create_virtual_package(struct apk_database *db, struct apk_dependency *dep)
 {
-	char ver[32];
 	struct apk_package *virtpkg;
-	struct tm tm;
 	EVP_MD_CTX *mdctx;
-	time_t now = time(NULL);
 	pid_t pid = getpid();
 
-	gmtime_r(&now, &tm);
-	strftime(ver, sizeof ver, "%Y%m%d.%H%M%S", &tm);
-
 	virtpkg = apk_pkg_new();
 	if (virtpkg == NULL) return 0;
 
-	virtpkg->name = name;
-	virtpkg->version = apk_atomize_dup(&db->atoms, APK_BLOB_STR(ver));
+	virtpkg->name = dep->name;
+	virtpkg->version = dep->version;
 	virtpkg->description = strdup("virtual meta package");
 	virtpkg->arch = apk_atomize(&db->atoms, APK_BLOB_STR("noarch"));
 	virtpkg->repos |= BIT(APK_REPOSITORY_CACHED);
 
 	mdctx = EVP_MD_CTX_new();
 	EVP_DigestInit_ex(mdctx, apk_checksum_default(), NULL);
-	EVP_DigestUpdate(mdctx, &tm, sizeof tm);
 	EVP_DigestUpdate(mdctx, &pid, sizeof pid);
 	EVP_DigestUpdate(mdctx, virtpkg->name->name, strlen(virtpkg->name->name) + 1);
+	EVP_DigestUpdate(mdctx, dep->version->ptr, dep->version->len);
 	virtpkg->csum.type = EVP_MD_CTX_size(mdctx);
 	EVP_DigestFinal_ex(mdctx, virtpkg->csum.data, NULL);
 	EVP_MD_CTX_free(mdctx);
@@ -110,6 +104,17 @@ static struct apk_package *create_virtual_package(struct apk_database *db, struc
 	return virtpkg;
 }
 
+static apk_blob_t *generate_version(struct apk_database *db)
+{
+	char ver[32];
+	struct tm tm;
+	time_t now = time(NULL);
+
+	gmtime_r(&now, &tm);
+	strftime(ver, sizeof ver, "%Y%m%d.%H%M%S", &tm);
+	return apk_atomize_dup(&db->atoms, APK_BLOB_STR(ver));
+}
+
 static int add_main(void *ctx, struct apk_database *db, struct apk_string_array *args)
 {
 	struct add_ctx *actx = (struct add_ctx *) ctx;
@@ -127,23 +132,33 @@ static int add_main(void *ctx, struct apk_database *db, struct apk_string_array
 	if (actx->virtpkg) {
 		apk_blob_t b = APK_BLOB_STR(actx->virtpkg);
 		apk_blob_pull_dep(&b, db, &virtdep);
+
 		if (APK_BLOB_IS_NULL(b) || virtdep.conflict ||
-		    virtdep.result_mask != APK_DEPMASK_ANY ||
-		    virtdep.version != &apk_atom_null) {
+		    (virtdep.name->name[0] != '.' && non_repository_check(db)))
+			goto bad_spec;
+
+		switch (virtdep.result_mask) {
+		case APK_DEPMASK_ANY:
+			if (virtdep.version != &apk_atom_null) goto bad_spec;
+			virtdep.result_mask = APK_VERSION_EQUAL;
+			virtdep.version = generate_version(db);
+			break;
+		case APK_VERSION_EQUAL:
+			if (virtdep.version == &apk_atom_null) goto bad_spec;
+			break;
+		default:
+		bad_spec:
 			apk_error("%s: bad package specifier", actx->virtpkg);
 			return -1;
 		}
-		if (virtdep.name->name[0] != '.' && non_repository_check(db))
-			return -1;
 
-		virtpkg = create_virtual_package(db, virtdep.name);
+		virtpkg = create_virtual_package(db, &virtdep);
 		if (!virtpkg) {
 			apk_error("Failed to allocate virtual meta package");
 			return -1;
 		}
 
-		virtdep.result_mask = APK_VERSION_EQUAL;
-		virtdep.version = virtpkg->version;
+		if (!args->num) apk_warning("creating empty virtual package");
 	}
 
 	foreach_array_item(parg, args) {
diff --git a/test/basic8.test b/test/basic8.test
index 19d3964ca8e43846cfcbabd45178a2983bd24a85..0042371b0d08b1eef7a5832abad1d0f624090dff 100644
--- a/test/basic8.test
+++ b/test/basic8.test
@@ -2,5 +2,6 @@
 --no-network
 add -t .virtual
 @EXPECT
+WARNING: creating empty virtual package
 (1/1) Installing .virtual (20190603.131426)
 OK: 0 MiB in 0 packages