Skip to main content

Backup storage

Backup storage is the managed way to give an instance an off-site target. Instead of typing a storage account or a bucket and its credential into every instance's Backup tab, you connect the account once under Backup storage > Accounts, and the console makes a container or bucket per instance, keeps it immutable, hands the instance a credential scoped to it where the provider can scope one, measures what it holds every night and estimates what it costs.

One feature per provider: backup-storage-azure, backup-storage-hetzner, backup-storage-b2, backup-storage-r2 and backup-storage-s3, all in the Professional edition. The backups themselves still need backup-azure or backup-s3, which is what sends them.

The five providers​

ProviderWhat an account isWhat the instance receivesImmutability
Azure Blob StorageOne storage account, reached through a service principal with the Storage Account Contributor role. The account key is never stored.A token scoped to its container, bound to a stored access policy so it can be revoked, valid a year and re-minted thirty days before it endsA container-level retention in days, unlocked (can be shortened) or locked (cannot, by anyone)
Hetzner Object StorageOne project and its key pair, made in the Hetzner Console, at one location (Falkenstein, Nuremberg or Helsinki)The account's own key by default, which reaches every bucket of the project; or a dedicated key you made in the Hetzner Console, which the console confines to the bucket by policyObject Lock, chosen when the bucket is made and never afterwards, in governance or compliance mode
Backblaze B2One account and an application key with access to all buckets and the Write Keys capability, plus bypassGovernance if a Governance bucket is ever to be deleted before its retention has passedA key confined to the bucket, minted by the consoleObject Lock, chosen when the bucket is made, in governance or compliance mode
Cloudflare R2One Cloudflare account, its account ID and an account API token, optionally tied to one jurisdiction and one location hint (see Cloudflare R2)A token confined to the bucket, minted by the console and deleted again on unbindA bucket lock rule that keeps the archives for a number of days, and that can be lifted on Delete
S3-compatibleAny endpoint with a key pair: Amazon S3, Scaleway and MinIO are presets that fill the endpoint and the rate cardThe account's own keyObject Lock where the endpoint honours it, chosen when the bucket is made

Hetzner allows 100 buckets and 200 keys per customer across every project, so one bucket per instance stops at 100 instances per Hetzner customer. The account's check says so when it gets close.

One container, one instance​

A container is bound to one instance and never to another. An instance, on the other hand, may hold one container at every account it has - one at Azure and one at Hetzner is the point of having several accounts - and the instance's Managed storage card lists them all. The console enforces the rule three ways:

  • the database refuses a second binding of the same container, and a second container of the same account on one instance;
  • the container carries a marker at the provider naming the instance it is for, and a container whose marker names another instance is refused;
  • an adopted container that carries no marker can only be bound while it is empty, since what it holds could be anybody's.

When an instance is deleted, its containers are retired: the data and the credential are kept, nothing is bound to them again, and the weekly report names what they cost until you delete them. To release a container from an instance that still runs, Unbind it on the Containers page: the target leaves the instance's Backup tab, the credential is revoked at the provider, and the container is retired with whatever it holds, ready to be deleted. The one instance that may take an unbound container back is the instance it holds data of; Bind it again and the marker decides. Deleting a container opens a confirmation under its row: it says what goes, and the delete waits until you type the container's name. It empties the container first - the console's own marker, the archives, every version the retention lets go - and then deletes it at the provider.

What a retention still holds stays, unless the retention can be lifted and you ask for that:

  • Governance (Hetzner, Backblaze, Cloudflare R2, a generic S3 endpoint) and an unlocked Azure policy can be lifted. The confirmation then offers Also remove the backups still under retention; ticked, the delete lifts the retention and removes everything. Lifting needs the account's key to have the right to: the bypassGovernance capability at Backblaze (a key cannot be changed, so make a new one with it), the s3:BypassGovernanceRetention permission at a generic endpoint, the Workers R2 Storage Write permission on the account's token at Cloudflare R2, and the Storage Account Contributor role at Azure.
  • Compliance and a locked Azure policy cannot be lifted by anybody.

When something is still held, the delete stops, says how much, which retention holds it, and - for one that can be lifted - what the key needs; the container stays, emptied as far as it could be.

Where two accounts of the S3 family (Hetzner, Backblaze, Cloudflare R2, a generic endpoint) name their bucket after the same instance, the second target on the instance's Backup tab carries the account's short code, since the targets of that family share one list on the instance and are told apart by name.

Cloudflare R2​

