VPS Snaps

How to use OVHcloud Object Storage for server backups

OVHcloud Object Storage is S3-compatible storage inside a Public Cloud project, at https://s3.<region>.io.cloud.ovh.net, such as https://s3.gra.io.cloud.ovh.net for Gravelines. As of October 2026, the Standard class in a 1-AZ region costs €0.00000972 per GiB an hour, about €7.10 per 1,000 GiB a month before VAT, and traffic and API requests are free. The user that creates a bucket keeps full control of it through its ACL, so manage buckets with an admin user and give each server its own S3 user, with a policy that lets it upload and read but not delete.

11 min readUpdated Checked against official documentation

Storage classes and deployment modes

OVHcloud sells five S3 classes, all through one endpoint per region. S3 tools pick a class by its AWS name:

ClassS3 nameWhereMinimum duration, retrieval
High Performance (NVMe)EXPRESS_ONEZONE1-AZ regionsNone
StandardSTANDARD, the default1-AZ and 3-AZNone
Infrequent AccessSTANDARD_IA1-AZ and 3-AZ30 days, fee per GiB read
Active ArchiveGLACIER_IR3-AZ regions90 days, fee per GiB read
Cold ArchiveDEEP_ARCHIVEParis only180 days; restored before download, in hours

A 1-AZ region spreads data over servers in one availability zone with 2N+1 redundancy, but OVHcloud says the service sits in one data center, and an outage there can make data unavailable or lose it. A 3-AZ region (Paris and Milan) keeps data in three independent zones and costs about twice as much. Local Zones offer Standard only. OVHcloud lists 1-AZ for backups; Standard suits nightly backups, and High Performance is for analytics, not backups. The legacy Swift offers and the 2023 standalone Cold Archive aren't covered here.

Regions and endpoints

Every region's endpoint is https://s3.<region>.io.cloud.ovh.net, with the code in lowercase:

LocationCodeMode
Gravelines, Francegra1-AZ
Roubaix, Francerbx1-AZ
Strasbourg, Francesbg1-AZ
Paris, Franceeu-west-par3-AZ
Milan, Italyeu-south-mil3-AZ
Frankfurt, Germanyde1-AZ
London, UKuk1-AZ
Warsaw, Polandwaw1-AZ
Beauharnois, Canadabhs1-AZ
Toronto, Canadaca-east-tor1-AZ
Singaporesgp1-AZ
Sydney, Australiaap-southeast-syd1-AZ
Mumbai, Indiaap-south-mum1-AZ
  • A bucket's endpoint is also on its General information tab under My containers.
  • https://s3.<region>.perf.cloud.ovh.net is a legacy endpoint kept for old tools; it maps classes differently and doesn't support lifecycle rules. Use io.
  • s3.<region>.cloud.ovh.net, without io, is for the legacy Swift offer.
  • rclone also lists two US regions, us-east-va and us-west-or, with endpoints ending in .io.cloud.ovh.us; their prices aren't covered here.

The examples use Gravelines and a bucket named acme-web-01-backups. Pick a region away from the servers it protects.

Create the bucket

  1. In the OVHcloud Control Panel, open Public Cloud, select the project, click Object Storage, then Create Object Container.
  2. Enter a name: 3 to 63 lowercase letters, digits, dots and hyphens, starting and ending with a letter or digit, unique within OVHcloud.
  3. Select the offer, the deployment mode and the region.
  4. Turn on versioning, and Object Lock if you may want locked backups. Object Lock can't be enabled later.
  5. Link a user: create one named backup-admin to manage the bucket. Its keys stay on your workstation.
  6. Choose whether to encrypt with SSE-OMK, OVHcloud-managed keys, and click Create.

Give each server its own S3 user

S3 users belong to the project. Under Object Storage, click Create User and create web-01; its access key and secret key are shown, and the ... menu shows the secret again later. Don't add it to the bucket with a profile; the policy below grants what it needs.

OVHcloud doesn't support bucket policies yet, and a bucket's owner, the user that created it, has full control through its ACL: OVHcloud says an owner is authorized even where its policy has no allow. A policy reliably limits only a user that doesn't own the bucket, so never give a server backup-admin's keys.

