diff --git a/src/restic_compose_backup/cli.py b/src/restic_compose_backup/cli.py
index bd8a763..4db35b5 100644
--- a/src/restic_compose_backup/cli.py
+++ b/src/restic_compose_backup/cli.py
@@ -1,20 +1,8 @@
 import argparse
-import os
 import logging
 from typing import List
 
-from restic_compose_backup import (
-    alerts,
-    backup_runner,
-    log,
-    restic,
-)
-from restic_compose_backup.config import Config
-from restic_compose_backup.containers import RunningContainers
-from restic_compose_backup import utils
-from restic_compose_backup import commands
-
-logger = logging.getLogger(__name__)
+from restic_compose_backup import commands, log
 
 
 def main():
@@ -22,187 +10,6 @@ def main():
     args = parse_args(sorted(commands.COMMANDS.keys()))
     command = commands.COMMANDS[args.action](args)
     command.run()
-    return
-
-    # Ensure log level is propagated to parent container if overridden
-    # if args.log_level:
-    #     containers.this_container.set_config_env('LOG_LEVEL', args.log_level)
-
-    if args.action == 'status':
-        status(config, containers)
-
-    elif args.action == 'snapshots':
-        snapshots(config, containers)
-
-    elif args.action == 'backup':
-        backup(config, containers)
-
-    elif args.action == 'start-backup-process':
-        start_backup_process(config, containers)
-
-    elif args.action == 'cleanup':
-        cleanup(config, containers)
-
-    elif args.action == 'alert':
-        alert(config, containers)
-
-
-def backup(config, containers):
-    """Request a backup to start"""
-    # Make sure we don't spawn multiple backup processes
-    if containers.backup_process_running:
-        alerts.send(
-            subject="Backup process container already running",
-            body=(
-                "A backup process container is already running. \n"
-                f"Id: {containers.backup_process_container.id}\n"
-                f"Name: {containers.backup_process_container.name}\n"
-            ),
-            alert_type='ERROR',
-        )
-        raise RuntimeError("Backup process already running")
-
-    # Map all volumes from the backup container into the backup process container
-    volumes = containers.this_container.volumes
-
-    # Map volumes from other containers we are backing up
-    mounts = containers.generate_backup_mounts('/volumes')
-    volumes.update(mounts)
-
-    logger.debug('Starting backup container with image %s', containers.this_container.image)
-    try:
-        result = backup_runner.run(
-            image=containers.this_container.image,
-            command='restic-compose-backup start-backup-process',
-            volumes=volumes,
-            environment=containers.this_container.environment,
-            source_container_id=containers.this_container.id,
-            labels={
-                containers.backup_process_label: 'True',
-                "com.docker.compose.project": containers.project_name,
-            },
-        )
-    except Exception as ex:
-        logger.exception(ex)
-        alerts.send(
-            subject="Exception during backup",
-            body=str(ex),
-            alert_type='ERROR',
-        )
-        return
-
-    logger.info('Backup container exit code: %s', result)
-
-    # Alert the user if something went wrong
-    if result != 0:
-        alerts.send(
-            subject="Backup process exited with non-zero code",
-            body=open('backup.log').read(),
-            alert_type='ERROR',
-        )
-
-
-def start_backup_process(config, containers):
-    """The actual backup process running inside the spawned container"""
-    if not utils.is_true(os.environ.get('BACKUP_PROCESS_CONTAINER')):
-        logger.error(
-            "Cannot run backup process in this container. Use backup command instead. "
-            "This will spawn a new container with the necessary mounts."
-        )
-        alerts.send(
-            subject="Cannot run backup process in this container",
-            body=(
-                "Cannot run backup process in this container. Use backup command instead. "
-                "This will spawn a new container with the necessary mounts."
-            )
-        )
-        exit(1)
-
-    status(config, containers)
-    errors = False
-
-    # Did we actually get any volumes mounted?
-    try:
-        has_volumes = os.stat('/volumes') is not None
-    except FileNotFoundError:
-        logger.warning("Found no volumes to back up")
-        has_volumes = False
-
-    # Warn if there is nothing to do
-    if len(containers.containers_for_backup()) == 0 and not has_volumes:
-        logger.error("No containers for backup found")
-        exit(1)
-
-    if has_volumes:
-        try:
-            logger.info('Backing up volumes')
-            vol_result = restic.backup_files(config.repository, source='/volumes')
-            logger.debug('Volume backup exit code: %s', vol_result)
-            if vol_result != 0:
-                logger.error('Volume backup exited with non-zero code: %s', vol_result)
-                errors = True
-        except Exception as ex:
-            logger.error('Exception raised during volume backup')
-            logger.exception(ex)
-            errors = True
-
-    # back up databases
-    logger.info('Backing up databases')
-    for container in containers.containers_for_backup():
-        if container.database_backup_enabled:
-            try:
-                instance = container.instance
-                logger.info('Backing up %s in service %s', instance.container_type, instance.service_name)
-                result = instance.backup()
-                logger.debug('Exit code: %s', result)
-                if result != 0:
-                    logger.error('Backup command exited with non-zero code: %s', result)
-                    errors = True
-            except Exception as ex:
-                logger.exception(ex)
-                errors = True
-
-    if errors:
-        logger.error('Exit code: %s', errors)
-        exit(1)
-
-    # Only run cleanup if backup was successful
-    result = cleanup(config, container)
-    logger.debug('cleanup exit code: %s', result)
-    if result != 0:
-        logger.error('cleanup exit code: %s', result)
-        exit(1)
-
-    # Test the repository for errors
-    logger.info("Checking the repository for errors")
-    result = restic.check(config.repository)
-    if result != 0:
-        logger.error('Check exit code: %s', result)
-        exit(1)
-
-    logger.info('Backup completed')
-
-
-def cleanup(config, containers):
-    """Run forget / prune to minimize storage space"""
-    logger.info('Forget outdated snapshots')
-    forget_result = restic.forget(
-        config.repository,
-        config.keep_daily,
-        config.keep_weekly,
-        config.keep_monthly,
-        config.keep_yearly,
-    )
-    logger.info('Prune stale data freeing storage space')
-    prune_result = restic.prune(config.repository)
-    return forget_result and prune_result
-
-
-def snapshots(config, containers):
-    """Display restic snapshots"""
-    stdout, stderr = restic.snapshots(config.repository, last=True)
-    for line in stdout.decode().split('\n'):
-        print(line)
 
 
 def parse_args(choices: List[str]):
