diff --git a/.pylintrc b/.pylintrc
index cd01475054c5d0182557154387bc8134b3e07f31..d0683f0da1ba7e36ee9fe57a5427182ad400b2e2 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,9 +1,9 @@
+[MESSAGES]
+disable = logging-fstring-interpolation
+
 [FORMAT]
 max-line-length=150
 
-[TYPECHECK]
-ignored-classes=SQLAlchemy, sqlalchemy.orm.scoping.scoped_session
-
 [SIMILARITIES]
 min-similarity-lines=8
 ignore-imports=yes
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 17aa5f2147fd8dc37a8a73d58547637175d374d7..cc7a31f5a8883e1fb310a06439bf17884d9e3912 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 all: lint
 
 install:
-	apt-get -y install awscli make python3-cryptography rclone restic yamllint
+	apt-get -y install awscli make python3-cryptography python3-tabulate rclone restic yamllint
 
 venv:
 	apt-get -y install python3-venv
diff --git a/requirements.lock b/requirements.lock
index 54ee3a7939fd01ccb076f6f6337418899c407a4a..aa7dd3d80f9d1bc94462a13ca9d170ceb1599fe2 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -72,6 +72,7 @@ s3transfer==0.10.1
 sarif-om==1.0.4
 six==1.16.0
 sympy==1.12
+tabulate==0.9.0
 tomlkit==0.12.4
 typing_extensions==4.10.0
 urllib3==2.2.1
diff --git a/requirements.txt b/requirements.txt
index 98717c27767f70b11754c3cac6ee13643b7b884a..0f055546e5a8217e4b52cc6e657e8ef4712347b3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
 # runtime
 cryptography
+tabulate
 
 # dev
 flake8
diff --git a/rwm.py b/rwm.py
index 911509a610555a69e150aed2037bd26ed5d52f92..a7cd88a26c41311dc02e9af1bdf7a31f22e2f699 100755
--- a/rwm.py
+++ b/rwm.py
@@ -2,17 +2,20 @@
 """rwm, restic/s3 worm manager"""
 
 import base64
+import dataclasses
 import logging
 import os
 import shlex
 import subprocess
 import sys
 from argparse import ArgumentParser
+from datetime import datetime
 from pathlib import Path
 
 import yaml
 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 from cryptography.hazmat.backends import default_backend
+from tabulate import tabulate
 
 
 __version__ = "0.2"
@@ -74,6 +77,28 @@ def rclone_obscure_password(plaintext, iv=None):
     return base64.urlsafe_b64encode(data).decode().rstrip("=")
 
 
+@dataclasses.dataclass
+class BackupResult:
+    """backup results data container"""
+
+    name: str
+    returncode: int
+    time_start: datetime
+    time_end: datetime
+
+    def to_dict(self):
+        """dict serializer"""
+
+        return {
+            "ident": "RESULT",
+            "name": self.name,
+            "status": "OK" if self.returncode == 0 else "ERROR",
+            "returncode": self.returncode,
+            "backup_start": self.time_start.isoformat(),
+            "backup_time": str(self.time_end-self.time_start),
+        }
+
+
 class RWM:
     """rwm impl"""
 
@@ -148,46 +173,92 @@ class RWM:
         }
         return run_command(["restic"] + args, env=env)
 
-    def backup(self, name):
-        """do restic backup from config"""
+    def restic_autoinit(self):
+        """runs restic init"""
 
-        if self.restic_cmd(["cat", "config"]).returncode != 0:
-            if (proc := self.restic_cmd(["init"])).returncode != 0:
-                logger.error("failed to autoinitialize restic repository")
-                return proc
+        logger.info("run restic_autoinit")
+        if (proc := self.restic_cmd(["cat", "config"])).returncode != 0:
+            proc = self.restic_cmd(["init"])
+        return proc
 
+    def restic_backup(self, name):
+        """runs restic backup by name"""
+
+        logger.info(f"run restic_backup {name}")
         conf = self.config["RWM_BACKUPS"][name]