Limit web-01 with a user policy. OVHcloud checks an explicit deny first, then an explicit allow, then falls back to ACLs:

web-01-policy.json
{
  "Statement": [
    {
      "Sid": "UploadAndRead",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:GetBucketLocation",
        "s3:ListBucketMultipartUploads",
        "s3:ListMultipartUploadParts",
        "s3:PutObject",
        "s3:AbortMultipartUpload",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::acme-web-01-backups",
        "arn:aws:s3:::acme-web-01-backups/*"
      ]
    },
    {
      "Sid": "NoDeletesOrLocks",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteBucket",
        "s3:PutLifecycleConfiguration",
        "s3:PutBucketVersioning",
        "s3:PutBucketObjectLockConfiguration",
        "s3:PutObjectRetention",
        "s3:BypassGovernanceRetention",
        "s3:ListAllMyBuckets"
      ],
      "Resource": ["*"]
    }
  ]
}
  • The first statement allows listing, uploading (multipart included) and downloading in this bucket only.
  • The second refuses deletes, rule and lock changes everywhere, and hides other buckets' names: OVHcloud allows s3:ListAllMyBuckets by default.

On the Object Storage Policy Users tab, open the ... menu on web-01's line and choose Import JSON file. OVHcloud suggests downloading a user's current JSON first if you're changing it.

Configure the AWS CLI

~/.aws/config
[profile ovh]
region = gra
endpoint_url = https://s3.gra.io.cloud.ovh.net
retry_mode = standard
max_attempts = 5
~/.aws/credentials
[ovh]
aws_access_key_id = <access_key>
aws_secret_access_key = <secret_key>
  • region is the lowercase code and must match the endpoint. OVHcloud's guide sets endpoints in services sections; endpoint_url in the profile does the same.
  • retry_mode = standard retries throttling errors, timeouts and HTTP 500, 502, 503 and 504 with backoff; max_attempts = 5 allows five attempts instead of three.

Run chmod 600 on both files. On the server, upload a test file with aws s3 cp /etc/hostname s3://acme-web-01-backups/web-01/test.txt --profile ovh; aws s3 rm on it should be refused with HTTP 403. If the delete works, the server has the owner's keys. On your workstation, add an ovh-admin profile the same way with backup-admin's keys; if its calls are refused, import OVHcloud's full-access example policy, s3:* on *, for that user.

Upload backups on a schedule

The script from the Hetzner Object Storage guide works with two changes: the bucket path gets the server's prefix, and the profile is ovh. Make it executable with chmod 700 and schedule it:

/usr/local/bin/ovh-backup.sh
#!/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-web-01-backups/web-01/$f" --profile ovh --only-show-errors
done
rm "$NAME" "$NAME.sha256"
/etc/cron.d/ovh-backup
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
30 3 * * * root /usr/local/bin/ovh-backup.sh

Uploads go to Standard; add --storage-class STANDARD_IA for Infrequent Access. OVHcloud allows 1 Gbps per connection, and the AWS CLI's default of 10 requests at once gets past that. For rclone, version 1.71 and later have an OVHcloud provider; on older versions, such as Ubuntu 24.04's 1.60, use provider = Other, as OVHcloud's guide does:

/root/.config/rclone/rclone.conf
[ovh]
type = s3
provider = OVHcloud
access_key_id = <access_key>
secret_access_key = <secret_key>
region = gra
endpoint = s3.gra.io.cloud.ovh.net
no_check_bucket = true

no_check_bucket stops rclone trying to create the bucket. With no acl line, rclone sends no ACL header and objects stay private. rclone's region list lacks Milan; type eu-south-mil and its endpoint. Use rclone copy, not rclone sync, which deletes and fails with this user.

Expire old backups with lifecycle rules

OVHcloud supports S3 lifecycle rules in its documented JSON format. This keeps each nightly backup 30 days plus 7 as an older version, and cleans up after failed uploads:

lifecycle.json
{
  "Rules": [
    {
      "ID": "expire-web-01",
      "Status": "Enabled",
      "Filter": { "Prefix": "web-01/" },
      "Expiration": { "Days": 30 },
      "NoncurrentVersionExpiration": { "NoncurrentDays": 7 }
    },
    {
      "ID": "remove-delete-markers",
      "Status": "Enabled",
      "Filter": {},
      "Expiration": { "ExpiredObjectDeleteMarker": true }
    },
    {
      "ID": "abort-uploads",
      "Status": "Enabled",
      "Filter": {},
      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 1 }
    }
  ]
}
Terminal
aws s3api put-bucket-lifecycle-configuration --bucket acme-web-01-backups --lifecycle-configuration file://lifecycle.json --profile ovh-admin
  • In a versioned bucket, Expiration adds a delete marker; NoncurrentVersionExpiration deletes the data 7 days after it stops being current.
  • ExpiredObjectDeleteMarker must sit in a rule of its own, and an empty Filter covers the whole bucket.
  • Unfinished multipart uploads are billed for the parts already sent, which the last rule removes.
  • Rules run on a best-effort basis, mostly within 24 hours, and storage is billed until they do. Versioning can't be suspended while a lifecycle configuration is in effect.

aws s3api head-object on a backup then shows an Expiration field with the expiry date and rule ID.

Versioning and Object Lock

Versioning keeps overwritten and deleted backups as older versions; once on, it can only be suspended. aws s3api get-bucket-versioning --bucket acme-web-01-backups --profile ovh-admin should show "Status": "Enabled". Object Lock needs versioning and stops deletion for a set time. On a bucket created with it, set a default retention, or use Configure Retention on the bucket's General information tab:

Terminal
aws s3api put-object-lock-configuration --bucket acme-web-01-backups --object-lock-configuration '{"ObjectLockEnabled": "Enabled", "Rule": {"DefaultRetention": {"Mode": "GOVERNANCE", "Days": 14}}}' --profile ovh-admin
  • GOVERNANCE: only users with s3:BypassGovernanceRetention, which the policy above denies web-01, can delete a locked version, adding --bypass-governance-retention.
  • COMPLIANCE: nobody, administrators included, can delete a version or change its retention until it ends.

Upload a test file; aws s3api get-object-retention --bucket acme-web-01-backups --key web-01/test.txt --profile ovh-admin should show the mode and a RetainUntilDate 14 days out. Keep the lock shorter than the 37-day lifecycle window, and move to COMPLIANCE only once the numbers are right: a locked version is stored, and billed, until its date. Object Lock isn't available for the Cold Archive class.

Verify and restore

Terminal
aws s3 ls s3://acme-web-01-backups/web-01/ --human-readable --summarize --profile ovh
Terminal
mkdir -p /tmp/restore-test && cd /tmp/restore-test && aws s3 cp s3://acme-web-01-backups/web-01/ . --recursive --exclude "*" --include "web-01-2026-10-04.tar.gz*" --profile ovh
Terminal
sha256sum -c web-01-2026-10-04.tar.gz.sha256 && tar -tzf web-01-2026-10-04.tar.gz > /dev/null && echo OK

The checksum proves the download matches what left the server, and tar -tzf reads the whole archive. Then extract and check the files, as in testing a restore. For an overwritten or expired backup, aws s3api list-object-versions --prefix web-01/ lists its versions, and aws s3api get-object with --version-id fetches one.

What it costs

As of October 2026, excluding VAT, billed per GiB-hour with 730 hours to an average month. Euro prices are from OVHcloud France, dollars from its international site:

ClassPer GiB-hourAbout per GiB a monthRetrieval
Standard, 1-AZ€0.00000972 ($0.00001111)€0.0071 ($0.0081)Free
Infrequent Access, 1-AZ€0.00000548 ($0.00000597)€0.0040 ($0.0044)€0.004 per GiB
High Performance, 1-AZ€0.000025 ($0.00002778)€0.0183 ($0.0203)Free
Standard, 3-AZ, first 50 TiB€0.00001917 ($0.00002152)€0.0140 ($0.0157)Free
Active Archive, 3-AZ€0.00000617 ($0.00000729)€0.0045 ($0.0053)€0.018 per GiB
Cold Archive, Paris€0.00000228 ($0.0000027)€0.0017 ($0.0020)€0.009 per GiB