diff --git a/src/restic_compose_backup/commands/backup.py b/src/restic_compose_backup/commands/backup.py
index 4fcde82..9d24aa6 100644
--- a/src/restic_compose_backup/commands/backup.py
+++ b/src/restic_compose_backup/commands/backup.py
@@ -1,4 +1,5 @@
 from .base import BaseCommand
+from restic_compose_backup import backup_runner, alerts
 
 
 class Command(BaseCommand):
@@ -6,4 +7,56 @@ class Command(BaseCommand):
     name = "backup"
 
     def run(self):
-        print("Backup!")
+        """Run the backup command"""
+        containers = self.get_containers()
+
+        if containers.backup_process_running:
+            alerts.send(
+                subject="Backup process container already running",
+                body=(
+                    "A backup process container is already running. \n"
+                    f"Id: {containers.backup_process_container.id}\n"
+                    f"Name: {containers.backup_process_container.name}\n"
+                ),
+                alert_type='ERROR',
+            )
+            raise RuntimeError("Backup process already running")
+
+        # Map all volumes from the backup container into the backup process container
+        volumes = containers.this_container.volumes
+
+        # Map volumes from other containers we are backing up
+        mounts = containers.generate_backup_mounts('/volumes')
+        volumes.update(mounts)
+
+        self.logger.debug('Starting backup container with image %s', containers.this_container.image)
+        try:
+            result = backup_runner.run(
+                image=containers.this_container.image,
+                command='restic-compose-backup start_backup_process',
+                volumes=volumes,
+                environment=containers.this_container.environment,
+                source_container_id=containers.this_container.id,
+                labels={
+                    containers.backup_process_label: 'True',
+                    "com.docker.compose.project": containers.project_name,
+                },
+            )
+        except Exception as ex:
+            self.logger.exception(ex)
+            alerts.send(
+                subject="Exception during backup",
+                body=str(ex),
+                alert_type='ERROR',
+            )
+            return
+
+        self.logger.info('Backup container exit code: %s', result)
+
+        # Alert the user if something went wrong
+        if result != 0:
+            alerts.send(
+                subject="Backup process exited with non-zero code",
+                body=open('backup.log').read(),
+                alert_type='ERROR',
+            )
diff --git a/src/restic_compose_backup/commands/base.py b/src/restic_compose_backup/commands/base.py
index 22422cf..31ff572 100644
--- a/src/restic_compose_backup/commands/base.py
+++ b/src/restic_compose_backup/commands/base.py
@@ -17,7 +17,9 @@ class BaseCommand:
 
     def get_containers(self):
         """Get running containers"""
-        return RunningContainers()
+        containers = RunningContainers()
+        containers.this_container.set_config_env('LOG_LEVEL', self.log_level)
+        return containers
 
     def run(self):
         """Run the command"""
diff --git a/src/restic_compose_backup/commands/cleanup.py b/src/restic_compose_backup/commands/cleanup.py
index e5416d0..e0fb505 100644
--- a/src/restic_compose_backup/commands/cleanup.py
+++ b/src/restic_compose_backup/commands/cleanup.py
@@ -1,4 +1,5 @@
 from .base import BaseCommand