An R2 account is one Cloudflare account. Its fields:

  • Account ID: the 32 characters the Cloudflare dashboard shows on the account's home page and on the R2 object storage page.

  • Account API token: under Manage account > Account API tokens, create a custom token on this account with two permissions:

    • Workers R2 Storage Write, to make, lock, list and delete buckets and to read and write their objects. Lifting a retention on Delete needs it too.
    • Account API Tokens Write, so the console can mint a token for each bucket and delete it again on unbind.

    Make an account API token, not a user token, and never use the Global API Key. Cloudflare shows the token once, when it is made.

  • Jurisdiction: leave it empty, or type eu (European Union), us (United States) or fedramp (FedRAMP, for Enterprise accounts only). Every bucket of the account is then made in that jurisdiction and reached at its own endpoint. A bucket's jurisdiction cannot be changed; to use two jurisdictions, add the Cloudflare account twice, once for each.

  • Location hint: leave it empty to let Cloudflare place the buckets, or type wnam or enam (Western or Eastern North America), weur or eeur (Western or Eastern Europe), apac (Asia-Pacific) or oc (Oceania). It is a hint, not a guarantee: only a jurisdiction gives one.

The account's check makes sure the token is active, can see the account's buckets, and can see the token permissions it needs to mint a bucket's token. Whether it may actually make buckets and tokens shows on the first bucket.

What the instance receives. For each bucket the console makes an account API token that can read, write and list objects in that one bucket and nowhere else. The instance's backup target is written with it, at the account's endpoint and the region auto. Unbinding or deleting the bucket deletes the token at Cloudflare.

What retention means on R2. R2 has no versions and no Object Lock. The console's retention is a bucket lock rule: the backup archives are kept from being deleted or overwritten for the number of days the bucket was given, counted from when each archive was written. It is the Object Lock, governance choice in the console's immutability lists, and reads governance on the Containers page. The rule covers the backup archives only, because a lock on R2 also refuses to overwrite an object, and the small files the console and the backups rewrite have to stay writable. Unlike Object Lock, a rule can be added to a bucket later and removed again by anybody whose token may edit the bucket's configuration. On Delete, Also remove the backups still under retention removes every lock rule of the bucket first and then deletes everything. Without it, what a rule still holds stays and the delete refuses, saying how much.

Retention on R2 also holds the backup rotation: a copy whose slot comes round again before the retention has passed cannot be replaced. So binding a bucket writes the instance's target with one daily policy that keeps more copies than the retention covers: seven, or one more than the retention's days (a 30-day retention keeps 31). The policy's copies are changed on the Containers page, not on the Backup tab, which shows them read-only. If you later lower Copies kept there, or lengthen the bucket's retention at Cloudflare, a backup that comes round to a copy the lock still holds fails and says so, and the copy it would have replaced stays as it was. Keep the copies times the interval longer than the retention.

What it costs. The rate card starts at Cloudflare's list price for Standard storage, USD 0.015 per GB-month, with the first 10 GB a month free and no egress charge. A bucket's size is measured by listing it, since Cloudflare reports usage per account only.

Setting it up​

Before the first account: the add button above the list opens the account form.
  1. On Backup storage > Accounts, add an account. The form is rendered from the provider's credential fields, and the account is checked the moment it is saved. Set the immutability and retention a new container takes, the naming pattern (ohm-{id12} unless you say otherwise) and the currency the estimate is in. For Azure, the Script generator tab writes the Cloud Shell script that makes the storage account and the service principal and prints a document; the Import tab reads that document into the form. The download button on an account's row gives the same script again, to repair or change the storage account. The script makes its application under the name you give it, or reuses the one of that name; no other application or identity with access to the storage account is touched. Running it again is safe: a new secret is added beside the application's earlier ones. The storage account it makes uses Azure's double encryption, which Azure lets you choose only when an account is created. To have the archives themselves locked with your own key, whatever the provider, see Backup encryption.
  2. On an instance's Backup tab, under Managed storage, pick the account and press Make and bind. The container is made at the provider with the account's defaults, the credential is minted, and the instance's backup target is written in the same run, flagged as managed: it shows Managed by: Backup storage, and its account, container and credential are read-only there from then on. Its Enabled switch and backup policies are changed on Backup storage > Containers: see The backup policies of a container.
  3. Alternatively, on Backup storage > Containers, make a container without an instance and bind it later, or adopt one that already exists at the provider.

A container whose setup at the provider was interrupted after it was created - Hetzner can take a while to show a new bucket - is listed as Unfinished with the reason. The console retries it by itself for about ten minutes, and Finish on its row completes it at any time, binding it to the instance it was made for.