Traffic in and out, internal or to the internet, and API requests are free. An object deleted before a class's minimum duration is billed for the hours left: 730 for Infrequent Access, 2,190 for Active Archive, 4,380 for Cold Archive.

UsageWorkingPer month
30 nightly 20 GiB archives, Standard, Gravelines600 GiB × €0.00000972 × 730€4.26
The same in Paris, 3-AZ600 GiB × €0.00001917 × 730€8.40
12 monthly 50 GiB archives, Infrequent Access, 1-AZ600 GiB × €0.00000548 × 730€2.40
Restoring one monthly archive to a server anywhere50 GiB × €0.004; traffic free€0.20

Infrequent Access pays off only for backups kept longer than about 17 days: €0.00000548 × 730 hours equals Standard's price for 412 hours.

Limits and common errors

  • Objects up to 48 TiB with multipart upload, in up to 10,000 parts of 5 MiB to 5 GiB; a single PUT takes up to 5 GiB.
  • Per project: 100 buckets by default (up to 1,000 on request) and 1,000 users. Up to 1,000 lifecycle rules. Per bucket, soft limits of 300 PUT and 900 GET requests a second by default.
  • No bucket policies, and no access over the private network (vRack): endpoints are public.
  • HTTP 403 on every request from a server's user: its policy wasn't imported, or doesn't name the bucket. Without an explicit allow, a user that doesn't own the bucket falls back to ACLs and is refused.
  • A policy that seems ignored: the keys belong to the bucket's owner, which always has full control.
  • Access Denied (HTTP 403) deleting a version: Object Lock is doing its job.
  • A lifecycle configuration refused: versioning is suspended, or a transition rule is under 30 days.
  • MaxVersionsReached (HTTP 403): one object key has 100,000 versions. Name backups by date.
  • Checksum errors from a recent AWS CLI: newer versions add checksums by default, which not every S3-compatible service accepts. Add request_checksum_calculation = when_required and response_checksum_validation = when_required to the profile.

An OVHcloud bucket for an OVHcloud server

OVHcloud keeps a VPS's automated backups in the server's own data center, as the OVHcloud VPS backup guide explains. A bucket in another region survives losing that site, and its files restore anywhere. For a 3-AZ bucket, the Offsite Replication option copies data to a remote site.

It doesn't cover the account: server, bucket and replica share one login and one bill. Keep another copy with a different provider, per the 3-2-1 rule, and encrypt backups before upload if only you should read them. The reverse works too: with free traffic, OVHcloud is an inexpensive off-site target for servers elsewhere.

Frequently asked questions

What is the S3 endpoint for OVHcloud Object Storage?
https://s3.<region>.io.cloud.ovh.net with the lowercase region code, such as https://s3.gra.io.cloud.ovh.net for Gravelines or https://s3.eu-west-par.io.cloud.ovh.net for Paris. Set the client's region to the same code.
Can I limit an OVHcloud S3 user to one bucket?
Yes, with a user policy that names the bucket's ARN, imported on the Object Storage Policy Users tab. Bucket policies aren't available, and the bucket's owner always keeps full control, so give servers users that don't own the bucket.
Does OVHcloud charge for Object Storage egress?
No. As of October 2026, incoming and outgoing traffic and API requests are free. Infrequent Access, Active Archive and Cold Archive charge per GiB retrieved.
Does OVHcloud Object Storage support Object Lock?
Yes, with governance and compliance retention and legal hold, but only on buckets created with Object Lock, which also turns on versioning. It isn't available for the Cold Archive class.
What is the difference between the io and perf endpoints?
s3.<region>.io.cloud.ovh.net is the current endpoint, defaulting to Standard. s3.<region>.perf.cloud.ovh.net is kept for older tools, defaults to High Performance and doesn't support lifecycle rules.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: