Learning objectives
After reading this article you will be able to:
- Upload a database dump and a folder of files to an S3-compatible bucket
- Schedule the upload, name files by date and apply automatic retention
- Verify integrity and restore from the bucket
On this page
- What do you need before you start?
- How do you configure rclone for an S3 bucket?
- How do you upload a database dump to the bucket?
- How do you schedule the upload?
- How do you apply retention and delete old copies?
- How do you restore and check that it worked?
- What are the common mistakes?
- How to do this on Square Cloud
- FAQs
A backup that lives on the same server as the data it protects fails together with that server. A broken disk, a suspended account, an rm -rf in the wrong directory or ransomware takes both at once. That is why a backup belongs somewhere separate, and object storage is one of the most practical places: cheap per GB, reachable over HTTP from anywhere and with a standard API.
The full flow is to create the file, upload it to an S3-compatible bucket, name it by date, delete what got old and test the restore. The examples use PostgreSQL and rclone, and the ideas apply to any database or folder. Check each tool's options in its official documentation, listed in the sources.
What do you need before you start?#
- A bucket on an S3-compatible object storage service, with its endpoint, access key and secret key (see what S3-compatible storage is and what a bucket is).
- rclone installed on the machine that runs the backup. The
aws-cliworks too, with similar commands. - The database tools:
pg_dumpandpg_restorefor PostgreSQL (for MySQL and MongoDB, see how to back up a database). - A key dedicated to backups, separate from the key your application uses. If the provider allows it, scope it to that one bucket.
Never put keys in the script or the repository. Use environment variables or a config file with restricted permissions (chmod 600).
How do you configure rclone for an S3 bucket?#
rclone accepts configuration through environment variables named RCLONE_CONFIG_<NAME>_<OPTION>, so you need no config file. For an S3-compatible service that is not Amazon, use provider=Other:
export RCLONE_CONFIG_BACKUP_TYPE=s3
export RCLONE_CONFIG_BACKUP_PROVIDER=Other
export RCLONE_CONFIG_BACKUP_ENDPOINT="https://your-endpoint.example.com"
export RCLONE_CONFIG_BACKUP_ACCESS_KEY_ID="your-key"
export RCLONE_CONFIG_BACKUP_SECRET_ACCESS_KEY="your-secret"The values above are placeholders: use your provider's. Test the connection by listing the bucket:
rclone lsd backup:
rclone ls backup:my-bucketIf the provider complains about permissions when creating the bucket and it already exists, add --s3-no-check-bucket to the upload commands: rclone then skips the bucket check (and the attempt to create it).
How do you upload a database dump to the bucket?#
Write a script that creates the dump, computes a hash, uploads both files and removes the temporary directory. The name carries the UTC date, which sorts the files and avoids overwriting yesterday's.
#!/usr/bin/env bash
set -euo pipefail
: "${DATABASE_URL:?set DATABASE_URL}"
: "${BUCKET:?set BUCKET}"
STAMP="$(date -u +%Y-%m-%dT%H%M%SZ)"
DIR="$(mktemp -d)"
FILE="app-$STAMP.dump"
trap 'rm -rf "$DIR"' EXIT
# -Fc: custom format, compressed and restorable with pg_restore
pg_dump "$DATABASE_URL" -Fc -f "$DIR/$FILE"
(cd "$DIR" && sha256sum "$FILE" > "$FILE.sha256")
rclone copyto "$DIR/$FILE" "backup:$BUCKET/db/$FILE"
rclone copyto "$DIR/$FILE.sha256" "backup:$BUCKET/db/$FILE.sha256"
echo "uploaded: db/$FILE"What matters here:
set -euo pipefailstops the script at the first error. Without it, apg_dumpthat failed halfway would be uploaded as if it were good.-Fcmakespg_dumpwrite a compressed archive thatpg_restorecan restore, even partially.rclone copytocopies one file to an exact destination path.- Run it with
DATABASE_URLandBUCKETset in the environment, never written into the script.
What about a folder of files?#
For user uploads or configuration, compress the folder and upload it as a single file:
tar -czf "$DIR/files-$STAMP.tar.gz" -C /path/to/folder .
rclone copyto "$DIR/files-$STAMP.tar.gz" "backup:$BUCKET/files/files-$STAMP.tar.gz"If the folder is large and changes little, rclone copy /path/to/folder backup:$BUCKET/files-current uploads only what changed. Avoid rclone sync for backups: it deletes from the destination whatever no longer exists at the source, so a mistake at the source would erase the copy too.
How do you schedule the upload?#
Use the system cron. To run every day at 3 AM, with the variables in a file only your user can read:
0 3 * * * set -a && . /etc/backup.env && /opt/scripts/backup-db.sh >> /var/log/backup-db.log 2>&1set -a exports every variable the file defines, so the script and rclone can see them. The log keeps the output so you can investigate failures. Because cron doesn't tell you when something breaks, add one more step: at the end of the script, call a URL on an external monitor (a "heartbeat" service) and set an alert for when the call stops arriving. Silence is the symptom of a backup that stopped.
How do you apply retention and delete old copies?#
Without retention, the bucket grows forever and so does the bill. You have two options:
- A lifecycle rule on the bucket, when the provider supports it: it deletes objects in a folder after N days, with no script. See object lifecycle rules.
- Cleanup in the script itself, which works on any service:
rclone delete "backup:$BUCKET/db" --min-age 30d--min-age 30d limits the command to objects older than 30 days. Run it with --dry-run first to see what would be deleted.
The lifecycle rule is preferable because it keeps working even if the source server is down. Keep copies from different periods (for example, daily for 7 days plus one monthly): if corrupted data lands in today's backup, you still need an earlier version. The full reasoning is in the 3-2-1 rule.
How do you restore and check that it worked?#
A backup that was never restored is only a hope. Test the way back on a test machine or database:
# find the most recent file
rclone lsf "backup:$BUCKET/db" | grep '\.dump$' | sort | tail -n 1
# download the dump and its hash, keeping the original name
N=app-2026-10-13T030000Z.dump
rclone copyto "backup:$BUCKET/db/$N" "./$N"
rclone copyto "backup:$BUCKET/db/$N.sha256" "./$N.sha256"
# check integrity
sha256sum -c "$N.sha256"
# restore into a new database
createdb restore_test
pg_restore -d restore_test --no-owner "$N"Keep the original name when downloading, because the .sha256 file records the dump's name. Then compare counts (SELECT COUNT(*) on the main tables) with the original database and write down how long the restore took. That is the real recovery time you would have in an emergency.
What are the common mistakes?#
- A partial dump uploaded as good:
set -e/pipefailis missing, or nobody checks the exit code. - Keys with too much power: if the backup key can also delete the whole bucket and it leaks, the attacker deletes the copies. Use a separate key and, if the service offers it, versioning or object lock.
- Unencrypted copies with personal data: encrypt with
gpgoragebeforerclone, and keep the key outside the bucket. rclone syncinstead ofcopy: it deletes from the destination along with the source.- Large files failing: check the provider's size limit and multipart upload support. See how to upload large files.
- Never testing the restore: the problem only shows up when you need it most.
How to do this on Square Cloud#
Square Cloud Blob Storage is object storage with an S3-compatible API, so the commands in this guide work once you swap the endpoint and keys: in rclone, set RCLONE_CONFIG_BACKUP_ENDPOINT="https://s3-blob.squarecloud.app", RCLONE_CONFIG_BACKUP_REGION=auto and the S3 keys created in the "S3 access" tab of the dashboard. The aws-cli and boto3 follow the same idea. Each account has three fixed buckets (public, private and legacy, which is read-only), and folders are key prefixes: send your dumps to private, under a folder such as db/.
There is no ingress or egress fee, and every plan includes a quota of 5 GB to 250 GB. For retention, the "delete after N days" folder rule replaces rclone delete --min-age: the count starts from each file's last write, and periods under 7 days are only available on Enterprise. Files up to 10 GB are accepted, multipart is only required above 100 MB, and durability is 99.999999999% with triple replication.
Square Cloud snapshots (manual on every plan; automatic daily for databases on every plan that includes them and for applications from the Pro plan) complement this flow. They cover quick recovery, including restoring an application snapshot into a new application in one click. The dumps you upload to Blob with rclone are a copy you control and can take anywhere.
See quotas and details on the Blob Storage page.
Blob Storage
S3-compatible object storage with no egress fees
FAQs
Why not keep the backup on the same server as the database?
Because the server and the copy share the same risks: a failing disk, a suspended account, an accidental deletion or ransomware. If both disappear together, you have no backup. A copy on another service removes that single point of failure.
Should I use rclone or the AWS CLI to upload?
Both speak the S3 API. rclone is more convenient for copying folders and filtering by age; the AWS CLI is the natural choice if you already use it. For an S3-compatible service, both only need the endpoint and the keys.
How many copies should I keep in the bucket?
It depends on how far back you may need to go. A common pattern is daily copies for 7 to 30 days and, if it makes sense, one per month for longer. Set the period and let an expiration rule delete the rest.
Do I need to encrypt the backup before uploading?
If the file contains personal data or secrets, encrypt it before the upload with gpg or age, so anyone with access to the bucket cannot read it. Store the decryption key separately from the bucket.
How do I know the backup in the bucket actually restores?
Only by restoring it. Download the latest file, restore it into a test database and compare row counts. Do this periodically and write down how long it took.
More in Storage
See all 15 articles- What is a presigned (signed) URL?A presigned URL is a temporary link to a private file that works without login or credentials. See how it works, its limits and when to use it.
- What is object storage?Object storage keeps files as objects with a key and metadata, accessed over an HTTP API. See how it works, what it is for and where its limits are.
- What does 'S3-compatible' storage mean?S3 is Amazon's object storage and its API became the industry standard. Learn what S3-compatible means, what differs between providers, and how to connect.
- Uploading large files: multipart and resumable uploadsMultipart upload splits a large file into parts that are sent in parallel and retried on their own. See the Python walkthrough, how to resume, and common mistakes.