+        excludes = []
+        for item in conf.get("excludes", []):
+            excludes += ["--exclude", item]
+        backup_extras = conf.get("backup_extras", [])
+        cmd_args = ["backup"] + backup_extras + excludes + conf["filesdirs"]
 
-        # restic backup
-        excludes = conf.get("excludes", [])
-        excludes = [x for pair in zip(["--exclude"] * len(excludes), excludes) for x in pair]
-        extras = conf.get("extras", [])
-        cmd_args = ["backup"] + extras + excludes + conf["filesdirs"]
+        return self.restic_cmd(cmd_args)
 
-        logger.info("running backup")
-        backup_proc = self.restic_cmd(cmd_args)
-        wrap_output(backup_proc)
-        if backup_proc.returncode != 0:
-            logger.error("backup failed, %s", backup_proc)
-            return backup_proc
+    def restic_forget_prune(self):
+        """runs forget prune"""
 
-        # restic forget prune
+        logger.info("run restic_forget_prune")
         keeps = []
         for key, val in self.config.get("RWM_RETENTION", {}).items():
             keeps += [f"--{key}", val]
-        if not keeps:
-            logger.error("no retention policy found")
         cmd_args = ["forget", "--prune"] + keeps
 
-        logger.info("running forget prune")
-        forget_proc = self.restic_cmd(cmd_args)
-        wrap_output(forget_proc)
+        return self.restic_cmd(cmd_args)
+
+    def backup_cmd(self, name):
+        """backup command"""
+
+        autoinit_proc = self.restic_autoinit()
+        if autoinit_proc.returncode != 0:
+            logger.error("restic autoinit failed")
+            wrap_output(autoinit_proc)
+            return autoinit_proc
+
+        wrap_output(backup_proc := self.restic_backup(name))
+        if backup_proc.returncode != 0:
+            logger.error("restic_backup failed")
+            return backup_proc
+
+        wrap_output(forget_proc := self.restic_forget_prune())
         if forget_proc.returncode != 0:
-            logger.error("forget prune failed, %s", forget_proc)
+            logger.error("restic_forget_prune failed")
             return forget_proc
 
         return backup_proc
 
+    def backup_all_cmd(self):
+        """backup all command"""
+
+        stats = {}
+        ret = 0
+
+        time_start = datetime.now()
+        autoinit_proc = self.restic_autoinit()
+        time_end = datetime.now()
+        if autoinit_proc.returncode != 0:
+            logger.error("restic autoinit failed")
+            wrap_output(autoinit_proc)
+            return autoinit_proc.returncode
+        stats["_autoinit"] = BackupResult("_autoinit", autoinit_proc.returncode, time_start, time_end)
+
+        for name in self.config["RWM_BACKUPS"].keys():
+            time_start = datetime.now()
+            wrap_output(backup_proc := self.restic_backup(name))
+            time_end = datetime.now()
+            ret |= backup_proc.returncode
+            stats[name] = BackupResult(name, backup_proc.returncode, time_start, time_end)
+
+        if ret == 0:
+            time_start = datetime.now()
+            wrap_output(forget_proc := self.restic_forget_prune())
+            time_end = datetime.now()
+            ret |= forget_proc.returncode
+            stats["_forget_prune"] = BackupResult("_forget_prune", forget_proc.returncode, time_start, time_end)
+
+        logger.info("rwm backup_all results")
+        print(tabulate([item.to_dict() for item in stats.values()], headers="keys", numalign="left"))
+        return ret
+
 
 def configure_logging(debug):
     """configure logger"""
@@ -212,6 +283,7 @@ def parse_arguments(argv):
 
     subparsers = parser.add_subparsers(title="commands", dest="command", required=False)
     subparsers.add_parser("version", help="show version")
+
     aws_cmd_parser = subparsers.add_parser("aws", help="aws command")
     aws_cmd_parser.add_argument("cmd_args", nargs="*")
     rc_cmd_parser = subparsers.add_parser("rclone", help="rclone command")