+from restic_compose_backup import restic
 
 
 class Command(BaseCommand):
@@ -6,4 +7,15 @@ class Command(BaseCommand):
     name = "cleanup"
 
     def run(self):
-        print("Cleanup!")
+        """Run forget / prune to minimize storage space"""
+        self.logger.info('Forget outdated snapshots')
+        forget_result = restic.forget(
+            self.config.repository,
+            self.config.keep_daily,
+            self.config.keep_weekly,
+            self.config.keep_monthly,
+            self.config.keep_yearly,
+        )
+        self.logger.info('Prune stale data freeing storage space')
+        prune_result = restic.prune(self.config.repository)
+        return forget_result and prune_result
diff --git a/src/restic_compose_backup/commands/snapshots.py b/src/restic_compose_backup/commands/snapshots.py
index f53adda..008edf9 100644
--- a/src/restic_compose_backup/commands/snapshots.py
+++ b/src/restic_compose_backup/commands/snapshots.py
@@ -1,4 +1,5 @@
 from .base import BaseCommand
+from restic_compose_backup import restic
 
 
 class Command(BaseCommand):
@@ -6,4 +7,7 @@ class Command(BaseCommand):
     name = "snapshots"
 
     def run(self):
-        print("Snapshots!")
+        """Display restic snapshots"""
+        stdout, stderr = restic.snapshots(self.config.repository, last=True)
+        for line in stdout.decode().split('\n'):
+            print(line)
diff --git a/src/restic_compose_backup/commands/start_backup_process.py b/src/restic_compose_backup/commands/start_backup_process.py
new file mode 100644
index 0000000..76bca77
--- /dev/null
+++ b/src/restic_compose_backup/commands/start_backup_process.py
@@ -0,0 +1,89 @@
+import os
+from . import BaseCommand
+from restic_compose_backup import restic, alerts, utils
+
+class Command(BaseCommand):
+    name = "start_backup_process"
+
+    def run(self):
+        """The actual backup process running inside the spawned container"""
+        if not utils.is_true(os.environ.get('BACKUP_PROCESS_CONTAINER')):
+            self.logger.error(
+                "Cannot run backup process in this container. Use backup command instead. "
+                "This will spawn a new container with the necessary mounts."
+            )
+            alerts.send(
+                subject="Cannot run backup process in this container",
+                body=(
+                    "Cannot run backup process in this container. Use backup command instead. "
+                    "This will spawn a new container with the necessary mounts."
+                )
+            )
+            exit(1)
+
+        self.run_command("status")  # status(config, containers)
+        errors = False
+        containers = self.get_containers()
+
+        # Did we actually get any volumes mounted?
+        try:
+            has_volumes = os.stat('/volumes') is not None
+        except FileNotFoundError:
+            self.logger.warning("Found no volumes to back up")
+            has_volumes = False
+
+        # Warn if there is nothing to do
+        if len(containers.containers_for_backup()) == 0 and not has_volumes:
+            self.logger.error("No containers for backup found")
+            exit(1)
+
+        if has_volumes:
+            try:
+                self.logger.info('Backing up volumes')
+                vol_result = restic.backup_files(self.config.repository, source='/volumes')
+                self.logger.debug('Volume backup exit code: %s', vol_result)
+                if vol_result != 0:
+                    self.logger.error('Volume backup exited with non-zero code: %s', vol_result)
+                    errors = True
+            except Exception as ex:
+                self.logger.error('Exception raised during volume backup')
+                self.logger.exception(ex)
+                errors = True
+
+        # back up databases
+        self.logger.info('Backing up databases')
+        for container in containers.containers_for_backup():
+            if container.database_backup_enabled:
+                try:
+                    instance = container.instance
+                    self.logger.info('Backing up %s in service %s', instance.container_type, instance.service_name)
+                    result = instance.backup()
+                    self.logger.debug('Exit code: %s', result)
+                    if result != 0:
+                        self.logger.error('Backup command exited with non-zero code: %s', result)
+                        errors = True
+                except Exception as ex:
+                    self.logger.exception(ex)
+                    errors = True
+
+        if errors:
+            self.logger.error('Exit code: %s', errors)
+            exit(1)
+
+        # Only run cleanup if backup was successful
+        #result = cleanup(config, container)
+        self.run_command("cleanup")
+
+        self.logger.debug('cleanup exit code: %s', result)
+        if result != 0:
+            self.logger.error('cleanup exit code: %s', result)
+            exit(1)
+
+        # Test the repository for errors
+        self.logger.info("Checking the repository for errors")
+        result = restic.check(self.config.repository)
+        if result != 0:
+            self.logger.error('Check exit code: %s', result)
+            exit(1)
+
+        self.logger.info('Backup completed')