How to use UpCloud Object Storage for server backups
UpCloud Managed Object Storage is S3-compatible, billed in 250 GB steps, with no egress fees, so any S3 tool can store backups there and restores cost no bandwidth. Create an instance in a region away from your servers, add a bucket, give each server a user whose custom policy reaches only that bucket, and point the AWS CLI at the instance's endpoint, https://<id>.upcloudobjects.com, with its region, such as europe-1. It supports versioning and lifecycle rules but not Object Lock yet, so keep a second copy elsewhere.
Pick a region
Each instance lives in one of five regions. Servers in the zones listed can also reach it over an SDN private network.
| Region | Physical location | Private network access from |
|---|---|---|
| APAC-1 | SG-SIN1 (Singapore) | AU-SYD1, SG-SIN1 |
| EUROPE-1 | FI-HEL2 (Helsinki) | All European zones |
| EUROPE-2 | DE-FRA1 (Frankfurt) | All European zones |
| EUROPE-3 | SE-STO1 (Stockholm) | All European zones |
| US-1 | US-CHI1 (Chicago) | US-CHI1, US-NYC1, US-SJO1 |
The European zones are DE-FRA1, DK-CPH1, ES-MAD1, FI-HEL1, FI-HEL2, NL-AMS1, NO-SVG1, PL-WAW1, SE-STO1 and UK-LON1. Config files and the API use the lowercase name, such as europe-1.
Keep backups in a different data centre from the servers they protect: a server in DE-FRA1 backing up to EUROPE-2 keeps both copies in one building. Servers outside UpCloud reach any region over its public endpoint.
Managed Object Storage replaced UpCloud's legacy Object Storage, scheduled for end of life on February 28, 2025. Tutorials written for the legacy service won't match these steps.
Create the instance and a bucket
- In the UpCloud Control Panel, open Object Storage and click Create new Object Storage.
- Choose the region. Enable Public access if servers outside UpCloud's private networks will connect, and attach an SDN private network if yours run in a zone listed for that region.
- Name the instance, click Create Object Storage, and wait for the status Ready.
- Open the instance's Buckets tab and click Create bucket. The name becomes a subdomain of the endpoint and must be unique only within this instance; use lowercase letters, numbers and hyphens.
Each instance has its own endpoint, shown in its details, such as https://abcd1.upcloudobjects.com; a private network adds https://abcd1-private.upcloudobjects.com, reachable only from that network. The examples use abcd1 and a bucket named acme-server-backups.
An instance can be deleted along with everything in it, so turn on termination protection, an API field. Create an API token in the Control Panel, put it in UPCLOUD_TOKEN, and list your instances (this needs jq):
curl -sS https://api.upcloud.com/1.3/object-storage-2 -H "Authorization: Bearer $UPCLOUD_TOKEN" | jq -r '.[] | [.uuid, .name, .region, (.endpoints[] | select(.type == "public" and .mode == "api") | .domain_name)] | @tsv'That prints each instance's UUID, name, region and public endpoint. Put the UUID in SERVICE, then:
curl -sS -X PATCH "https://api.upcloud.com/1.3/object-storage-2/$SERVICE" -H "Authorization: Bearer $UPCLOUD_TOKEN" -H "Content-Type: application/json" -d '{"termination_protection": true}'Termination protection guards the instance as a whole. Deleting a bucket through the UpCloud API, which the Terraform provider uses, removes every object in it, permanently; only the S3 API refuses to delete a bucket that isn't empty.
Create a key that reaches one bucket
Users, policies and keys belong to one instance. The built-in ECSS3FullAccess policy covers every bucket, and ECSS3ReadOnlyAccess reads every bucket. Give the first to an admin user for your workstation, never to a server:
- On the instance's Users tab, click Add user, name it, for example
admin, and click Add. - Add the
ECSS3FullAccesspolicy. - Click Access key and save both parts. The secret is shown once; UpCloud doesn't store it.
For a server, write a custom policy in the JSON format AWS uses:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBackupBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::acme-server-backups"
},
{
"Sid": "ReadWriteBackups",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:AbortMultipartUpload"
],
"Resource": "arn:aws:s3:::acme-server-backups/*"
}
]
}s3:ListBuckettakes the bucket ARN and letsaws s3 lsandaws s3 synclist the bucket. Object actions take the ARN ending in/*.s3:PutObjectuploads,s3:GetObjectdownloads for restores and checks, ands3:AbortMultipartUploadcleans up a failed upload.s3:DeleteObjectlets a backup tool prune old copies; withouts3:DeleteObjectVersion, a delete in a versioned bucket only adds a delete marker. Drop it if lifecycle rules do the pruning.
The API takes the policy URL-encoded inside a JSON body, up to 6,144 characters. tojson compacts the file and @uri encodes it:
jq '{name: "backups-acme-server-backups", description: "Read and write acme-server-backups only", document: (tojson | @uri)}' backup-policy.json > policy-request.jsonCreate the policy and the user, attach one to the other, and make the user's key:
curl -sS -X POST "https://api.upcloud.com/1.3/object-storage-2/$SERVICE/policies" -H "Authorization: Bearer $UPCLOUD_TOKEN" -H "Content-Type: application/json" -d @policy-request.jsoncurl -sS -X POST "https://api.upcloud.com/1.3/object-storage-2/$SERVICE/users" -H "Authorization: Bearer $UPCLOUD_TOKEN" -H "Content-Type: application/json" -d '{"username": "web-01-backup"}'curl -sS -X POST "https://api.upcloud.com/1.3/object-storage-2/$SERVICE/users/web-01-backup/policies" -H "Authorization: Bearer $UPCLOUD_TOKEN" -H "Content-Type: application/json" -d '{"name": "backups-acme-server-backups"}'curl -sS -X POST "https://api.upcloud.com/1.3/object-storage-2/$SERVICE/users/web-01-backup/access-keys" -H "Authorization: Bearer $UPCLOUD_TOKEN"The last response holds access_key_id and secret_access_key. The secret is returned only this once.
A user can have two active access keys. To rotate without downtime, create a second key, move the server to it, then mark the old key Inactive and delete it.
Configure the AWS CLI
[profile upcloud]
region = europe-1
endpoint_url = https://abcd1.upcloudobjects.com
request_checksum_calculation = WHEN_REQUIRED
response_checksum_validation = WHEN_REQUIRED
retry_mode = adaptive
max_attempts = 5
s3 =
multipart_threshold = 100MB
multipart_chunksize = 64MB
max_concurrent_requests = 20[upcloud]
aws_access_key_id = <access_key_id>
aws_secret_access_key = <secret_access_key>- The checksum lines add integrity checksums only where an operation requires one; UpCloud says AWS CLI 2.23.0 and later need them to avoid Content-MD5 errors. UpCloud's guide writes
response_checksum_calculation, but AWS readsresponse_checksum_validation. retry_mode = adaptiveretries with backoff, up to 5 attempts, and slows down when throttled.- Under
s3, files over 100 MB upload in 64 MB parts, 20 at a time, as UpCloud suggests for large objects.
Run chmod 600 ~/.aws/config ~/.aws/credentials, since the secret is stored in plain text. Then list the bucket:
aws s3 ls s3://acme-server-backups/ --profile upcloud --endpoint-url https://abcd1.upcloudobjects.com--endpoint-url overrides the profile's endpoint_url, so the commands below leave it off. No output means an empty bucket and a working key. Skip the bare aws s3 ls: listing every bucket isn't in the policy. Listing another bucket should fail with AccessDenied.
Turn on versioning and expire old backups
Bucket settings need the admin key, so run these on your workstation with a second profile, upcloud-admin, set up the same way. Versioning keeps overwritten and deleted backups as older versions:
aws s3api put-bucket-versioning --bucket acme-server-backups --versioning-configuration Status=Enabled --profile upcloud-adminaws s3api get-bucket-versioning --bucket acme-server-backups --profile upcloud-adminThe second should print "Status": "Enabled". Every version counts as stored data, so add a lifecycle rule. UpCloud warns that AWS CLI 2.23.0 and later can fail to set one with Missing required header for this request: Content-MD5, and suggests AWS CLI 2.22.32 or earlier, or s3cmd. For s3cmd, run s3cmd --configure -c ~/.s3cfg-upcloud and give the admin key, a blank region, abcd1.upcloudobjects.com as the endpoint, %(bucket)s.abcd1.upcloudobjects.com as the bucket template, and HTTPS. Then write the rule:
<?xml version="1.0" encoding="UTF-8"?>
<LifecycleConfiguration>
<Rule>
<ID>expire-backups</ID>
<Status>Enabled</Status>
<Prefix></Prefix>
<Expiration>
<Days>30</Days>
</Expiration>
<NoncurrentVersionExpiration>
<NoncurrentDays>7</NoncurrentDays>
</NoncurrentVersionExpiration>
<AbortIncompleteMultipartUpload>
<DaysAfterInitiation>7</DaysAfterInitiation>
</AbortIncompleteMultipartUpload>
</Rule>
</LifecycleConfiguration>- An empty
Prefixcovers the whole bucket;web-01/would cover one server. Expirationturns each backup into an older version 30 days after upload, andNoncurrentVersionExpirationremoves it for good 7 days later. A backup lives about 37 days, and an accidental delete can be undone for a week.AbortIncompleteMultipartUploadclears parts of uploads that never finished, which still take up space.
s3cmd -c ~/.s3cfg-upcloud setlifecycle lifecycle.xml s3://acme-server-backupss3cmd -c ~/.s3cfg-upcloud getlifecycle s3://acme-server-backupsCheck that all three actions come back. A new configuration replaces the old one, as on S3, and applies to backups already in the bucket.
Upload backups on a schedule
Give each backup a dated name so nothing overwrites it, and store a checksum beside it. This script archives two directories with tar and uploads both files:
#!/bin/sh
set -eu
NAME="web-01-$(date +%F).tar.gz"
cd /var/backups
tar -czf "$NAME" /etc /var/www
sha256sum "$NAME" > "$NAME.sha256"
for f in "$NAME" "$NAME.sha256"; do
aws s3 cp "$f" "s3://acme-server-backups/web-01/$f" --profile upcloud --only-show-errors
done
rm "$NAME" "$NAME.sha256"set -eu stops at the first failure, so a failed upload never reaches the rm, and --only-show-errors keeps cron mail down to real problems. Make it executable with chmod 700, then schedule it:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
15 4 * * * root /usr/local/bin/upcloud-backup.shThe PATH line matters: cron's default path often leaves out /usr/local/bin, where the AWS CLI v2 installer puts aws. See scheduling backups with cron.
UpCloud says load on its shared storage, and with it latency, rises around midnight to 2:00 AM in European time zones, when many backups run. Schedule outside that window or stagger servers.
Verify and restore a backup
aws s3 ls s3://acme-server-backups/web-01/ --recursive --human-readable --summarize --profile upcloudA listing proves the upload landed, not that it restores. Download one backup with its checksum file:
mkdir -p /tmp/restore-test && cd /tmp/restore-test && aws s3 cp s3://acme-server-backups/web-01/ . --recursive --exclude "*" --include "web-01-2026-10-03.tar.gz*" --profile upcloudsha256sum -c web-01-2026-10-03.tar.gz.sha256 && tar -tzf web-01-2026-10-03.tar.gz > /dev/null && echo OKLater filters win, so --exclude "*" then --include fetches just the two files. sha256sum -c prints web-01-2026-10-03.tar.gz: OK when the download matches the hash taken before upload, and tar -tzf reads the whole archive. Then extract into a scratch directory and check the files; see testing a restore.
To recover an overwritten or deleted backup, list its versions and fetch one by ID. The server key can't list versions, so use the admin profile:
aws s3api list-object-versions --bucket acme-server-backups --prefix web-01/ --profile upcloud-adminaws s3api get-object --bucket acme-server-backups --key web-01/web-01-2026-10-03.tar.gz --version-id <version-id> web-01-2026-10-03.tar.gz --profile upcloud-adminNo Object Lock yet: protect backups another way
As of October 2026, UpCloud lists Object Lock as coming soon and the object retention and legal hold APIs as unsupported. Nothing on the storage side stops a user with full access, or anyone controlling your UpCloud account, from deleting backups. So:
- Keep versioning on and give servers only a policy like the one above, so their keys can't delete versions or change bucket settings.
- Keep the admin key and API tokens off servers, and turn on termination protection.
- Keep a second copy with another provider, per the 3-2-1 rule. See protecting backups from ransomware.
Data is encrypted at rest with AES-256 under keys UpCloud manages. The bucket encryption API calls aren't supported, so for keys only you hold, encrypt backups before upload.
What it costs
Managed Object Storage is billed on stored data in 250 GB steps, each billed by the hour. A new instance starts at 250 GB and grows another 250 GB whenever usage crosses a step, with no upper limit. As of October 2026, UpCloud's cost calculator lists each 250 GB at €5.00 a month. Egress is zero-cost, and the billing page lists no request charges.
| Stored (backups and old versions) | Billed as | Per month |
|---|---|---|
| 100 GB | 250 GB | €5.00 |
| 600 GB | 750 GB | €15.00 |
| 2,000 GB | 2,000 GB | €40.00 |
The minimum applies per instance, so use several buckets in one instance rather than an instance per server. For very heavy public traffic, UpCloud's Fair Transfer Policy applies: past the account's limit it sends a warning and may throttle to 100 Mbit/s. Private endpoint traffic doesn't count.
Limits and common errors
- Objects up to 5 TB with multipart upload; parts up to 5 GiB.
- Not supported: Object Lock, retention and legal hold, bucket replication, the Block Public Access and bucket encryption calls, and
RestoreObject.
Missing required header for this request: Content-MD5: AWS CLI 2.23.0 or later. Add the two checksum lines; for lifecycle rules, use s3cmd or AWS CLI 2.22.32 or earlier.- Checksum mismatch errors from other tools on newer AWS SDKs: set
AWS_REQUEST_CHECKSUM_CALCULATION=when_requiredandAWS_RESPONSE_CHECKSUM_VALIDATION=when_required, as UpCloud advises. Could not connect to the endpoint URL: the endpoint is wrong, public access is off, or a-privateendpoint is used from outside its network.InvalidAccessKeyId: the key is wrong, or the endpoint belongs to a different instance from the one the user was created in.
Frequently asked questions
- What is the S3 endpoint for UpCloud Object Storage?
- Each instance has its own,
https://<id>.upcloudobjects.com, shown in its details; a private network addshttps://<id>-private.upcloudobjects.com. - What region do I use for UpCloud Object Storage in the AWS CLI?
- The instance's region in lowercase, such as
europe-1,us-1orapac-1, together with the instance's endpoint. - Does UpCloud Object Storage support Object Lock?
- Not as of October 2026. UpCloud lists it as coming soon, and the retention and legal hold APIs as unsupported. Versioning and lifecycle rules work.
- How much does UpCloud Object Storage cost?
- As of October 2026, UpCloud's cost calculator lists €5.00 a month per 250 GB, billed hourly, with a 250 GB minimum per instance and no egress fees.
- Can I limit an UpCloud Object Storage key to one bucket?
- Yes. Create a custom policy whose resources are that bucket's ARNs, through the UpCloud API or Terraform, and attach it to a user of its own.
How this was checked
Commands, limits and prices were checked against these official pages, on October 3, 2026:
- UpCloud docs: Managed Object Storage
- UpCloud docs: Object Storage availability
- UpCloud docs: Object Storage billing
- UpCloud docs: Scalable bucket size
- UpCloud docs: Full encryption
- UpCloud docs: S3 standard compatible (supported and unsupported APIs)
- UpCloud guide: How to get started with Managed Object Storage
- UpCloud guide: Connecting to an Object Storage instance using AWS CLI
- UpCloud guide: Connecting to an Object Storage instance using S3cmd
- UpCloud guide: How to perform basic Object Storage operations using AWS CLI
- UpCloud guide: Best practices for Managed Object Storage
- UpCloud guide: How to configure lifecycle policies
- UpCloud guide: How to enable and manage S3 object versioning
- UpCloud guide: How to create a user for an Object Storage instance using the API
- UpCloud guide: How to generate access keys for a user using the API
- UpCloud guide: How to apply user access policies using the API
- UpCloud guide: How to deploy Managed Object Storage using Terraform
- UpCloud guide: Managed Object Storage as an S3 state backend (checksum settings)
- UpCloud API: Managed Object Storage (services, buckets, users, policies, access keys)
- UpCloud API: Architecture overview (authentication)
- UpCloud Terraform provider: upcloud_managed_object_storage_policy
- UpCloud docs: Network Transfer (Fair Transfer Limit)
- UpCloud changelog: Managed Object Storage now available in all regions (legacy service end of life)
- UpCloud Cost Calculator: Object Storage
- AWS SDKs and Tools Reference Guide: Data integrity protections for Amazon S3
- AWS CLI reference: S3 configuration
- AWS CLI reference: s3 commands (filters)
- AWS CLI reference: s3 cp
- AWS CLI reference: s3api list-object-versions