@@ -220,8 +292,10 @@ def parse_arguments(argv):
     rcc_cmd_parser.add_argument("cmd_args", nargs="*")
     res_cmd_parser = subparsers.add_parser("restic", help="restic command")
     res_cmd_parser.add_argument("cmd_args", nargs="*")
+
     backup_cmd_parser = subparsers.add_parser("backup", help="backup command")
     backup_cmd_parser.add_argument("name", help="backup config name")
+    subparsers.add_parser("backup_all", help="backup all command")
 
     return parser.parse_args(argv)
 
@@ -252,8 +326,11 @@ def main(argv=None):
         ret = wrap_output(rwmi.rclone_crypt_cmd(args.cmd_args))
     if args.command == "restic":
         ret = wrap_output(rwmi.restic_cmd(args.cmd_args))
+
     if args.command == "backup":
-        ret = rwmi.backup(args.name).returncode
+        ret = rwmi.backup_cmd(args.name).returncode
+    if args.command == "backup_all":
+        ret = rwmi.backup_all_cmd()
 
     logger.info("rwm finished with %s (ret %d)", "success" if ret == 0 else "errors", ret)
     return ret
diff --git a/tests/test_default.py b/tests/test_default.py
index da52e2b378669bd8b1693f39ed51a099f208872d..495b399c6b27981c9624b3d3d7b780257a6d33cc 100644
--- a/tests/test_default.py
+++ b/tests/test_default.py
@@ -46,12 +46,13 @@ def test_main(tmpworkdir: str):  # pylint: disable=unused-argument
 
     # command branches
     mock = Mock(return_value=CompletedProcess(args='dummy', returncode=0))
-    for item in ["aws", "rclone", "rclone_crypt", "restic"]:
+    for item in ["aws", "rclone", "rclone_crypt", "restic", "backup"]:
         with patch.object(rwm.RWM, f"{item}_cmd", mock):
             assert rwm_main([item, "dummy"]) == 0
 
-    with patch.object(rwm.RWM, "backup", mock):
-        assert rwm_main(["backup", "dummy"]) == 0
+    mock = Mock(return_value=0)
+    with patch.object(rwm.RWM, "backup_all_cmd", mock):
+        assert rwm_main(["backup_all"]) == 0
 
 
 def test_aws_cmd(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
@@ -162,8 +163,8 @@ def _list_files(trwm, snapshot_id):
     ]
 
 
