Offsite Backup for My Blog Server: Qiniu Infrequent Access Storage + Restic
Once your blog is up and running, it's easy to fall into the illusion that everything is done: the pages load, the admin panel works, and you can publish posts.
But after thinking about it, I realized that's not really the case.
Rebuilding the blog itself isn't hard. What's hard is all the stuff that accumulates over time: articles, databases, images, configuration files, reverse proxy settings, Docker Compose files, environment variables, cron jobs. If the server's hard drive fails, or I accidentally delete the wrong directory, or something goes wrong during a system upgrade, those things might not be recoverable by just "redeploying."
So I finally decided to set up an offsite backup for my blog server.
I chose Qiniu's Infrequent Access storage, Restic as the backup tool, and a weekly backup frequency. It's not a fancy solution, but it's more than enough for a personal blog.
This post is mainly a record of my own process. If something goes wrong with the server in the future, at least I can follow this guide to restore everything.
Why Offsite Backup
Many people's first instinct for backup is to make a local copy on the server.
For example:
cp -r /www /backup/www
That counts as a backup, but the problem is it's still on the same machine.
If you just accidentally delete a file, a local backup works. But if the hard drive fails, the server OS crashes, or the entire machine becomes inaccessible, so is your local backup.
So I think a more reliable approach is:
- Run the blog on the local server.
- Store the backup somewhere else.
- Don't touch it under normal circumstances.
- Be able to pull it back and restore when something actually goes wrong.
That's why I chose object storage this time.
Why Qiniu Infrequent Access Storage
Qiniu object storage has different tiers, like Standard, Infrequent Access, and Archive.
Blog backups have a specific characteristic: they're rarely downloaded — only when something goes wrong and you need to restore.
So Standard storage is a bit wasteful, and Archive storage is too cumbersome. Infrequent Access sits right in the middle, suitable for data that's "not frequently accessed, but you want to be able to retrieve it when needed."
However, Infrequent Access storage isn't without its pitfalls.
It usually has a minimum storage duration. For example, Infrequent Access typically has a 30-day minimum storage period. If you upload and then delete it quickly, you might still be billed for the minimum storage time.
Another point is that there are retrieval fees when you restore and download data. So it's cheap to just leave it there, but when you actually need to download and restore, there will be an additional cost.
So my strategy is simple:
- Back up once a week.
- Don't delete things every day.
- Don't keep only one backup.
- Keep backups from the last few months.
I ended up keeping the last 12 weekly backups, which is about 3 months.
Why Not Just Upload a Compressed Archive
At first, I thought about the simplest method: create a compressed archive every week and upload it to Qiniu.
For example:
tar -czf blog-backup.tar.gz /www /etc/nginx
This method is indeed simple, but after thinking it over, I saw a few issues.
First, it's always a full backup. If the website directory is 30GB, you'd have to repackage and re-upload 30GB every week. Even if you only changed one article that week, you'd have to upload everything again.
Second, version management is a pain. Over time, you'll have many archives. You'll easily forget which one is good, which one contains the database, which one you've restored from.
Fourth, the restore experience is mediocre. When you actually need to restore, you have to download the archive, extract it, verify the paths, and manually overwrite files.
Also, you have to handle encryption yourself. Blogs might contain databases, configuration files, keys, environment variables. A plain compressed archive sitting in object storage doesn't feel very secure.
Fourth, the restore experience is mediocre. When you actually need to restore, you have to download the archive, extract it, verify the paths, and manually overwrite files.
Later, I found out that Qiniu Kodo supports the S3-compatible API, and Restic can back up directly to S3, so I chose Restic in the end.
What is Restic
Restic is a backup tool. It doesn't just package files and upload them; it creates a backup repository.
It has several advantages:
- Supports incremental backups.
- Supports deduplication.
- Encrypted by default.
- Can view historical snapshots.
- Can restore to a specific point in time.
- Can interface with S3-compatible object storage.
Simply put, the first backup will upload more data, but subsequent backups only upload the changed parts.
For example, if I only changed a few articles this week, Restic won't stupidly re-upload the entire blog. It will identify the changes and only upload what's necessary.
This is quite suitable for weekly backups.
First, Confirm What to Back Up
Before backing up, I think the most important step isn't writing commands, but thinking clearly: what exactly needs to be backed up?
Because many people jump straight to backing up the entire server root directory:
/
This seems like the safest approach, but it might not be.
There are many things in the entire system that don't need to be backed up, like caches, logs, temporary files, Docker image layers. These things are large in size and might not be useful during recovery.
What I really care about are these:
/www
/var/www
/opt
/etc/nginx
/etc/systemd/system
Different people have different server paths, so you can't just copy this directly.
If you deployed your blog using 1Panel, you might want to focus on:
/opt/1panel
/opt/1panel/apps
/www
If you deployed your blog using Docker Compose, you should focus on backing up:
docker-compose.yml.env- Mounted data directories
- Database dump files
If your blog has a database, you must dump the database separately. If you only back up the website files without the database, you'll likely end up with just the program after recovery, without any article data.
Things Not Recommended for Backup
I generally don't recommend backing up the following directories directly:
/var/log
/tmp
/var/tmp
/var/lib/docker/overlay2
node_modules
.cache
logs
The reasons are simple.
Logs keep growing and are generally not needed when restoring a blog.
Temporary directories don't need to be backed up.
node_modules can be reinstalled.
Docker's overlay2 is huge and not suitable for direct copy restoration.
Backing up cache directories just wastes space.
More backup isn't always better. Back up what needs backing up, and don't include what doesn't.
Installing Restic
My server is Ubuntu, so I installed it directly with apt:
sudo apt update
sudo apt install -y restic
After installation, check the version:
restic version
If you see a version number, it's installed.
Prepare Qiniu Information
Next, you need to prepare some information from Qiniu:
- Access Key
- Secret Key
- Bucket Name
- Region ID
You can find the Access Key and Secret Key in the Qiniu console.
The Bucket Name is the name of the object storage space you created.
The Region ID depends on which region your space is in. For example, East China, South China, North China, Singapore, etc., have different Region IDs.
The format for Restic to connect to Qiniu Kodo's S3 endpoint is roughly:
s3:s3.<Region-ID>.qiniucs.com/<bucket_name>
For example, if your Region ID is:
cn-east-1
And your Bucket Name is:
my-blog-backup
Then the repository address can be written as:
s3:s3.cn-east-1.qiniucs.com/my-blog-backup/blog-server
The final blog-server is a path I added to distinguish different backup contents within this bucket.
If you want to back up NAS, photos, other servers in the future, you can also use different directories within the same bucket.
Create Restic Configuration File
I don't want to manually enter AK, SK, repository address, and password every time I run a command, so I put these things in a separate configuration file.
First, create the directory:
sudo mkdir -p /etc/restic
Then create the configuration file:
sudo nano /etc/restic/blog.env
Write the following content:
You need to change these to your own:
- Your Qiniu AccessKey
- Your Qiniu SecretKey
cn-east-1- Your bucket name
- A long backup password you set yourself
RESTIC_PASSWORD must be saved securely.
This password is not your Qiniu password; it's the password Restic uses to encrypt the backup repository. You'll need it to restore backups later.
If you lose this password, even if the files in Qiniu are still there, you won't be able to restore them.
After saving, tighten the permissions on the configuration file:
sudo chmod 600 /etc/restic/blog.env
This way, regular users can't easily read the keys inside.
Initialize the Backup Repository
The first time you use Restic, you need to initialize the repository.
Execute:
sudo bash -c 'source /etc/restic/blog.env && restic -r "$RESTIC_REPOSITORY" -o s3.region="$QINIU_REGION" init'
If successful, you'll see a message like:
created restic repository ...
If you get an error here, don't proceed further. Generally, check these things:
- Is the Access Key correct?
- Is the Secret Key correct?
- Is the Bucket Name correct?
- Is the Region ID correct?
- Can the server access Qiniu's S3 endpoint?
Once this step works, continue.
Test with a Small Directory First, Don't Back Up Real Data Directly
I recommend not rushing to back up the entire blog.
First, test with a small directory to confirm that both upload and restore work.
Create a test directory:
mkdir -p /tmp/qiniu-test
echo "hello backup" > /tmp/qiniu-test/test.txt
Back up this small directory:
sudo bash -c 'source /etc/restic/blog.env && restic -r "$RESTIC_REPOSITORY" -o s3.region="$QINIU_REGION" backup /tmp/qiniu-test'
View snapshots:
sudo bash -c 'source /etc/restic/blog.env && restic -r "$RESTIC_REPOSITORY" -o s3.region="$QINIU_REGION" snapshots'
If you can see the snapshot you just created, the backup has been uploaded.
Then test restoration:
Check the restored file:
cat /tmp/qiniu-restore/tmp/qiniu-test/test.txt
If you see:
hello backup
It means both backup and restore work fine.
Although this step might seem a bit excessive, I think it's very important. Because a backup tool isn't just about being able to upload; you must confirm you can restore.
Write the Real Backup Script
Once the test is successful, you can write the real backup script.
Create the script:
sudo nano /usr/local/bin/backup-blog-to-qiniu.sh
Write the following content:
After saving, give the script execute permissions:
sudo chmod +x /usr/local/bin/backup-blog-to-qiniu.sh
There are several places you need to modify according to your own server.
If you don't have /var/www, you can delete it.
If you don't have /www, you can also delete it.
If your blog data is in another directory, add that directory.
If your database is in Docker, change the database dump command to your own container name.
Don't just copy the paths and forget about them. The most important thing for a backup script is that it matches your server's actual situation.
First Manual Run
After writing the script, run it manually once:
sudo /usr/local/bin/backup-blog-to-qiniu.sh
The first time will be slower because it has to upload all the data being backed up.
After it finishes, check the log:
tail -n 100 /var/log/blog-qiniu-backup.log
Then check the snapshots:
sudo bash -c 'source /etc/restic/blog.env && restic -r "$RESTIC_REPOSITORY" -o s3.region="$QINIU_REGION" snapshots'
If you can see a new snapshot, the real backup was successful.
Set Up Weekly Automatic Backup
I plan to back up once a week, for example, every Monday at 4 AM.
Edit root's crontab:
sudo crontab -e
Add this line:
0 4 * * 1 /usr/local/bin/backup-blog-to-qiniu.sh
This line means:
Run the backup script at 4 AM every Monday.
After saving, check it:
sudo crontab -l
If you can see the line you just added, the cron job is set up.
You can also check the server's timezone:
timedatectl
If the server timezone isn't what you want, you can change it to your local time, e.g., Asia/Seoul:
sudo timedatectl set-timezone Asia/Seoul
How to Restore a Backup
Backup is just the first step; restoration is the key.
First, check what snapshots are available:
sudo bash -c 'source /etc/restic/blog.env && restic -r "$RESTIC_REPOSITORY" -o s3.region="$QINIU_REGION" snapshots'
Restore the latest backup to /restore/blog:
To restore a specific snapshot, replace latest with the snapshot ID:
During restoration, I don't recommend overwriting the live directory directly.
A safer approach is to restore to a temporary directory first, like:
/restore/blog
After confirming the files are complete, the database dump exists, and the configuration is correct, then decide how to overwrite.
If you have a MySQL backup file, you can restore it like this:
mysql -u root -p < mysql-all.sql
If MySQL is in a Docker container, it might be like this:
docker exec -i mysql-container-name mysql -u root -pDatabasePassword < mysql-all.sql
The exact command depends on your database environment.
About Costs
The cost of Infrequent Access storage is mainly not about "upload traffic," but these parts:
- Storage capacity fees.
- Request fees.
- Data retrieval fees during restoration.
- Egress traffic fees when downloading to the internet.
If you only back up once a week and don't restore normally, the cost is usually not too high.
What really makes the cost high is:
- Backing up the entire Docker directory.
- Backing up logs, caches, temporary files.
- The backup contains a large number of images, videos, attachments.
- Frequently deleting objects in Infrequent Access storage.
- Frequently downloading and restoring from Infrequent Access storage.
That's why I didn't choose "back up once a week and keep only one." Infrequent Access storage is not suitable for frequent deletion. Keeping the last 12 weekly backups is more stable.
Restic's forget command is:
restic forget --keep-weekly 12 --prune
This means keeping the last 12 weekly backups.
If the blog data grows larger in the future, you can adjust the strategy, like keeping 8 weeks, 12 weeks, 24 weeks, depending on your data volume and budget.
My Final Backup Strategy
To summarize, my backup strategy is roughly:
- Back up blog files.
- Back up Nginx configuration.
- Back up Docker Compose and environment variables.
- Dump the database separately.
- Don't back up caches, logs, node_modules.
- Run automatically once a week.
- Keep the last 12 weekly snapshots.
- Periodically perform a manual restore test.
For a personal blog, this solution isn't complicated and won't be too expensive.
The most important thing is that when the server actually has a problem, you won't be completely helpless.
I used to think backup was something to "deal with later." But servers don't usually give you a heads-up when something goes wrong.
So I decided to take care of it this time.
You can take your time writing the blog, and you can tinker with the server, but it's best not to keep your data in only one place.