Backups
What is copied, where it goes, how long it is kept, and what you have to do to get one back.
Production environments are backed up without you doing anything. Two things are copied: the database, and the files your application has written — uploads, generated exports, anything under the directories that survive a release.
Your code is not backed up, because it is in git and in a build artifact already. Restoring a repository from us would be restoring it from a worse copy.
Where a copy goes
| Copy | Where |
|---|---|
| Near | object storage (Cloudflare R2) on the same continent as your machines |
| Cold | a different provider, in the EU: Scaleway, in Paris |
| On the machine | the database only, for the last two days |
Two offsite copies, on different providers. A backup that shares a fate with the thing it protects is not a backup — and one that shares a provider with it is only slightly better.
The near copy follows your machines. The cold copy recently moved from Hetzner in Helsinki to Scaleway in Paris; until the new one holds enough history, both are written, so nothing already kept is lost in the move.
The copy on the machine is the one that survives a dropped table or a bad deploy, which are the restores people actually ask for; it is the database only, because a copy of your files on the same disk as your files protects against nothing. It keeps everything from the last 48 hours and never fewer than the three most recent, and a restore reads from it first because nothing has to leave the machine.
It shares the machine's disk with your database, so it gives way first: once that disk is 90% full, or has less than 1 GB free, the copy on the machine is not written — the backup says so as a failed step — and the database keeps the room it needs to go on working. The offsite copies are written as usual. What the copy on the machine takes is shown on the environment's Storage tab.
Keeping a copy of your own
Under Backups on your team, you can point backups at storage you control as well. Two steps, because the credentials belong to your team rather than to one destination:
- Add the storage under Integrations, once — S3-compatible object storage, an SFTP host or an rclone remote. On S3, give it the bucket backups should go in, and a destination appears on its own.
- Otherwise add a destination under Backups and pick the integration. It asks for a name and nothing else: where it writes and what it signs with come from the integration, so rotating a key there fixes every backup that uses it.
This is additive. Connecting your own storage asks for a copy inside your boundary; it does not stop us keeping ours. It is also how non-production environments get backed up offsite, since we do not do that for you.
When they run
Once a day, in a two-hour window at night: 03:00–05:00 in the country your team is billed in, so a backup is not running while your visitors are. If your visitors keep other hours, choose the hour and timezone yourself under Other → Backup / maintenance schedule on the project's Configuration page. One window covers every environment of the project, and scheduled maintenance on your machines — upgrades that restart a service — is done in the same window. A team with no billing address yet is backed up at 05:00–07:00 local to the continent its machines are on.
The database can be copied every four hours instead. That is an add-on on production, available on either plan and bought under Commercial terms, and it is included on the Dedicated shape. Files stay once a day whatever the database does: uploads change slowly, and the data somebody actually loses in an afternoon is in the database. The extra dumps are kept for a week; beyond that the daily series is what reaches back.
If a backup is missed — the machine was rebuilding, something was down — it runs as soon as it is noticed rather than waiting for tomorrow. A backup that keeps failing is worth a notification: nothing else notices until somebody needs to restore.
What is kept
| Production | Staging | Development | |
|---|---|---|---|
| Daily | 7 | 3 | 1 |
Deliberately shallow. The restores people ask for are from the last few days, and a snapshot from a year ago restored today is a different site rather than an older one.
The same on either plan. How far back a series reaches is not decided by whether a project is shared or dedicated — it is bought for production, and changed whenever you like under Commercial terms on the project's configuration page. Thirty days instead of a week keeps a snapshot for each of the last thirty days, with a weekly and a monthly alongside, and it is the one that catches a problem nobody noticed at the time — the mistake you find on your next invoice rather than the same afternoon. The database can also be dumped every four hours rather than nightly, bought the same way.
The staging and development counts apply where those environments have a backup at all — on your own storage, or the database copy on their machine; see below.
A backup you take by hand is kept for 30 days, regardless of the counts above. Somebody taking one is usually about to do something risky, and what it protects against tends to surface days later — after seven dailies would have rolled it off.
Non-production environments
We do not back up staging or development offsite. What we pay to store is the data you cannot reproduce, and a staging environment is a deploy away from existing again.
They keep the database copy on their own machine, which is what saves a botched migration. If you want them kept properly, connect your own storage.
Restoring
Backups → Restore a snapshot on the environment. It replaces what is there now — anything written since the snapshot is lost — so it asks you to confirm.
Production can only be restored from its own history. Putting staging's database into production is not a restore; it is replacing the live site with a copy of somewhere your developers have been working.
The encryption key
Every repository is encrypted, and each one has a key of its own. A repository is one environment at one destination, so an environment writing to three places has three repositories with three unrelated keys. That is deliberate: a key decrypts the whole repository it belongs to, so sharing one across environments would mean a staging machine holding something that could read production.
The key is generated when the repository is first created, 256 bits from the system's random source, and stored encrypted. It is handed to a machine only for the job that needs it and never written to a task record.
Losing it makes that repository unreadable — by you, by us, by anyone. Restic has no way to change a repository's password without rewriting the repository, so it is the same key for the life of the backups.
Taking it with you
Where the destination is your own storage, the key is yours to have: the
team's Backups page lists every repository on it, with Show the key
beside each. With your own credentials and the key you can restore anywhere,
with no involvement from us — restic -r <repository> restore. It takes an
Admin or the Owner rather than anybody who can restore, because one string
decrypts every snapshot in that repository, including ones taken before you
joined the team, and cannot be changed afterwards — somebody who leaves the
team keeps it.
On the platform's own destinations the key is not offered, and it would not help: those buckets hold every customer's backups behind credentials that are ours, so the key would unlock something you cannot reach. What works there is an export — see below — or bringing your own destination, which is the recommended arrangement for exactly this reason.
Taking a copy out
Backups → Prepare a download turns one snapshot into a plain copy — a database dump or the files — and gives you a URL to it. That is the route out that always works, on any destination: it is decrypted on the machine, so you need no key and no credentials of ours.
What comes out is unencrypted and the URL needs no further authentication, so it asks you to confirm before it is made. The link expires shortly after it is ready; prepare it again for a fresh one.
When a subscription ends
The machines are destroyed a day after the end of the period you paid for. The backups are kept for thirty days after the subscription ends, so the site can be brought back, and then deleted from our storage — the data itself, not only the record of it.
A copy in your own storage is not ours to delete and stays where it is, with its key still yours: that is the one copy that outlives the subscription for as long as you want it to.
Copying data between environments
Backups → Copy from another environment. This is how you get realistic data into staging. It is not read from a backup: the receiving machine asks the source for a fresh database dump and copies its files across directly, so what arrives is what the source holds right now. It replaces rather than merges — the database is rebuilt and the file directories mirrored, so nothing that was there before survives beside it. A deploy follows.
The deploy is not optional and not cosmetic. The database that arrives belongs to whatever code the source was running, and the environment receiving it is running its own — so the update hooks and configuration import have to run for the two to agree. Without that you have a database and a codebase that disagree, which shows up as a fatal on the first page anyone opens.
Nothing is scrubbed unless you say so. See below.
On an environment whose database has a machine of its own, the copy runs in parts, each on the machine holding it: the database on the database machine, the files on the web machine, then your sanitisation commands where the application runs. They appear as separate rows under Activity. A restore of the database there does the same: the data goes back on the database machine, and your sanitisation and the cache rebuild follow on the web machine.
Not available into production, for the same reason a cross-environment restore is not.
Resetting a database
Backups → Reset database empties the environment's database and starts it
again on the version vallic.yaml declares, with a backup taken first and the
newest backup restored after. It exists for one job — moving to a database
version that cannot read the old files — and
Service upgrade is where to start if that is what you
are doing.
Only an Admin or the Owner can do it, and only by typing the environment's name.
Sanitising what arrives
We do not scrub anything on our own. We cannot: we do not know which of your tables hold people. Guessing would produce a result nobody could rely on — scrubbed enough to look handled, not enough to be.
So you declare it, in .vallic/commands/sanitization.yml:
commands:
- 'drush sql:sanitize --yes'
- 'drush sql:query "UPDATE orders SET customer_email = CONCAT(id, ''@example.test'')"'
- 'drush sql:query "TRUNCATE payment_tokens"'
They run in your application container, in order, immediately after the database arrives — from a copy or from a restore into that environment — and before anyone can reach the environment. The first one to fail stops the rest and fails the task — a half-scrubbed environment holding real data while looking handled is worse than one that visibly did not finish.
If the file is not there, nothing runs. Production data lands in staging exactly as it was, and every developer with access to that environment can read it. That is worth deciding deliberately rather than discovering.
The file is read from the environment receiving the data, on the branch it is running — so staging's copy governs what happens to staging.
Never on production. A restore into production is production being put back from its own history, and that data belongs there — so the file is not read and nothing runs, whatever it declares. Nothing can be copied into production from another environment either, so there is no case where production is scrubbed.
Other files can live in .vallic/commands/ later; sanitisation is the first.
Next
Logs covers the other thing that leaves the machine, and Shell access covers importing a database dump by hand.