-def test_backup(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
-    """test backup command"""
+def test_backup_cmd(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
+    """test backup_cmd command"""
 
     trwm = RWM({
         "RWM_S3_ENDPOINT_URL": motoserver,
@@ -187,7 +188,7 @@ def test_backup(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-arg
     Path("testdatadir/testdata1.txt").write_text("dummydata", encoding="utf-8")
     Path("testdatadir/testfile_to_be_ignored").write_text("dummydata", encoding="utf-8")
 
-    assert trwm.backup("testcfg").returncode == 0
+    assert trwm.backup_cmd("testcfg").returncode == 0
 
     snapshots = _list_snapshots(trwm)
     assert len(snapshots) == 1
@@ -195,15 +196,7 @@ def test_backup(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-arg
     assert "/testdatadir/testdata1.txt" in snapshot_files
 
 
-def test_backup_autoinit():  # pylint: disable=unused-argument
-    """mocked error handling test"""
-
-    mock = Mock(return_value=CompletedProcess(args='dummy', returncode=1))
-    with patch.object(rwm.RWM, "restic_cmd", mock):
-        RWM({}).backup("dummy")
-
-
-def test_backup_excludes(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
+def test_backup_cmd_excludes(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
     """test backup command"""
 
     trwm = RWM({
@@ -229,7 +222,7 @@ def test_backup_excludes(tmpworkdir: str, motoserver: str):  # pylint: disable=u
     Path("testdatadir/proc").mkdir()
     Path("testdatadir/proc/to_be_also_excluded").write_text("dummydata", encoding="utf-8")
 
-    assert trwm.backup("testcfg").returncode == 0
+    assert trwm.backup_cmd("testcfg").returncode == 0
 
     snapshots = _list_snapshots(trwm)
     assert len(snapshots) == 1
@@ -238,32 +231,61 @@ def test_backup_excludes(tmpworkdir: str, motoserver: str):  # pylint: disable=u
     assert "/testdatadir/etc/config2" in snapshot_files
 
 
-def test_backup_error_handling(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
+def test_backup_cmd_error_handling(tmpworkdir: str, motoserver: str):  # pylint: disable=unused-argument
     """test backup command err cases"""
 
-    mock = Mock(side_effect=[
-        CompletedProcess(args='dummy', returncode=0),  # autoinit
-        CompletedProcess(args='dummy', returncode=11)  # backup
-    ])
-    with patch.object(rwm.RWM, "restic_cmd", mock):
-        trwm = RWM({
-            "RWM_BACKUPS": {
-                "dummycfg": {"filesdirs": ["dummydir"]}
-            }
-        })
-        proc = trwm.backup("dummycfg")
-        assert proc.returncode == 11
-
-    mock = Mock(side_effect=[
-        CompletedProcess(args='dummy', returncode=0),  # autoinit
-        CompletedProcess(args='dummy', returncode=0),  # backup
-        CompletedProcess(args='dummy', returncode=12)  # forget
-    ])
-    with patch.object(rwm.RWM, "restic_cmd", mock):
-        trwm = RWM({
-            "RWM_BACKUPS": {
-                "dummycfg": {"filesdirs": ["dummydir"]}
-            }
-        })
-        proc = trwm.backup("dummycfg")
-        assert proc.returncode == 12
+    rwm_conf = {
+        "RWM_BACKUPS": {
+            "dummycfg": {"filesdirs": ["dummydir"]}
+        }
+    }
+    mock_ok = Mock(return_value=CompletedProcess(args='dummy', returncode=0))
+    mock_fail = Mock(return_value=CompletedProcess(args='dummy', returncode=11))
+
+    with patch.object(rwm.RWM, "restic_autoinit", mock_fail):
+        assert RWM(rwm_conf).backup_cmd("dummycfg").returncode == 11
+
+    with (
+        patch.object(rwm.RWM, "restic_autoinit", mock_ok),
+        patch.object(rwm.RWM, "restic_backup", mock_fail)
+    ):
+        assert RWM(rwm_conf).backup_cmd("dummycfg").returncode == 11
+
+    with (
+        patch.object(rwm.RWM, "restic_autoinit", mock_ok),
+        patch.object(rwm.RWM, "restic_backup", mock_ok),
+        patch.object(rwm.RWM, "restic_forget_prune", mock_fail)
+    ):
+        assert RWM(rwm_conf).backup_cmd("dummycfg").returncode == 11
+
+
+def test_backup_all_cmd(tmpworkdir: str):  # pylint: disable=unused-argument
+    """test backup command err cases"""
+
+    rwm_conf = {
+        "RWM_BACKUPS": {
+            "dummycfg": {"filesdirs": ["dummydir"]}
+        }
+    }
+    mock = Mock(return_value=CompletedProcess(args='dummy', returncode=0))
+
+    with (
+        patch.object(rwm.RWM, "restic_autoinit", mock),
+        patch.object(rwm.RWM, "restic_backup", mock),
+        patch.object(rwm.RWM, "restic_forget_prune", mock)
+    ):
+        assert RWM(rwm_conf).backup_all_cmd() == 0
+
+
+def test_backup_all_cmd_error_handling(tmpworkdir: str):  # pylint: disable=unused-argument
+    """test backup command err cases"""
+
+    rwm_conf = {
+        "RWM_BACKUPS": {
+            "dummycfg": {"filesdirs": ["dummydir"]}
+        }
+    }
+    mock_fail = Mock(return_value=CompletedProcess(args='dummy', returncode=11))
+
+    with patch.object(rwm.RWM, "restic_autoinit", mock_fail):
+        assert RWM(rwm_conf).backup_all_cmd() == 11