diff --git a/src/rnpkeys/rnpkeys.cpp b/src/rnpkeys/rnpkeys.cpp
index 1a6997c28..471ce00c7 100644
--- a/src/rnpkeys/rnpkeys.cpp
+++ b/src/rnpkeys/rnpkeys.cpp
@@ -80,6 +80,7 @@ const char *usage =
   "  --password             Password, which should be used during operation.\n"
   "  --pass-fd              Read password(s) from the file descriptor.\n"
   "  --force                Force operation (like secret key removal).\n"
+  "  --keyfile              Load key(s) only from the file specified.\n"
   "  --output [file, -]     Write data to the specified file or stdout.\n"
   "  --overwrite            Overwrite output file without a prompt.\n"
   "  --notty                Do not write anything to the TTY.\n"
@@ -141,6 +142,7 @@ struct option options[] = {
   {"set-expire", required_argument, NULL, OPT_SET_EXPIRE},
   {"current-time", required_argument, NULL, OPT_CURTIME},
   {"allow-weak-hash", no_argument, NULL, OPT_ALLOW_WEAK_HASH},
+  {"keyfile", required_argument, NULL, OPT_KEYFILE},
   {NULL, 0, NULL, 0},
 };
 
@@ -614,6 +616,10 @@ setoption(rnp_cfg &cfg, optdefs_t *cmd, int val, const char *arg)
     case OPT_SET_EXPIRE:
         cfg.set_str(CFG_SET_KEY_EXPIRE, arg);
         return true;
+    case OPT_KEYFILE:
+        cfg.set_str(CFG_KEYFILE, arg);
+        cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+        return true;
     default:
         *cmd = CMD_HELP;
         return true;
@@ -642,7 +648,18 @@ rnpkeys_init(cli_rnp_t &rnp, const rnp_cfg &cfg)
                 "want to use it.");
         return false;
     }
-    /* TODO: at some point we should check for error here */
-    (void) rnp.load_keyrings(true);
+
+    bool disable_ks = rnp.cfg().get_bool(CFG_KEYSTORE_DISABLED);
+    if (!disable_ks && !rnp.load_keyrings(true)) {
+        ERR_MSG("fatal: failed to load keys");
+        return false;
+    }
+
+    /* load the keyfile if any */
+    if (disable_ks && !rnp.cfg().get_str(CFG_KEYFILE).empty() && !cli_rnp_add_key(&rnp)) {
+        ERR_MSG("fatal: failed to load key(s) from the file");
+        return false;
+    }
+
     return true;
 }
diff --git a/src/rnpkeys/rnpkeys.h b/src/rnpkeys/rnpkeys.h
index 611fcc10e..de212ade3 100644
--- a/src/rnpkeys/rnpkeys.h
+++ b/src/rnpkeys/rnpkeys.h
@@ -58,6 +58,7 @@ typedef enum {
     OPT_CURTIME,
     OPT_ADD_SUBKEY,
     OPT_SET_EXPIRE,
+    OPT_KEYFILE,
 
     /* debug */
     OPT_DEBUG
diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py
index e6f5ed76b..c037ebcbf 100755
--- a/src/tests/cli_tests.py
+++ b/src/tests/cli_tests.py
@@ -2282,6 +2282,17 @@ def test_rnpkeys_g10_list_order(self):
         else:
             self.assertEqual(file_text(data_path('test_cli_rnpkeys/g10_list_keys_sec_no_bp')), out, 'g10 secret key listing failed')
 
+    def test_rnpkeys_list_from_keyfile(self):
+        KEYRING_2 = data_path('keyrings/2')
+        ret, out, err = run_proc(RNPK, ['--homedir', KEYRING_2, '--list-keys', '--keyfile', data_path(KEY_ALICE_PUB)])
+        self.assertEqual(ret, 0)
+        self.assertRegex(out, r'1 key found.*')
+        self.assertRegex(out, r'(?s)^.*73edcc9119afc8e2dbbdcde50451409669ffde3c.*Alice')
+        self.assertNotRegex(out, r'(?s)^.*c80aa54aa5c6ac73a373687134abe4bd')
+        ret, out, err = run_proc(RNPK, ['--homedir', KEYRING_2, '--list-keys', '--keyfile', 'wrongkeyfile'])
+        self.assertEqual(ret, 1)
+        self.assertRegex(err, r'(?s)^.*fatal: failed to load key\(s\) from the file')
+
     def test_rnpkeys_g10_def_key(self):
         RE_SIG = r'(?s)^.*' \
         r'Good signature made .*' \
@@ -2921,7 +2932,6 @@ def test_empty_keyrings(self):
             self.assertEqual(ret, 1)
             self.assertRegex(err, r'(?s)^.*Error: failed to load keyring from \'.*pubring\.gpg\'')
             self.assertNotRegex(err, r'(?s)^.*Error: failed to load keyring from \'.*secring\.gpg\'')
-            self.assertRegex(out, r'(?s)^.*Key\(s\) not found.')
             # Run with .rnp home directory with empty keyrings
             shutil.rmtree(RNPDIR, ignore_errors=True)
             os.mkdir(RNPDIR, 0o700)