diff --git a/source/src/commands/backupdata.cpp b/source/src/commands/backupdata.cpp
new file mode 100644
index 0000000..dadf175
--- /dev/null
+++ b/source/src/commands/backupdata.cpp
@@ -0,0 +1,172 @@
+#include <unistd.h>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <filesystem>
+
+#include "utils/output.hpp"
+#include "utils/assert.hpp"
+#include "utils/utils.hpp"
+#include "command_registry.hpp"
+#include "config.hpp"
+#include "services.hpp"
+#include "servers.hpp"
+#include "server_env_manager.hpp"
+#include "templates.hpp"
+#include "utils/directories.hpp"
+#include "shared_commands.hpp"
+
+
+namespace dropshell {
+
+int backupdata_handler(const CommandContext& ctx);
+
+static std::vector<std::string> backupdata_name_list={"backupdata","bd","backup","bup"};
+
+// Static registration
+struct BackupDataCommandRegister {
+    BackupDataCommandRegister() {
+        CommandRegistry::instance().register_command({
+            backupdata_name_list,
+            backupdata_handler,
+            shared_commands::std_autocomplete_allowall,
+            false, // hidden
+            true, // requires_config
+            true, // requires_install
+            0,     // min_args (after command)
+            1,     // max_args (after command)
+            "backupdata SERVER SERVICE",
+            "Backup data for a service on a server.",
+            // heredoc
+            R"(
+                backupdata SERVER SERVICE     Backup data for a service on a server.
+                backupdata SERVER all         Backup data for all services on a server.
+
+                Note: This command will not create any data or configuration.
+                It will simply backup the data on the remote server, saving it to a local file.
+                Restore the data with restore.
+            )"
+        });
+    }
+} backupdata_command_register;
+
+
+bool backupdata_service(const std::string& server, const std::string& service)
+{
+    server_env_manager server_env(server);
+    if (!server_env.is_valid())
+    {
+        error << "Server " << server << " is not valid" << std::endl;
+        return false;
+    }
+
+    LocalServiceInfo sinfo = get_service_info(server, service);
+    if (!SIvalid(sinfo))
+    {
+        error << "Service " << service << " is not valid" << std::endl;
+        return false;
+    }
+
+    const std::string command = "backup";
+
+    if (!gTemplateManager().template_command_exists(sinfo.template_name, command)) {
+        info << service << " has no data to backup" << std::endl;
+        debug << "(no backup script for " << sinfo.template_name << ")" << std::endl;
+        return true; // nothing to back up.
+    }
+    
+    // Check if basic installed stuff is in place.
+    std::string remote_service_template_path = remotepath::service_template(server, service);
+    std::string remote_command_script_file = remote_service_template_path + "/" + command + ".sh";
+    std::string remote_service_config_path = remotepath::service_config(server, service);
+    if (!server_env.check_remote_items_exist({
+        remotepath::service(server, service),
+        remote_command_script_file,
+        remotefile::service_env(server, service)})
+    )
+    {
+        error << "Error: Required service directories not found on remote server" << std::endl;
+        info << "Is the service installed?" << std::endl;
+        return false;
+    }
+
+    // Create backups directory on server if it doesn't exist
+    std::string remote_backups_dir = remotepath::backups(server);
+    debug << "Remote backups directory on "<< server <<": " << remote_backups_dir << std::endl;
+    std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
+    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("",mkdir_cmd, {}), cMode::Defaults)) {
+        error << "Failed to create backups directory on server" << std::endl;
+        return false;
+    }
+
+    // Create backups directory locally if it doesn't exist
+    std::string local_backups_dir = gConfig().get_local_backup_path();
+    if (local_backups_dir.empty()) {
+        error << "Error: Local backups directory not found" << std::endl;
+        info << "Run 'dropshell edit' to configure DropShell" << std::endl;
+        return false;
+    }
+    if (!std::filesystem::exists(local_backups_dir)) 
+        std::filesystem::create_directories(local_backups_dir);
+
+    // Get current datetime for backup filename
+    shared_commands::cBackupFileName backup_filename_construction(server, service, sinfo.template_name);
+    if (!backup_filename_construction.is_valid()) {
+        error << "Invalid backup filename" << std::endl;
+        return false;
+    }
+
+    // Construct backup filename
+    std::string backup_filename = backup_filename_construction.get_filename();
+    std::string remote_backup_file_path = remote_backups_dir + "/" + backup_filename;
+    std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_filename).string();
+
+    // assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
+    ASSERT(3 == count_substring(magic_string(), local_backup_file_path), "Invalid backup filename");
+
+    {   // Run backup script
+        shared_commands::cRemoteTempFolder remote_temp_folder(server_env);
+        if (!server_env.run_remote_template_command(service, command, {}, false, {{"BACKUP_FILE", remote_backup_file_path}, {"TEMP_DIR", remote_temp_folder.path()}})) {
+            error << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
+            return false;
+        }
+
+        // Copy backup file from server to local
+        if (!shared_commands::scp_file_from_remote(server_env, remote_backup_file_path, local_backup_file_path, false)) {
+            error << "Failed to copy backup file from server" << std::endl;
+            return false;
+        }
+    } // dtor of remote_temp_folder will clean up the temp folder on the server
+
+    info << "Backup created successfully. Restore with:"<<std::endl;
+    info << "   dropshell restore " << server << " " << service << " " << backup_filename << std::endl;
+    return true;
+}
+
+
+int backupdata_handler(const CommandContext& ctx)
+{
+    if (ctx.args.size() < 1)
+    {
+        error << "Server name is required" << std::endl;
+        return 1;
+    }
+
+    std::string server = safearg(ctx.args, 0);
+
+    if (ctx.args.size() < 2)
+    {
+        // backup all services on the server
+        maketitle("Backing up data for all services on " + server);
+        bool okay = true;
+        std::vector<LocalServiceInfo> services = get_server_services_info(server);
+        for (const auto &service : services)
+            okay &= backupdata_service(server, service.service_name);
+        return okay ? 0 : 1;
+    }
+
+    std::string service = safearg(ctx.args, 1);
+    return backupdata_service(server, service);
+}
+
+} // namespace dropshell
\ No newline at end of file
diff --git a/source/src/commands/shared_commands.cpp b/source/src/commands/shared_commands.cpp
index 9694b23..6863b9d 100644
--- a/source/src/commands/shared_commands.cpp
+++ b/source/src/commands/shared_commands.cpp
@@ -13,239 +13,318 @@ namespace dropshell
     namespace shared_commands
     {
 
-    // ------------------------------------------------------------------------------------------------
-    // std_autocomplete : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    void std_autocomplete(const CommandContext &ctx)
-    {
-        if (ctx.args.size() == 0)
-        { // just the command, no args yet.
-            // list servers
-            std::vector<ServerInfo> servers = get_configured_servers();
-            for (const auto &server : servers)
-            {
-                rawout << server.name << std::endl;
-            }
-        }
-        else if (ctx.args.size() == 1)
+        // ------------------------------------------------------------------------------------------------
+        // std_autocomplete : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        void std_autocomplete(const CommandContext &ctx)
         {
-            // list services
-            std::vector<LocalServiceInfo> services = get_server_services_info(ctx.args[0]);
-            for (const auto &service : services)
-            {
-                rawout << service.service_name << std::endl;
-            }
-        }
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // std_autocomplete_allowall : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    void std_autocomplete_allowall(const CommandContext &ctx)
-    {
-        std_autocomplete(ctx);
-        if (ctx.args.size() == 1)
-            rawout << "all" << std::endl;
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // rsync_tree_to_remote : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    bool rsync_tree_to_remote(
-        const std::string &local_path,
-        const std::string &remote_path,
-        server_env_manager &server_env,
-        bool silent)
-    {
-        ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync.");
-
-        std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " +
-                                quote(local_path + "/") + " " +
-                                quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" +
-                                      remote_path + "/");
-        return execute_local_command("", rsync_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // get_arch : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    std::string get_arch()
-    {
-        // determine the architecture of the system
-        std::string arch;
-#ifdef __aarch64__
-        arch = "arm64";
-#elif __x86_64__
-        arch = "amd64";
-#endif
-        return arch;
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // cRemoteTempFolder : SHARED CLASS
-    // ------------------------------------------------------------------------------------------------
-    cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
-    {
-        std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
-        std::string mkdir_cmd = "mkdir -p " + quote(p);
-        if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
-            error << "Failed to create temp directory on server" << std::endl;
-        else
-            mPath = p;
-    }
-
-    cRemoteTempFolder::~cRemoteTempFolder()
-    {
-        std::string rm_cmd = "rm -rf " + quote(mPath);
-        execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("", rm_cmd, {}), cMode::Silent);
-    }
-
-    std::string cRemoteTempFolder::path() const
-    {
-        return mPath;
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // get_all_services_status : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name)
-    {
-        std::map<std::string, ServiceStatus> status;
-
-        server_env_manager env(server_name);
-        if (!env.is_valid())
-        {
-            error << "Invalid server environment" << std::endl;
-            return status;
-        }
-
-        std::string output;
-        if (!execute_ssh_command(env.get_SSH_INFO(), sCommand(remotepath::agent(server_name), 
-            "./_allservicesstatus.sh", 
-            {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", remotepath::agent(server_name)}}), 
-            cMode::CaptureOutput | cMode::Silent, 
-            &output))
-            return status;
-
-        std::stringstream ss(output);
-        std::string line;
-        while (std::getline(ss, line))
-        {
-            std::string key, value;
-            std::size_t pos = line.find("=");
-            if (pos != std::string::npos)
-            {
-                key = dequote(trim(line.substr(0, pos)));
-                value = dequote(trim(line.substr(pos + 1)));
-
-                // decode key, it's of format SERVICENAME_[HEALTH|PORTS]
-                std::string service_name = key.substr(0, key.find_last_of("_"));
-                std::string status_type = key.substr(key.find_last_of("_") + 1);
-
-                if (status_type == "HEALTH")
-                { // healthy|unhealthy|unknown
-                    if (value == "healthy")
-                        status[service_name].health = HealthStatus::HEALTHY;
-                    else if (value == "unhealthy")
-                        status[service_name].health = HealthStatus::UNHEALTHY;
-                    else if (value == "unknown")
-                        status[service_name].health = HealthStatus::UNKNOWN;
-                    else
-                        status[service_name].health = HealthStatus::ERROR;
+            if (ctx.args.size() == 0)
+            { // just the command, no args yet.
+                // list servers
+                std::vector<ServerInfo> servers = get_configured_servers();
+                for (const auto &server : servers)
+                {
+                    rawout << server.name << std::endl;
                 }
-                else if (status_type == "PORTS")
-                { // port1,port2,port3
-                    std::vector<std::string> ports = string2multi(value);
-                    for (const auto &port : ports)
-                    {
-                        if (port != "unknown")
-                            status[service_name].ports.push_back(str2int(port));
+            }
+            else if (ctx.args.size() == 1)
+            {
+                // list services
+                std::vector<LocalServiceInfo> services = get_server_services_info(ctx.args[0]);
+                for (const auto &service : services)
+                {
+                    rawout << service.service_name << std::endl;
+                }
+            }
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // std_autocomplete_allowall : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        void std_autocomplete_allowall(const CommandContext &ctx)
+        {
+            std_autocomplete(ctx);
+            if (ctx.args.size() == 1)
+                rawout << "all" << std::endl;
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // rsync_tree_to_remote : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        bool rsync_tree_to_remote(
+            const std::string &local_path,
+            const std::string &remote_path,
+            server_env_manager &server_env,
+            bool silent)
+        {
+            ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync.");
+
+            std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " +
+                                    quote(local_path + "/") + " " +
+                                    quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" +
+                                          remote_path + "/");
+            return execute_local_command("", rsync_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // get_arch : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        std::string get_arch()
+        {
+            // determine the architecture of the system
+            std::string arch;
+#ifdef __aarch64__
+            arch = "arm64";
+#elif __x86_64__
+            arch = "amd64";
+#endif
+            return arch;
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // cRemoteTempFolder : SHARED CLASS
+        // ------------------------------------------------------------------------------------------------
+        cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
+        {
+            std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
+            std::string mkdir_cmd = "mkdir -p " + quote(p);
+            if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
+                error << "Failed to create temp directory on server" << std::endl;
+            else
+                mPath = p;
+        }
+
+        cRemoteTempFolder::~cRemoteTempFolder()
+        {
+            std::string rm_cmd = "rm -rf " + quote(mPath);
+            execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("", rm_cmd, {}), cMode::Silent);
+        }
+
+        std::string cRemoteTempFolder::path() const
+        {
+            return mPath;
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // get_all_services_status : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name)
+        {
+            std::map<std::string, ServiceStatus> status;
+
+            server_env_manager env(server_name);
+            if (!env.is_valid())
+            {
+                error << "Invalid server environment" << std::endl;
+                return status;
+            }
+
+            std::string output;
+            if (!execute_ssh_command(env.get_SSH_INFO(), sCommand(remotepath::agent(server_name), "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", remotepath::agent(server_name)}}),
+                                     cMode::CaptureOutput | cMode::Silent,
+                                     &output))
+                return status;
+
+            std::stringstream ss(output);
+            std::string line;
+            while (std::getline(ss, line))
+            {
+                std::string key, value;
+                std::size_t pos = line.find("=");
+                if (pos != std::string::npos)
+                {
+                    key = dequote(trim(line.substr(0, pos)));
+                    value = dequote(trim(line.substr(pos + 1)));
+
+                    // decode key, it's of format SERVICENAME_[HEALTH|PORTS]
+                    std::string service_name = key.substr(0, key.find_last_of("_"));
+                    std::string status_type = key.substr(key.find_last_of("_") + 1);
+
+                    if (status_type == "HEALTH")
+                    { // healthy|unhealthy|unknown
+                        if (value == "healthy")
+                            status[service_name].health = HealthStatus::HEALTHY;
+                        else if (value == "unhealthy")
+                            status[service_name].health = HealthStatus::UNHEALTHY;
+                        else if (value == "unknown")
+                            status[service_name].health = HealthStatus::UNKNOWN;
+                        else
+                            status[service_name].health = HealthStatus::ERROR;
+                    }
+                    else if (status_type == "PORTS")
+                    { // port1,port2,port3
+                        std::vector<std::string> ports = string2multi(value);
+                        for (const auto &port : ports)
+                        {
+                            if (port != "unknown")
+                                status[service_name].ports.push_back(str2int(port));
+                        }
                     }
                 }
             }
+            return status;
         }
-        return status;
-    }
 
-
-
-    // ------------------------------------------------------------------------------------------------
-    // healthtick : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    std::string healthtick(const std::string &server, const std::string &service)
-    {
-        std::string green_tick = "\033[32m✓\033[0m";
-        std::string red_cross = "\033[31m✗\033[0m";
-        std::string yellow_exclamation = "\033[33m!\033[0m";
-        std::string unknown = "\033[37m✓\033[0m";
-
-        HealthStatus status = is_healthy(server, service);
-        if (status == HealthStatus::HEALTHY)
-            return green_tick;
-        else if (status == HealthStatus::UNHEALTHY)
-            return red_cross;
-        else if (status == HealthStatus::UNKNOWN)
-            return unknown;
-        else
-            return yellow_exclamation;
-    }
-
-    // ------------------------------------------------------------------------------------------------
-    // HealthStatus2String : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    std::string HealthStatus2String(HealthStatus status)
-    {
-        if (status == HealthStatus::HEALTHY)
-            return ":tick:";
-        else if (status == HealthStatus::UNHEALTHY)
-            return ":cross:";
-        else if (status == HealthStatus::UNKNOWN)
-            return ":greytick:";
-        else if (status == HealthStatus::NOTINSTALLED)
-            return ":warning:";
-        else
-            return ":error:";
-    }
-
-
-    // ------------------------------------------------------------------------------------------------
-    // is_healthy : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    HealthStatus is_healthy(const std::string &server, const std::string &service)
-    {
-        server_env_manager env(server);
-        if (!env.is_valid())
+        // ------------------------------------------------------------------------------------------------
+        // healthtick : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        std::string healthtick(const std::string &server, const std::string &service)
         {
-            error << "Server service not initialized" << std::endl;
-            return HealthStatus::ERROR;
+            std::string green_tick = "\033[32m✓\033[0m";
+            std::string red_cross = "\033[31m✗\033[0m";
+            std::string yellow_exclamation = "\033[33m!\033[0m";
+            std::string unknown = "\033[37m✓\033[0m";
+
+            HealthStatus status = is_healthy(server, service);
+            if (status == HealthStatus::HEALTHY)
+                return green_tick;
+            else if (status == HealthStatus::UNHEALTHY)
+                return red_cross;
+            else if (status == HealthStatus::UNKNOWN)
+                return unknown;
+            else
+                return yellow_exclamation;
         }
 
-        if (!env.check_remote_dir_exists(remotepath::service(server, service)))
+        // ------------------------------------------------------------------------------------------------
+        // HealthStatus2String : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        std::string HealthStatus2String(HealthStatus status)
         {
-            return HealthStatus::NOTINSTALLED;
+            if (status == HealthStatus::HEALTHY)
+                return ":tick:";
+            else if (status == HealthStatus::UNHEALTHY)
+                return ":cross:";
+            else if (status == HealthStatus::UNKNOWN)
+                return ":greytick:";
+            else if (status == HealthStatus::NOTINSTALLED)
+                return ":warning:";
+            else
+                return ":error:";
         }
 
-        std::string script_path = remotepath::service_template(server, service) + "/status.sh";
-        if (!env.check_remote_file_exists(script_path))
+        // ------------------------------------------------------------------------------------------------
+        // is_healthy : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        HealthStatus is_healthy(const std::string &server, const std::string &service)
         {
-            return HealthStatus::UNKNOWN;
+            server_env_manager env(server);
+            if (!env.is_valid())
+            {
+                error << "Server service not initialized" << std::endl;
+                return HealthStatus::ERROR;
+            }
+
+            if (!env.check_remote_dir_exists(remotepath::service(server, service)))
+            {
+                return HealthStatus::NOTINSTALLED;
+            }
+
+            std::string script_path = remotepath::service_template(server, service) + "/status.sh";
+            if (!env.check_remote_file_exists(script_path))
+            {
+                return HealthStatus::UNKNOWN;
+            }
+
+            // Run status script, does not display output.
+            if (!env.run_remote_template_command(service, "status", {}, true, {}))
+                return HealthStatus::UNHEALTHY;
+            return HealthStatus::HEALTHY;
         }
 
-        // Run status script, does not display output.
-        if (!env.run_remote_template_command(service, "status", {}, true, {}))
-            return HealthStatus::UNHEALTHY;
-        return HealthStatus::HEALTHY;
-    }
+        // ------------------------------------------------------------------------------------------------
+        // healthmark : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        std::string healthmark(const std::string &server, const std::string &service)
+        {
+            HealthStatus status = is_healthy(server, service);
+            return HealthStatus2String(status);
+        }
 
+        // ------------------------------------------------------------------------------------------------
+        // cBackupFileName : SHARED CLASS
+        // ------------------------------------------------------------------------------------------------
+        cBackupFileName::cBackupFileName(const std::string &server, const std::string &service, const std::string &template_name)
+        {
+            mServer = server;
+            mService = service;
+            mTemplateName = template_name;
+            // Get current datetime for backup filename
+            auto now = std::chrono::system_clock::now();
+            auto time = std::chrono::system_clock::to_time_t(now);
+            std::stringstream datetime;
+            datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
+            mDatetime = datetime.str();
+        }
 
-    // ------------------------------------------------------------------------------------------------
-    // healthmark : SHARED COMMAND
-    // ------------------------------------------------------------------------------------------------
-    std::string healthmark(const std::string &server, const std::string &service)
-    {
-        HealthStatus status = is_healthy(server, service);
-        return HealthStatus2String(status);
-    }
+        cBackupFileName::cBackupFileName(const std::string &filename)
+        {
+            // Parse the filename according to the format:
+            // server + magic_string() + template_name + magic_string() + service + magic_string() + datetime + ".tgz"
+            std::string name = filename;
+            if (name.size() > 4 && name.substr(name.size() - 4) == ".tgz")
+                name = name.substr(0, name.size() - 4);
+            std::string sep = magic_string();
+            size_t first = name.find(sep);
+            size_t second = name.find(sep, first + sep.size());
+            size_t third = name.find(sep, second + sep.size());
+            if (first == std::string::npos || second == std::string::npos || third == std::string::npos)
+            {
+                mServer = mService = mTemplateName = mDatetime = "";
+                return;
+            }
+            mServer = name.substr(0, first);
+            mTemplateName = name.substr(first + sep.size(), second - (first + sep.size()));
+            mService = name.substr(second + sep.size(), third - (second + sep.size()));
+            mDatetime = name.substr(third + sep.size());
+        }
+
+        std::string cBackupFileName::get_filename() const
+        {
+            return mServer + magic_string() + mTemplateName + magic_string() + mService + magic_string() + mDatetime + ".tgz";
+        }
+
+        std::string cBackupFileName::get_server() const { return mServer; }
+        std::string cBackupFileName::get_service() const { return mService; }
+        std::string cBackupFileName::get_template_name() const { return mTemplateName; }
+        std::string cBackupFileName::get_datetime() const { return mDatetime; }
+
+        bool cBackupFileName::is_valid() const
+        {
+            // All fields must be non-empty, and none may contain the magic string
+            return !mServer.empty() && !mService.empty() && !mTemplateName.empty() && !mDatetime.empty() &&
+                   !has_magic_string(mServer) && !has_magic_string(mService) && !has_magic_string(mTemplateName);
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // scp_file_to_remote : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        bool scp_file_to_remote(const server_env_manager &server_env, const std::string &local_path, const std::string &remote_path, bool silent)
+        {
+            if (!server_env.is_valid())
+            {
+                error << "Invalid server environment" << std::endl;
+                return false;
+            }
+            ASSERT(!remote_path.empty() && !local_path.empty(), "Remote or local path not specified. Can't scp.");
+            std::string scp_cmd = "scp -P " + server_env.get_SSH_PORT() + " " + quote(local_path) + " " + server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" + quote(remote_path) + (silent ? " > /dev/null 2>&1" : "");
+            return execute_local_command("", scp_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
+        }
+
+        // ------------------------------------------------------------------------------------------------
+        // scp_file_from_remote : SHARED COMMAND
+        // ------------------------------------------------------------------------------------------------
+        bool scp_file_from_remote(const server_env_manager &server_env, const std::string &remote_path, const std::string &local_path, bool silent)
+        {
+            if (!server_env.is_valid())
+            {
+                error << "Invalid server environment" << std::endl;
+                return false;
+            }
+            ASSERT(!remote_path.empty() && !local_path.empty(), "Remote or local path not specified. Can't scp.");
+            std::string scp_cmd = "scp -P " + server_env.get_SSH_PORT() + " " + server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" + quote(remote_path) + " " + quote(local_path) + (silent ? " > /dev/null 2>&1" : "");
+            return execute_local_command("", scp_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
+        }
 
     } // namespace shared_commands
 
diff --git a/source/src/commands/shared_commands.hpp b/source/src/commands/shared_commands.hpp
index ad3ebe3..ce21123 100644
--- a/source/src/commands/shared_commands.hpp
+++ b/source/src/commands/shared_commands.hpp
@@ -57,6 +57,29 @@ namespace dropshell
     void std_autocomplete(const CommandContext &ctx);
     void std_autocomplete_allowall(const CommandContext &ctx);
 
+    class cBackupFileName
+    {
+    public:
+        cBackupFileName(const std::string &server, const std::string &service, const std::string &template_name);
+        cBackupFileName(const std::string &filename);
+
+        std::string get_filename() const;
+        std::string get_server() const;
+        std::string get_service() const;
+        std::string get_template_name() const;
+        std::string get_datetime() const;
+
+        bool is_valid() const;
+
+    private:
+        std::string mServer;
+        std::string mService;
+        std::string mTemplateName;
+        std::string mDatetime;
+    };
+
+    bool scp_file_to_remote(const server_env_manager &server_env, const std::string &local_path, const std::string &remote_path, bool silent);
+    bool scp_file_from_remote(const server_env_manager &server_env, const std::string &remote_path, const std::string &local_path, bool silent);
 
     } // namespace shared_commands
 } // namespace dropshell
diff --git a/source/src/service_runner.cpp b/source/src/service_runner.cpp
index 1a43d10..2660b8f 100644
--- a/source/src/service_runner.cpp
+++ b/source/src/service_runner.cpp
@@ -91,7 +91,6 @@ namespace fs = std::filesystem;
 
 namespace dropshell {
 
-static const std::string magic_string = "-_-";
 
 service_runner::service_runner(const std::string& server_name, const std::string& service_name) : 
     mServerEnv(server_name), mServer(server_name), mService(service_name), mValid(false)
@@ -298,7 +297,7 @@ bool service_runner::restore(std::string backup_file, bool silent)
     }
 
     // split the backup filename into parts based on the magic string
-    std::vector<std::string> parts = dropshell::split(backup_file, magic_string);
+    std::vector<std::string> parts = dropshell::split(backup_file, "-_-");
     if (parts.size() != 4) {
         std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl;
         return false;
@@ -383,12 +382,6 @@ bool service_runner::restore(std::string backup_file, bool silent)
 }
 
 
-bool name_breaks_backups(std::string name)
-{
-    // if name contains -_-, return true
-    return name.find("-_-") != std::string::npos;
-}
-
 // backup the service over ssh, using the credentials from server.env (via server_env.hpp)
 // 1. run backup.sh on the server
 // 2. create a backup file with format server-service-datetime.tgz
@@ -452,17 +445,18 @@ bool service_runner::backup(bool silent) {
     std::stringstream datetime;
     datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
     
-    if (name_breaks_backups(mServer)) {std::cerr << "Error: Server name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;}
-    if (name_breaks_backups(mService)) {std::cerr << "Error: Service name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;}
-    if (name_breaks_backups(service_info.template_name)) {std::cerr << "Error: Service template name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;}
-
     // Construct backup filename
-    std::string backup_filename = mServer + magic_string + service_info.template_name + magic_string + mService + magic_string + datetime.str() + ".tgz";
+    shared_commands::cBackupFileName backup_filename_construction(mServer, mService, service_info.template_name);
+    if (!backup_filename_construction.is_valid()) {
+        std::cerr << "Invalid backup filename" << std::endl;
+        return false;
+    }
+    std::string backup_filename = backup_filename_construction.get_filename();
     std::string remote_backup_file_path = remote_backups_dir + "/" + backup_filename;
     std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_filename).string();
 
     // assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
-    ASSERT(3 == count_substring(magic_string, local_backup_file_path), "Invalid backup filename");
+    ASSERT(3 == count_substring("-_-", local_backup_file_path), "Invalid backup filename");
 
     {   // Run backup script
         shared_commands::cRemoteTempFolder remote_temp_folder(mServerEnv);
@@ -501,7 +495,7 @@ std::string service_runner::get_latest_backup_file(const std::string& server, co
     }
 
     // Build the expected prefix for backup files
-    std::string prefix = server + magic_string + info.template_name + magic_string + service + magic_string;
+    std::string prefix = server + "-_-" + info.template_name + "-_-" + service + "-_-";
     std::string latest_file;
     std::string latest_datetime;
 
diff --git a/source/src/utils/utils.cpp b/source/src/utils/utils.cpp
index 2a10e9b..c75f5da 100644
--- a/source/src/utils/utils.cpp
+++ b/source/src/utils/utils.cpp
@@ -10,6 +10,16 @@
 
 namespace dropshell {
 
+std::string magic_string() {
+    return "-_-";
+}
+
+bool has_magic_string(std::string name)
+{
+    return name.find(magic_string()) != std::string::npos;
+}
+
+
 void maketitle(const std::string& title, sColour colour) {
     colourstream(colour) << std::string(title.length() + 4, '-') << std::endl;
     colourstream(colour) << "| " << title << " |" << std::endl;
diff --git a/source/src/utils/utils.hpp b/source/src/utils/utils.hpp
index 4655238..f9a57dc 100644
--- a/source/src/utils/utils.hpp
+++ b/source/src/utils/utils.hpp
@@ -16,6 +16,8 @@ void maketitle(const std::string& title, sColour colour=sColour::INFO);
 
 bool replace_line_in_file(const std::string& file_path, const std::string& search_string, const std::string& replacement_line);
 
+std::string magic_string();
+bool has_magic_string(std::string name);
 
 // utility functions
 std::string trim(std::string str);