An S3-compatible account's service URL must be https, and it may not point at this server or at a private address: the server signs every request with the account's key, and a URL like that could send the key somewhere it was never meant to go. A self-hosted store on your own network, such as MinIO on a private address, is allowed once you type yes under Endpoint on the private network on the account; leave the field empty for everything else.

The backup policy on the target decides how many copies are kept and how often; the container's retention decides how long each copy is kept from deletion. Give the policy enough copies and interval to cover the retention, or the rotation will wait on it. For example, a container retained for 14 days with a daily policy needs at least 15 copies.

The backup policies of a container​

A bound container's backup policies and its target's Enabled switch are changed on Backup storage > Containers, with the calendar button on its row; the instance's Backup tab shows them read-only, and the Backup storage link on its Managed by line opens this editor for its container. The button opens Backup policies of the container, with:

  • the target's Enabled switch: off, the instance's backups are not written to the container;
  • its backup policies, in the same editor the Backup tab uses for a target you typed yourself. Each policy is one backup set in the container, with its name, its interval in seconds, the Copies kept and its own Enabled switch. Add policy, Copy policy and Remove policy work as they do there, and a target keeps at least one policy.

Save writes them to the instance's backup configuration with a job, and the next backup follows them. The connection - the account, the container, the credential and the prefix - stays as the binding wrote it, and how long the container keeps what is written is its own retention, set when it was made, so the policies carry no immutability of their own here.

A binding starts the target with one daily policy. At Azure and Cloudflare R2 it keeps seven copies, or one more than the container's retention in days when that is longer; a bucket at Hetzner, Backblaze or a generic S3 endpoint starts with an S3 target's default policy.

Removing a policy there asks, when you save, what happens to the recovery points it wrote: keep them, or delete them. See Removing a policy or a target. The container's own retention is never lifted for one policy: the copies it still holds stay until their date.

Under the policies, Orphaned backup sets lists what the container holds that no policy writes any more: a policy removed with its data kept, one removed before the console asked, or copies a retention kept at the time. Each has a delete button that opens a confirmation; an Unlocked or Governance retention on the copies themselves adds a second one that lifts it. The deletion is a job, an administrator's, and its result says what was deleted and what retention still keeps.

The Overview​

Backup storage > Overview shows what the managed containers hold and cost: the totals, a breakdown per provider, per account and per instance, a thirty-day trend and the list of what wants attention: retired and unbound containers, containers that could not be measured, and credentials that expire within sixty days.

The figures come from the nightly Backup storage measurement on the Maintenance page, which lists every container at its provider and keeps a sample per container for the trend. The cost is an estimate at list price: no provider bills per container. Azure's published price for the account's region and tier is read on every measurement; Hetzner's is the account's base price with its included terabyte and the rest per terabyte, shared out by what each bucket holds; Backblaze, Cloudflare R2 and the S3 presets start from their published rates. Every rate card can be changed on the account. A new Azure account starts in EUR, the currency its price is read in until you choose another on the account; every other provider's account starts in the currency its published rates are in. Every estimate is shown in the account's own currency, as its rate card gives it: nothing is converted.

The weekly report​

The first measurement of each week raises the Backup storage report alert event: the totals, the estimated cost per currency, and the containers that cost money for nobody. A rule on it under Alerting > Rules delivers it to e-mail, Slack, Discord or a webhook.

When an account's periodic check fails, or passes again, the Backup storage account check event says so in the console's bell. It is shown there only; rules never match it.

On a server managed by an Odoo Management Plane​

An Odoo Management Plane that manages this server can bind a container to an instance too. Such a target shows on the instance's Backup tab with Managed by: Odoo Management Plane, greyed out like one this section made; it is changed and removed on the plane, not here. The targets this server's own Backup storage section bound stay managed here.

Restores and copies​

A managed target stays with the instance it was bound to. A restore keeps it only on that instance, where the binding lives; a copy, a duplicate or an import of another server's archive leaves it out and the job's result names it, so the copy never backs up into the source's container. Bind the copy a container of its own. See Restoring.

A target an older restore brought along from elsewhere is managed by nothing on this server: no container here carries it and no Odoo Management Plane is bound to the server. Its Backup tab shows Managed by: Nobody, and Remove provider removes it like a target you typed yourself.

On the command line​

The managed storage is a console feature; the API carries it under /api/v1/backup-storage. The targets it writes appear in ohm instance backup-config show like any other, with a managed container id on them.