{"id":180,"date":"2026-05-30T16:42:06","date_gmt":"2026-05-30T16:42:06","guid":{"rendered":"https:\/\/blog.offshorekaka.in\/?p=180"},"modified":"2026-08-27T05:43:48","modified_gmt":"2026-08-27T05:43:48","slug":"backup-strategy-for-offshore-servers","status":"publish","type":"post","link":"https:\/\/offshorekaka.in\/blog\/backup-strategy-for-offshore-servers\/","title":{"rendered":"Backup Strategy for Offshore Servers: What to Automate"},"content":{"rendered":"<div class=\"ok-pro-panel\">\n<p><span class=\"ok-pro-kicker\">Server Guides<\/span><\/p>\n<h2>The only thing that counts<\/h2>\n<p>A backup is not a backup until you have restored from it. Everything else on this page is detail. We have seen people run nightly archives for two years, lose a server, and then discover the cron job had been failing silently since month one because a disk filled up.<\/p>\n<p>So this guide covers what to back up on an <a href=\"https:\/\/offshorekaka.in\/offshore-vps-server\/\">offshore server<\/a>, how to automate it, how to get the copies off the machine, and how to actually test a restore without breaking the live site.<\/p>\n<div class=\"ok-pro-grid\">\n<div class=\"ok-pro-card\"><strong>Time<\/strong><span>30 minutes to set up properly.<\/span><\/div>\n<div class=\"ok-pro-card\"><strong>You need<\/strong><span>SSH and somewhere off-server to send copies.<\/span><\/div>\n<div class=\"ok-pro-card\"><strong>Test cadence<\/strong><span>Restore once a quarter, minimum.<\/span><\/div>\n<\/div>\n<\/div>\n<div class=\"ok-toc\"><strong>On this page<\/strong><a href=\"#what\">What actually needs backing up<\/a><a href=\"#321\">3-2-1 in plain terms<\/a><a href=\"#db\">The database<\/a><a href=\"#files\">The files<\/a><a href=\"#cron\">Automating it<\/a><a href=\"#offsite\">Getting it off the server<\/a><a href=\"#restore\">Testing a restore<\/a><a href=\"#faq\">FAQ<\/a><\/div>\n<h2 id=\"what\">What actually needs backing up<\/h2>\n<p>Not everything on the disk. Copying the whole filesystem gives you a huge archive full of things you can reinstall in five minutes, and it makes restores slower and more confusing.<\/p>\n<ul>\n<li><strong>The database.<\/strong> Every post, user, order and setting. Losing this is the one you cannot recover from.<\/li>\n<li><strong>User uploads.<\/strong> <code>wp-content\/uploads<\/code> for WordPress. Images and documents people gave you that exist nowhere else.<\/li>\n<li><strong>Config files.<\/strong> <code>wp-config.php<\/code>, your nginx or Apache vhost, any cron scripts, TLS certificates if they are not from Let&#8217;s Encrypt.<\/li>\n<li><strong>Custom code.<\/strong> A child theme or a plugin you wrote. If it is in git, git is your backup.<\/li>\n<\/ul>\n<p>Core files, stock plugins and themes are all downloadable, so they are optional. Include them if the archive stays small, skip them if it does not.<\/p>\n<h2 id=\"321\">3-2-1 in plain terms<\/h2>\n<p>Three copies of the data, on two different kinds of storage, one of them somewhere else entirely. The rule matters because of how servers actually die. A snapshot on the same hypervisor does not help when the account gets suspended. A second disk does not help when the datacentre has a problem. The off-site copy is the one that saves you, and it is the one most setups are missing.<\/p>\n<h2 id=\"db\">The database<\/h2>\n<pre><code>mysqldump --single-transaction --quick -u dbuser -p dbname &gt; \/backups\/db-$(date +%F).sql<\/code><\/pre>\n<p>Those two flags matter on a live site. <code>--single-transaction<\/code> takes a consistent snapshot without locking tables, so the site keeps working during the dump. <code>--quick<\/code> streams rows instead of buffering the whole table in memory, which is what stops a big table from killing a 2GB VPS.<\/p>\n<p>Compress it. SQL is text and compresses to about a tenth of its size:<\/p>\n<pre><code>mysqldump --single-transaction --quick -u dbuser -p dbname | gzip &gt; \/backups\/db-$(date +%F).sql.gz<\/code><\/pre>\n<h2 id=\"files\">The files<\/h2>\n<pre><code>tar czf \/backups\/files-$(date +%F).tgz \/var\/www\/yourdomain.com<\/code><\/pre>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-backup-tar-run.webp\" alt=\"Terminal creating a tar archive then listing its contents to confirm the files are inside\" loading=\"lazy\" width=\"2296\" height=\"686\"><figcaption>Create it, then list it. An archive you have never opened is not evidence of anything.<\/figcaption><\/figure>\n<p>That second command is the habit worth building. <code>tar tzf<\/code> reads the archive and prints what is in it. It takes a second and it catches the case where the archive was created but is empty, which is more common than you would think.<\/p>\n<p>Skip caches while you are at it. They are large, they regenerate on their own, and they add nothing to a restore:<\/p>\n<pre><code>tar czf \/backups\/files-$(date +%F).tgz \n  --exclude='*\/cache\/*' --exclude='*\/wp-content\/cache\/*' \n  \/var\/www\/yourdomain.com<\/code><\/pre>\n<h2 id=\"cron\">Automating it<\/h2>\n<p>Put the commands in a script rather than in the crontab line itself. Crontab lines with pipes and date substitutions get mangled, and a script is something you can run by hand to test.<\/p>\n<pre><code>crontab -e<\/code><\/pre>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-backup-cron.webp\" alt=\"Crontab entries for a nightly backup and weekly offsite sync, with a log showing successful runs\" loading=\"lazy\" width=\"2296\" height=\"536\"><figcaption>The log on the right is the important half. Without it a failing job is invisible.<\/figcaption><\/figure>\n<p>Always redirect output to a log. A cron job that fails silently is worse than no cron job, because you believe you are covered. Make the script echo the archive sizes as it goes, so a sudden drop from 400MB to 2KB is obvious at a glance.<\/p>\n<p>Add retention too, or the disk fills and every job after that fails:<\/p>\n<pre><code>find \/backups -name '*.tgz' -mtime +14 -delete\nfind \/backups -name '*.sql.gz' -mtime +30 -delete<\/code><\/pre>\n<h2 id=\"offsite\">Getting it off the server<\/h2>\n<p>A backup sitting on the same disk as the site protects you from exactly one thing: deleting a file by accident. It does nothing about hardware failure, a compromise, or an account suspension.<\/p>\n<pre><code>rsync -az --delete \/backups\/ backup@203.0.113.20:\/srv\/yourdomain\/<\/code><\/pre>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-backup-rsync-offsite.webp\" alt=\"rsync transferring backup files to a remote server with a transfer summary\" loading=\"lazy\" width=\"2296\" height=\"490\"><figcaption>rsync only sends what changed, so nightly runs after the first are quick.<\/figcaption><\/figure>\n<p>For object storage, rclone handles Backblaze B2, Wasabi and S3 with the same syntax:<\/p>\n<pre><code>rclone copy \/backups remote:yourdomain-backups<\/code><\/pre>\n<p>Two things to get right here. Use a key that can write but not delete, so that if the server is compromised the attacker cannot wipe your backups along with it. And encrypt anything leaving the server, because a database dump contains user data and password hashes.<\/p>\n<h2 id=\"restore\">Testing a restore<\/h2>\n<p>Never test on production. Spin up the cheapest VPS you can rent for an hour, or use a staging subdomain, and restore into that.<\/p>\n<figure class=\"wp-block-image size-large ok-inline-visual\"><img decoding=\"async\" src=\"https:\/\/offshorekaka.in\/blog\/wp-content\/uploads\/2026\/08\/ok-db-export-import.webp\" alt=\"Terminal exporting a MySQL database to a file and importing it into a new database\" loading=\"lazy\" width=\"2296\" height=\"396\"><figcaption>Export and import. If the import throws errors, your backup is not usable.<\/figcaption><\/figure>\n<p>The restore itself:<\/p>\n<ol>\n<li>Extract the file archive to the new web root.<\/li>\n<li>Create an empty database and import the dump into it.<\/li>\n<li>Update the database credentials in <code>wp-config.php<\/code>.<\/li>\n<li>Fix the URLs so the copy does not phone home to the live site. Our <a href=\"https:\/\/offshorekaka.in\/blog\/create-staging-site-on-offshore-hosting\/\">staging site guide<\/a> covers this.<\/li>\n<li>Load the homepage, an inner page, and log in to the dashboard.<\/li>\n<\/ol>\n<p>Write down how long it took. When you actually need a restore, knowing it takes forty minutes is the difference between a calm afternoon and a panic.<\/p>\n<p>Set an alert on the backup log too, so a failing job tells you rather than waiting to be discovered. Our <a href=\"https:\/\/offshorekaka.in\/blog\/offshore-server-monitoring-guide\/\">monitoring guide<\/a> covers alerting on disk space, which is the thing that usually kills a backup job in the first place.<\/p>\n<h2 id=\"trouble\">When something breaks<\/h2>\n<table>\n<thead>\n<tr>\n<th>What you see<\/th>\n<th>Why<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Backup file is a few kilobytes<\/td>\n<td>Wrong credentials or wrong database name, and the error went to a log nobody reads<\/td>\n<td>Run the command by hand and read the output<\/td>\n<\/tr>\n<tr>\n<td>Cron job never runs<\/td>\n<td>Script is not executable, or cron has a minimal PATH<\/td>\n<td><code>chmod +x<\/code> the script and use full paths to mysqldump and tar inside it<\/td>\n<\/tr>\n<tr>\n<td>Disk full, everything fails<\/td>\n<td>No retention policy<\/td>\n<td>Add the find and delete lines above<\/td>\n<\/tr>\n<tr>\n<td>Import fails on unknown collation<\/td>\n<td>Restoring a newer MySQL dump onto an older server<\/td>\n<td>Match the versions, or dump with <code>--compatible<\/code><\/td>\n<\/tr>\n<tr>\n<td>Site loads but images are missing<\/td>\n<td>Uploads were excluded by an over-eager exclude pattern<\/td>\n<td>List the archive and check uploads is really in it<\/td>\n<\/tr>\n<tr>\n<td>MySQL dies during the dump<\/td>\n<td>No <code>--quick<\/code>, and a large table filled memory<\/td>\n<td>Add <code>--single-transaction --quick<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"checklist\">Checklist<\/h2>\n<ul>\n<li>Database dumped with <code>--single-transaction --quick<\/code> and compressed.<\/li>\n<li>Files archived, caches excluded, and the archive listed to confirm contents.<\/li>\n<li>Both running from a script on a cron schedule.<\/li>\n<li>Output written to a log that shows file sizes.<\/li>\n<li>Retention set so the disk cannot fill.<\/li>\n<li>Copies going off the server with a write-only key.<\/li>\n<li>A full restore tested somewhere that is not production.<\/li>\n<li>Restore time written down.<\/li>\n<\/ul>\n<div class=\"ok-cta-box\">\n<p><strong>Want backups you do not have to think about?<\/strong><\/p>\n<p>OffshoreKaka shared hosting includes automatic backups, and VPS plans give you root so you can run exactly the schedule you want.<\/p>\n<p><a class=\"btn ok-cta-link\" href=\"https:\/\/offshorekaka.in\/offshore-web-hosting\/\">See the hosting plans<\/a><\/p>\n<\/div>\n<h2 id=\"faq\">FAQ<\/h2>\n<h3>How often should backups run?<\/h3>\n<p>Ask yourself how much work you are willing to redo. A blog that gets one post a week is fine on daily backups. A shop taking orders every hour needs the database backed up several times a day, because losing six hours of orders is a real business problem. Files change less than databases, so it is normal to back them up less often.<\/p>\n<h3>Are host snapshots enough?<\/h3>\n<p>They are a good first copy and a fast way back from a bad update. They are not an off-site copy, because they live with the same provider and usually on the same infrastructure. If the account goes away, so do they. Use them, but not only them.<\/p>\n<h3>Should I encrypt the backups?<\/h3>\n<p>Yes, for anything leaving the server. A database dump has user emails and password hashes in it. <code>gpg --symmetric<\/code> before upload, or rclone&#8217;s built-in crypt remote, both work fine.<\/p>\n<h3>Do backups help SEO?<\/h3>\n<p>Only in the sense that a site which comes back up in an hour instead of a week keeps its rankings, and one that is gone for a week does not. There is no direct credit for having them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A backup is not a backup until you have restored it. What to automate, where to keep it, and how to test without touching production.<\/p>\n","protected":false},"author":1,"featured_media":910,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-180","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server-security"],"_links":{"self":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/180","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/comments?post=180"}],"version-history":[{"count":5,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/180\/revisions"}],"predecessor-version":[{"id":1008,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/posts\/180\/revisions\/1008"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/media\/910"}],"wp:attachment":[{"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/media?parent=180"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/categories?post=180"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/offshorekaka.in\/blog\/wp-json\/wp\/v2\/tags?post=180"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}