Changelog

Everything we ship to BucketPilot — new features, and the fixes worth knowing about.

Analytics

Which files are duplicated, not just how much

The Savings Finder told you how many terabytes were duplicated. Now it shows you the files — grouped, described in plain language, and linked straight to the object in your bucket.

"1.5 million objects are byte-identical copies" is a number you cannot act on. See the biggest on the duplicates recommendation opens the findings themselves: the largest groups, worst waste first, each one linked to the file in your browser.

Matched on content, not on name — and that turns out to be the point

Two objects count as duplicates when their checksum and size match. The name is not part of it, and in practice most duplicated bytes are files stored under names that do not match — the kind of thing you would never find by searching for a repeated filename.

Each group is described rather than just counted

Where the copies form a recognisable pattern, the finding says so:

  • *One file, filed once into each of 48 folders — Page 21 to Page 68* — a document covering a page range, written once under every page it contains.
  • *The same file in 122 folders, in 3 buckets* — a file that has spread across buckets, which usually means a copy nobody remembers making.
  • *184 different names across 89 folders* — identical content, unrelated names.

No delete button, and that is deliberate

A duplicate is not automatically waste. Something in your systems may be resolving a path to each of those copies, and collapsing them is a change at your end, not a cleanup we can do from a dialog. The findings tell you where to look; what to remove stays your decision.

Worth knowing before you act: for files uploaded in parts, the checksum covers the parts rather than the whole object, so it depends on the part size used at upload as well as the content. Matching checksum and size is strong evidence — confirm before deleting anything irreplaceable. Where a group is very large the dialog shows a sample of the copies and says so; the count itself is exact.

Buckets

Old object versions cost the same as live ones

On a versioned bucket, every previous copy is billed at full price. You can now move them to a cheaper class instead of deleting them — and the Savings Finder tells you what that is worth.

Turn versioning on and S3 keeps every previous copy of every object. Those copies are billed exactly like live files, they appear in no listing, and nothing in most tooling shows you they are there.

Tier them, don't just delete them

The lifecycle editor could already expire noncurrent versions — permanently delete them after N days. It can now transition them instead: move them to a cheaper storage class and keep them. Current files are untouched, nothing is destroyed, and the class can be changed later.

Only the destructive option existed before. If you wanted to spend less on old versions, the only button on offer was the one that threw them away.

The Savings Finder can see them now

A new recommendation reports what your noncurrent versions are costing across every versioned bucket, and applies the rule to the buckets you choose. It writes a separate lifecycle rule from the cold-data one, so tiering old versions never quietly rewrites what you set for live files.

Rules that could never have worked are now refused

S3 accepts a lifecycle rule that it will then silently never act on — moving objects to a class their current class cannot reach. There is no error and no event; the rule simply sits there doing nothing, and the only clue is a bill that does not fall. BucketPilot now checks the destination against the classes your bucket actually holds and refuses the rule with the reason, and the rule simulator says *"S3 will not perform this"* instead of *"0 objects eligible"* — which reads as *"nothing is old enough yet"*.

And editing a rule no longer drops the parts we don't show

Saving a lifecycle rule replaces the whole configuration on the bucket. Clauses the editor did not display — date-based transitions, expired-delete-marker cleanup, object-size filters — were being lost when you edited something unrelated. They are now carried through untouched.

You can also set abort incomplete multipart uploads from the same screen. A failed upload leaves its parts behind, they are charged as storage, and they appear in no object listing — so nothing else could show them to you.

Platform

What the support assistant is allowed to see

Credentials, secrets and object contents never reach the assistant or the model behind it — enforced in code and tested, not stated in a policy.

The in-app assistant answers questions about your account, which means it reads some of your data and sends some of it to a model API. What it may see is now a mechanism rather than an intention.

Never sent, in either direction

  • Credentials and secrets. Anything credential-shaped in a message you type — access keys, tokens, passwords, connection strings, private keys, signed URLs — is removed before the message is stored, before it reaches the model, and before it can enter any cache or support email. Redaction happens once, at the door: scrubbing later would be theatre, because by then the value has already been written down.
  • Object contents and file names. The assistant reads account metadata — plans, limits, bucket names, job status, failure reasons — and never object keys. Where a name could leak through a written activity description or a job's error message, it is removed there too.

Minimum metadata

The assistant's tools select named columns, not rows. They never read the credentials table, and a bucket that has lost its credential is reported to the assistant as a yes/no, not as which one.

Why we are telling you this

"Here is my key, why doesn't it work" is the most natural thing in the world to paste into a support box about credentials. It should not be the thing that puts a secret in a log — so the product assumes you will, and handles it.

Platform

Your Windows servers, same as everything else

Connect a Windows machine over SSH and browse it as a drive, migrate off it, or restore onto it — with the name and path rules Windows actually enforces, checked before anything is written.

Servers you connect over SSH used to mean Linux. Now a Windows machine works the same three ways: browsed as a drive beside your buckets, migrated off, or restored onto.

BucketPilot works out which kind of machine it is rather than asking you. On the first successful connection it reads the server's own answer, stores it, and from then on separates, compares and contains every path on that server the way that operating system does. There is no dropdown to get wrong.

The part that matters: what Windows won't store

An object key can be almost any text. A Windows filename cannot — and the dangerous cases are not the ones Windows refuses, they are the ones it accepts and quietly does something else with:

  • A colon in a name is accepted, and opens a hidden NTFS data stream — the bytes end up inside a file with a truncated name, and a naive copy reports success for something that exists nowhere you would look.
  • Capitalisation folds. Report.pdf and report.pdf are two objects in a bucket and one file on Windows, so copying both would silently overwrite one and report two successes.

Every name is checked before it is written, per file, and a name that cannot arrive as itself is refused with the reason while the rest of the run continues. On a migration into Windows you can also press Check names first and see the whole list — unwritable names, case collisions, reserved device names — before the run starts, while there is still time to rename them at the source.

Set-up help that matches your machine

The instructions on the server dialog follow the platform we detected, including the two Windows steps that catch nearly everyone: OpenSSH Server is an optional feature there, so a healthy machine that never had it installed looks exactly like one that is switched off; and an administrator's key does not go in that account's .ssh folder — Windows ignores it there, which looks identical to a wrong key. A failed connection test now says which of those it was.

Fast, and incremental

A Windows drive is enumerated by a single process on the server rather than by thousands of round trips, so re-scans only look at what changed. On a large, mostly-unchanged drive that is the difference between seconds and minutes — hourly auto-sync is comfortable on Windows.

Server drives and server destinations are on the Business plan; migrating *from* a server is available from Pro.

Platform

A new release no longer blanks the page you're on

A tab left open across one of our deploys could go white on the next page you opened, until you reloaded it by hand. It now recovers itself, once, without losing where you were.

If you left BucketPilot open in a tab and we shipped a release underneath you, the next page you clicked into could come up blank — no error, no message, nothing but a white panel until you reloaded.

What was actually wrong. The app is split up so that each page's code is only fetched when you go to it, and a release renames those files. A tab loaded before the release asks for a file that no longer exists, gets a 404, and the app had nowhere to put that failure — so it unmounted everything.

Now a page that can't fetch itself reloads exactly once, lands on the page you were going to, and can never get stuck in a loop reloading. If anything else goes wrong on the way, you get a screen that says what happened and a Reload button, instead of white.

It was reported to us as "the credentials page goes white after a deployment". It was never that page — it was whichever page you happened to open first.

Buckets

Every version of a file, and a way back from a delete

On a versioned bucket you can now see every version of an object, put an old one back, and undelete a file — all from the file browser.

Versioning has always been something you could switch on. Now it is something you can use, without leaving the browser or opening a console.

Switch on All versions in a bucket that has versioning enabled and the listing changes shape: every version of every object, newest first, each row naming the file it belongs to.

  • Put an old version back — *Restore this version* copies it onto the current key. Nothing is destroyed: the version you are replacing is kept too, so a restore is itself undoable.
  • Undelete a file. A deleted object in a versioned bucket isn't gone, it's hidden behind a delete marker. Those are shown as their own rows — remove the marker and the file is back where it was.
  • Find something you deleted by searching its name, which previously found nothing at all because the current listing no longer had it.

Deletes are in the activity feed for the first time, and each one says whether it is recoverable — read from the bucket's live state at the moment of the delete rather than from what we last cached, so it can't cheerfully tell you a permanent delete was recoverable. The confirmation before a delete says the same thing, while you can still change your mind.

Folder rows caught up with file rows at the same time: copy, move, storage class, tags and version history are all available on a folder.

One safeguard worth knowing about. If a bucket's versioning was switched off outside BucketPilot after we last looked, restoring into it would have overwritten the current object with no copy kept. That is now refused on the bucket's live status, and the refusal offers to switch versioning back on and finish the restore for you.

More in Working with objects.

Buckets

Copy between buckets — across regions, and across accounts

Copies and moves in the file browser now work between regions and between AWS accounts, and run several times faster when S3 can do the work itself.

Copying a file or a folder into another bucket used to work only when the two buckets were close relatives. Now one question decides every copy: can S3 do this itself?

  • Across regions now works. S3 refuses a plain server-side copy between regions, and that refusal used to surface as a failure with no useful explanation. Those copies now take the route that does work.
  • Across accounts works from the file browser, for buckets connected under different credentials.
  • It is much faster. When S3 can do the copy itself, we ask it to and the data does not pass through BucketPilot on that path. Measured on a cross-region copy: 10.7 MB/s before, 96 MB/s after, across eight parallel lanes.

The same helper now decides this for backup and migration jobs, which had been assuming the opposite answer — so a same-account backup that S3 could have done for free no longer pays to move the data through us.

Copies between different providers — S3 to R2, Azure, or one of your own servers — still run as a job, because there is no provider that can do that copy on its own.

Billing

Close your account — and change your mind for 30 days

Closing your account cancels the subscription and locks it straight away, but deletes nothing for 30 days. Signing back in brings it back in one click.

You can close your BucketPilot account yourself, from Settings. Until now the only way was to ask us.

Straight away: your subscription is cancelled, your sessions are signed out, API keys stop working, and the account drops to the free feature set. Nothing is deleted for 30 days — your buckets and drives are your own storage and are never touched at all, and everything we hold about them stays put. Sign back in within those 30 days and one click reactivates the account exactly as it was.

If you own a workspace with other people in it, you are asked to hand ownership to another admin first, so a team is never left without an owner. Transferring ownership of a workspace is new in its own right — before this, ownership could be acquired but never moved.

Jobs

"Is this backed up?" — ask the bucket

Every bucket and server drive has a Backups tab: what protects it, what it protects, and whether the cover is only partial.

The question gets asked while you are looking at a bucket, and answering it used to mean going to the Backups page and matching names by eye.

Open any bucket or server drive and there is now a Backups tab:

  • What protects this — the jobs that copy it somewhere else, when each last ran, and whether it succeeded.
  • What this protects — because a bucket that *receives* a backup is holding someone else's copies, and that is a different fact about it. Direction is carried rather than flattened.
  • Partial cover reads as partial. A job that copies one folder inside a bucket does not turn the header green — it says which part is covered.
  • No cover at all offers to create a job, deep-linked to the real form with this bucket already filled in as the source.

Coverage is read from the same place the protection board reads it, so the two can't disagree — including the case where a drive is protected by a migration rather than a backup job.

Buckets

Watch a copy, or walk away from it

Copies, moves and recursive folder deletes now show a real progress bar, keep going when you navigate away or close the tab, and survive our own deploys.

Bulk work in the file browser used to be something you had to sit still for.

  • A real progress bar. The total is worked out before the first object is copied, so the bar measures the actual job rather than guessing.
  • One job for a whole selection. Select a mix of files and folders and it is one piece of work with one bar, not one per item.
  • Walk around the app. The bar lives above the page, so a 40-minute copy keeps running and keeps reporting while you use the rest of BucketPilot. Refresh the browser, or close the tab and come back, and it is still there.
  • It survives losing the machine that was running it. These operations are now recorded as work in progress rather than living inside one process. If the process running yours goes away, another picks the job up and skips everything that already landed — checked against your storage, not against our own count, so nothing is copied twice and nothing is missed.

There is no size threshold any more: three files can be three gigabytes, so everything gets the same treatment.

One number for the last of those: a routine deploy in the middle of a transfer used to freeze the progress bar for about ten minutes. It is now three seconds.

Buckets

Bucket policies are checked before AWS sees them

Values are validated as you write, a valid-but-risky policy asks before saving rather than after, and ten copy-paste recipes are published.

"Save policy failed" was, for most people who saw it, a placeholder in our own starter template that they had not noticed — and the message told them none of that.

  • Checked before submission. Account ids, ARNs and principals are validated as you write them, by the same rules on the editor and the API, so a typo is caught where you can see it rather than coming back as a failure from AWS.
  • AWS's own answer, when it is AWS refusing. A policy AWS rejects now shows what AWS said. It used to arrive as a generic server error, which read as our outage rather than your typo.
  • Risky-but-valid policies ask first. Making a bucket readable by the whole internet is a legitimate thing to do and a terrible accident. You are asked before it saves — the console only badges it afterwards. Purely informational notes never interrupt.
  • We never block a policy AWS would accept. Uncertain cases are warnings, not errors. (The validator's first week produced exactly the failure it exists to prevent, by calling a working production policy invalid. That rule is the fix.)
  • Ten copy-paste recipes are published in the docs — read-only public access, CloudFront, VPC-only, deny-unencrypted and others. Each one is checked against our own validator on every build, so a recipe we publish can never be one our editor rejects.

Every save is still kept, with a diff and one-click restore. Full details in Bucket policies.

Billing

Restoring back onto a server is included wherever the migration is

Putting a migration back onto the server it came from no longer requires Business. It was the worst possible moment to meet a paywall.

If you migrated your own server into a bucket on Pro, restoring that data back onto the server used to require an upgrade — discovered at the moment you needed it.

That gate is gone, and the reason is structural rather than generous. A restore cannot choose where it writes: it puts data back exactly where the migration took it from, and there is no destination to name. If the migration was allowed, the way back is allowed.

Still Business, and unchanged: naming a server as a destination in the first place — server-to-server and bucket-to-server migrations, and connecting a server as a drive. Those are the cases where you point at an arbitrary machine.

See Plans & billing.

Platform

Signing in, tidied up

Password rules are stated before you're rejected, every password field can be revealed, the Google account picker works, and sign-in no longer tells a stranger whether an address has an account.

Four things from a pass over sign-up and sign-in.

  • The password requirements are stated up front, instead of appearing as a rejection after you have chosen one. There are no character rules — no "must contain a symbol". What we check is strength, plus whether the password appears in known breach data, and that is now what the form says.
  • A reveal toggle on every password field — all four of them, including the ones during a reset.
  • Choosing which Google account signs you in now works. We announced it on 5 August and it never reached Google: the setting was being sent under a name Google's API doesn't read, so the picker was skipped and whichever account you were already signed into was used. If you have a personal and a work Google account, you get the chooser.
  • Sign-in no longer reveals whether an email address has an account here. A wrong password and an unknown address get the same answer.
Docs

The exact permissions we ask for, published

The minimum IAM policy BucketPilot needs is written out in the connect dialog, the docs and the assistant — and it no longer includes deleting buckets.

"What access does this actually need?" is the first question a security review asks, and the honest answer needs to be in front of the person answering it.

The minimum IAM policy is now published in full: in the dialog where you connect a bucket, in Connecting AWS, and to the support assistant. All three render from one definition, so they cannot drift apart.

s3:DeleteBucket has been removed from the published policy and from both CloudFormation templates. BucketPilot has never used it — there is no feature in the product that deletes a bucket — and it should not have been asked for. A stack you created before this keeps the old permission until you update it, so if you want it gone, update the stack or edit the policy directly.

And a key with a broad s3:* policy now gets a note explaining that it is wider than we need, rather than silence.

Buckets

Size alerts for server drives

A server drive you have indexed can carry size thresholds like any bucket, with the caveats that come from measuring a filesystem rather than a bucket.

Size & cost alerts arrived for buckets last week. They now work on server drives too, on the Business plan.

The Alerts tab appears on a drive as soon as you turn indexing on for it, because that is what makes the drive measurable — and it disappears again if you turn indexing off, taking that drive's rules with it. A rule left behind would be armed, invisible and permanently silent, so we say so rather than leave one.

Size thresholds only. Cost thresholds are refused on a drive for a reason we would rather state than hide: it is your own storage on your own machine, and we have no price to apply to it.

The email and the tab carry the drive's own caveats — the figure comes from the last walk, symlinks are not followed, and a hard-linked file is counted once per path.

Fixed along the way: a re-walk's new size could take up to a day to reach the alert, because a walk finishing didn't tell the analytics they were stale. Only the very first walk was picked up promptly.

Buckets

Know before the bill does — size & cost alerts

Set thresholds on a bucket's size or estimated storage cost and get an email when it passes them. Several per bucket, so you can escalate.

A bucket that quietly triples in size costs you twice — once on the storage bill, and once in the time it takes to notice. On the Business plan, buckets can now tell you.

Open a bucket, go to the Alerts tab, and set a threshold:

  • Size, in GB — works with every storage provider.
  • Estimated cost, in dollars per month — available wherever we can price the storage.

Add as many as you need. Escalating thresholds are the point: $20, then $40, then $80. When several are set you hear about the highest one you've passed, not all of them — a bucket at $45 tells you it's over $40, not also over $20, which you already knew. Cross a higher one and you're told straight away.

You get an email when a bucket goes over, then once a day while it stays over. Drop back under and the alert resets itself, so the next crossing is reported immediately rather than a day later. Alerts go to workspace admins, and anyone can turn the emails off for themselves in Settings → Notifications.

Two things worth knowing. Alerts read the same index the rest of the app uses, so a bucket that hasn't been indexed yet has nothing to measure — the tab says so instead of showing you a zero. And the cost figure is an estimate of storage: your indexed data at the region's per-GB rate. It doesn't include request, transfer or retrieval charges, so it won't match your provider's invoice exactly. It's there to catch a bucket running away, not to reconcile a bill.

Full details in Size & cost alerts.

Teams

Invite your whole team

Team workspaces no longer have a seat limit — invite as many people as the team needs, and each invitation now only works for the person it was sent to.

A team workspace used to stop at five people. That number came from the identity service behind our sign-in, not from anything about the product, and it was the wrong answer for teams who wanted everyone in one place.

There is no seat limit. Invite as many people as the team needs. The Team page now shows how many members you have, rather than how many you have left.

Two things came with it:

  • Invitations are addressed to one person. A link only works for the email address it was sent to — so a forwarded or mis-clicked invitation can't quietly be used by someone else, and an admin's own seat can't be spent by opening a colleague's link.
  • Clearer answers when a link doesn't work. An invitation that has expired, one that has already been used, and one you've opened while signed in as a different account are three different situations, and now say so. If you open a link for a workspace you're already in, you simply land in it.

Everything else about workspaces is unchanged: team features still ride the workspace owner's Business plan, data is still fully isolated per workspace, and roles (admin, member, read-only) work exactly as before.

Buckets

Storage connections are verified the moment you add them

A new connection is now exercised against your storage before it is saved — so a setup that can't do what you need is caught at the point you create it, not on the first job that fails.

Adding a storage connection used to succeed as long as the keys were well-formed. Whether the connection could actually *do* anything — list your buckets, create one, run a backup — only became apparent later, on the first operation that failed. The error you saw then described that operation, not the connection behind it, which is a long way from the actual problem.

Connections are now checked at the point you add them:

  • The connection is exercised for real before it is stored. If it cannot reach your storage, you are told immediately, and told why.
  • Role-based setups must include their role. The Role ARN field is now required, and a role that cannot be assumed is reported as exactly that — with the message your provider returned — instead of surfacing later as an unrelated-looking bucket error.
  • Narrowly-scoped connections still work. A connection that reaches your storage but cannot enumerate every bucket is saved as before — plenty of them are deliberately scoped that way. What is new is that you are told up front that Discover buckets will come back empty for it, so you can add buckets by name rather than staring at a blank panel wondering which end is broken.

Discover buckets is also current immediately. Connecting or removing a connection used to leave the discovery list showing its previous answer for a few minutes — indistinguishable from an account with nothing in it. It now refreshes the moment anything changes, so what you see after connecting is what you actually have.

Buckets

Auto-sync is now included on Starter

Keeping a bucket's index up to date on its own is no longer a Pro feature — every paid plan has it.

Auto-sync — the hourly or daily background re-scan that picks up objects written straight to your storage — is now included on Starter, alongside Pro and Business. It used to require Pro.

Nothing to do if you already have it on. If you're on Starter, open any bucket and flip on Auto-sync new objects, then pick hourly or daily.

Two things worth knowing:

  • Buckets indexed from daily S3 Inventory don't need it. They already refresh from the inventory report every day, so the toggle doesn't apply to them.
  • Auto-sync stops if a plan lapses to the free tier, and starts again when you resubscribe. Your existing index stays browsable either way.

*(An earlier entry from 27 July describes auto-sync as Pro-only. That was accurate then; this entry supersedes it.)*

Teams

Every action in your workspace is on the record

The activity feed now covers team and access changes — invitations, members, groups, API keys and permissions — not just the work that moved files.

The activity feed recorded what happened to your *data*: uploads, job runs, restores, new buckets. What it didn't record was who changed *access* to it — and that's the half an admin actually goes looking for.

"Who gave them access to that bucket?" now has an answer. These are recorded from today:

  • People — invitations sent and revoked, members removed, roles changed.
  • Groups — created, renamed, membership changed, deleted. Deleting a group also revokes every bucket grant that pointed at it, and the entry says so, because that's usually the missing link when someone loses access unexpectedly.
  • Permissions — every change to who can reach a bucket or a folder, including whether it was opened to the workspace or restricted.
  • API keys — created and revoked, with the key's name, its permission level, and its short prefix so you can tie an entry to a specific key. The key itself is never recorded.
  • Servers and credentials — added, updated, removed. A server entry names *what* changed — a rotated key and a changed hostname have very different consequences when you're reading back through a week of history.
  • Indexing — when a bucket index was started, and by whom.
  • Deletions — removing a bucket or a server drive. The entry states plainly that the bucket itself was untouched; we removed our record of it, nothing more.

Every one of these appears in the activity feed, in team analytics, and in the Business audit export — the CSV carries timestamp, actor and action, so it drops straight into a review or a compliance pack.

Access events are grouped and coloured apart from routine data movement, so scanning a busy feed for the moment something changed hands doesn't mean reading every row.

Platform

Activity history is kept for 100 days

A clear, stated retention window for your workspace's activity feed and audit trail — long enough for a quarterly review, and bounded so the record stays fast to search.

Now that the activity feed is a complete record of who did what, it needs a stated lifespan rather than an implied one.

Activity is kept for 100 days. Entries older than that are removed automatically. The window comfortably covers a quarter-end review and every reporting period the analytics pages use.

If you need to keep it longer, export it. The Business audit export writes the full record to CSV for any date range you choose — timestamp, actor, action, bucket and job. Exported files are yours, with no expiry. If your organization has a retention obligation beyond 100 days, a scheduled export is the way to meet it.

Two things worth being direct about:

  • Removal is permanent. There is no archive behind the feed and no way to recover entries once they age out — which is exactly why the window is published here rather than left as an implementation detail.
  • Nothing else is affected. This applies only to the activity record. Your buckets, objects, jobs, backups and their run history are untouched and have no such window.
Teams

Server drives are private by default

A newly connected SSH/SFTP drive now starts closed to the rest of your workspace, so access to a live filesystem is something you grant rather than something you remember to remove.

A server drive isn't a bucket. Its root is a real filesystem — a web root, a home directory, an application folder — and the cost of the wrong person having write access there is different in kind.

New server drives now start restricted. When you connect one, you and your organization's admins can use it immediately; everyone else sees nothing until they're granted access. Inviting a teammate no longer hands them every server in the workspace by default.

  • Grant per person or per group, for the whole drive or for a single folder.
  • A block always beats a grant, so you can open a drive broadly and still fence off one directory.
  • The lock badge is on the drive itself, in the bucket list — you can see that a drive is restricted without opening its permissions.

Folder-level rules are enforced everywhere on server drives, not just in the file list: browsing, downloading, renaming, deleting, creating folders and uploading all consult the same rules.

Cloud buckets are unchanged — they still default to open for the whole workspace, which suits shared object storage. Server drives you already connected are also unchanged, so nothing anyone relies on today stops working. If you'd like an existing drive closed, set it to Restricted on its Permissions tab.

Buckets

Clearer answers when a server refuses a file

Uploads, renames and deletes on a server drive now report what the server actually said — a permissions problem, a full disk, a quota — instead of a generic connection failure.

When a server drive rejected an operation, BucketPilot reported that the server "may be unreachable". Often it was perfectly reachable: it had answered immediately, and the answer was no.

Server responses are now translated into something you can act on:

  • Permission denied — the account BucketPilot connects as can't write to that directory. The message points you at the folder's owner and permissions, which is where the fix is.
  • No space left — the disk is full.
  • Quota exceeded — the account's own limit is reached, even though the disk isn't.
  • Read-only filesystem — the path can't be written to at all.
  • Path no longer exists — the folder moved or was removed since the page loaded.

This applies to every operation on a server drive, not just uploads. A genuine connectivity failure still reports as one — but it now means what it says, rather than being the answer to everything.

Analytics

Server drive uploads appear in activity and analytics

Files uploaded to an SSH/SFTP drive are now recorded like any other upload, so they show in the activity feed, the audit export and team analytics.

Uploads to cloud buckets have always been recorded. Uploads to server drives were not — the file arrived, and nothing wrote it down.

The effect was quiet but real: the Uploads series in team analytics flattened as workspaces moved onto server drives, and the audit trail had a gap exactly where the newest work was happening.

Server drive uploads are now logged the same way, with the file and its size, so they appear in the activity feed, in the audit export, and in the per-member contribution figures in team analytics.

Historical uploads to server drives can't be recovered — they were never recorded. Everything from this release forward is.

Analytics

Restores are counted separately from scheduled runs

Recovery is no longer mixed into your automation figures, and the time-saved estimate now counts only runs that actually moved data.

A restore was being filed alongside scheduled backup and migration runs. It made the automation numbers on your dashboard read high — a workspace with a single job could show a month's worth of "automated runs" that were mostly recoveries and retries.

Restores now have their own category. They still appear in your activity feed and in analytics, where they're grouped as *Restores* rather than buried among scheduled work. If you're reviewing an account, the moments someone recovered data are now the easiest thing to find rather than the hardest.

"Automated runs" counts scheduled and manual job runs only, and the time-saved estimate beside it now counts only runs that actually copied something. A scheduled run that finds everything already in sync is still shown — that's your automation confirming it has nothing to do — but it no longer claims to have saved you work it didn't do.

We'd rather these numbers be smaller and true.

Platform

Sensible limits on names and folders

Names and folder names are now checked against what your storage provider will actually accept, and refused clearly here rather than failing somewhere downstream.

Every text field that names something — credentials, servers, jobs, groups, drives, folders — now has a limit, and says so when you cross it.

Two different kinds of limit, for two different reasons:

  • Your provider's rules. Amazon S3 caps an object key at 1,024 bytes; Linux filesystems cap a single folder or file name at 255. Exceeding either used to produce an error from the provider, several steps away from the box you typed in. BucketPilot now checks first and names the field.
  • Ours. Display names are capped at 100 characters — generous for a name, short enough that lists and tables stay readable.

Limits are measured in bytes where the provider measures bytes. This matters more than it sounds: Arabic, Chinese, Japanese and emoji characters take two to four bytes each, so a folder name that looks comfortably short can exceed a filesystem's limit. The message tells you the actual size and the actual limit, and mentions when non-Latin characters are the reason.

Folder names are also checked for the things that would quietly put your files somewhere else — slashes, .., and invisible control characters.

Platform

Choose which Google account signs in

Signing in with Google now always asks which account to use, instead of silently continuing with whichever one your browser happens to hold.

If your browser was signed in to a single Google account, "Continue with Google" used it without asking. Convenient — until it's the wrong one.

BucketPilot creates an account for whoever arrives. Signing in with a personal Google account instead of a work one didn't produce an error; it produced a second, empty workspace, with none of the buckets you expected and nothing on screen to explain why.

Google now always shows its account picker, on both sign-in and sign-up. It costs one click and removes a class of confusion that was very hard to diagnose from the inside — particularly on invitations, where the account you accept with is the account that joins the organization.

Migrations

Restore files back to a server, not just a bucket

Disaster recovery now works in both directions for SSH/SFTP servers: restore a folder — or an entire directory tree — from your cloud backup straight back onto the machine it came from.

If BucketPilot has been copying a server's files, it can now put them back.

Until today, restore existed only for bucket-to-bucket backup jobs. That left the data most likely to be irreplaceable — a web root, a document store, an application directory on a Linux box — with a backup and no way home. Every migration job can now be restored, and either end of the recovery can be a server.

Three recovery paths, all one click:

  • Server → cloud → server. A job backing up an SSH/SFTP machine into S3, Cloudflare R2, or Google Cloud Storage can now push those files back down to the original server.
  • Server → server. Cross-machine replication reverses cleanly, so the mirror machine becomes a recovery source.
  • Cloud → server → cloud. An offline copy pulled down to your own hardware can be sent back up to the bucket.

A restore is the reverse of the job that made it. It reads from wherever the job wrote and writes back to wherever the job read — including undoing the folder mapping the job applied, so files land at the paths they originally had rather than nested a level deeper.

It restores your files, and only your files. When a job copies the *contents* of a directory into a destination folder, that folder can also hold files the job never put there. A restore now consults the job's own manifest — the record of what each run actually copied — and leaves anything else alone, reporting it rather than pushing it onto your source machine.

The safety rules are the same ones a recovery tool should have, and they aren't configurable:

  • Nothing is ever deleted at the destination of a restore.
  • Files already present are left untouched, so a newer file is never buried under an older copy. Rolling back to the backup's contents is an explicit, off-by-default choice.
  • Writes to a server are atomic — a file is written to a temporary path and renamed into place, so an interrupted restore leaves either the old file or nothing, never a half-written one.
  • If the backup directory can't be read, the restore fails and says so instead of reporting that it found nothing.

Restore is available on Pro; restoring onto an SSH/SFTP server requires Business, the same plan that covers copying to one. Jobs that read from Azure Blob Storage can't be restored — Azure is supported as a migration source only — and BucketPilot now says so upfront rather than failing partway.

Jobs

See exactly which files a restore brought back

Every restore now records the filenames it recovered, distinguishes an empty folder from a missing one, and keeps the record long after the run finishes.

A restore that reports 0 objects restored can mean two completely different things: everything was already in place, or nothing was found because the path was wrong. Until now those looked identical — and the person reading them has usually just lost data.

Restores are now explicit about what happened.

  • The filenames, not just the count. Each restore keeps the list of files it actually wrote and shows it under the progress bar. It's stored with the run, so you can open a restore from last week and still see what it recovered. Large recoveries keep a capped sample, and the page says plainly when the list is a sample rather than the whole set.
  • "Nothing matched" is its own outcome. A restore that found no files under the folder you named now says so, names the scope it searched, and flags it — instead of a green tick and a zero.
  • A missing source fails loudly. If the directory being restored *from* doesn't exist or can't be read, the restore fails and names the path and host. An empty backup and an unreachable one are opposite answers and no longer look the same.
  • Live progress shows only what's being restored. The filename ticking past is a file being written, not one being checked and skipped — so a restore that recovers one file out of a thousand reads as exactly that.

Restores also run where they belong. They execute on background workers rather than the tier serving the app, so a large recovery no longer competes with the UI you're watching it from, and a deploy mid-restore doesn't interrupt it. Progress is shared across replicas, so the numbers advance smoothly instead of jumping backwards.

Jobs

Backup jobs check compatibility before you save

Pointing a backup job at storage it can't copy between is now refused when you create it, with a clear reason, instead of failing on the first scheduled run.

Backup jobs copy over the S3 API, which not every storage type speaks. Pointing one at an SSH/SFTP-backed bucket used to be accepted without complaint — and then fail on its first run, hours later, with a technical error about adapters.

That's the worst possible time to find out. The job looked configured, it was on a schedule, and nothing had been backed up.

Compatibility is now checked when you create the job. If either end can't be copied over the S3 API, BucketPilot refuses it immediately, names the storage type, and points you at the right tool — a migration job, which is built for servers and, as of today, can be restored too.

Jobs

Restore from a backup, in one click

Put back files you deleted by mistake — a whole folder or the whole bucket — straight from the backup job that was quietly keeping them safe.

A backup is only worth having if you can get your files back out of it. Now you can.

Open any backup job and choose Restore. Objects are copied from the backup back to the original bucket — the opposite direction to a normal run.

It's deliberately cautious, because the moment you need it is the worst moment to be surprised:

  • Nothing is ever deleted from your original bucket.
  • Files already there are left untouched, so a newer version is never replaced by an older copy.
  • Restore a single folder instead of everything — usually what you want, and far quicker.

If you do want the bucket rolled back to the backup's contents, there's an explicit option for that. It's off by default.

Buckets

Mirror a bucket using AWS's own replication

Set up native S3 replication from BucketPilot — AWS copies each new object within seconds and moves the data itself, while we watch it and tell you if it ever stops.

Some copies should be instant. Mirror sets up Amazon's own S3 Replication between two of your buckets: AWS copies each new object within seconds and moves the data itself, so no job runs on your account.

We handle the fiddly part — the replication rule and the IAM role — and then keep an eye on it. A replication rule is famously fire-and-forget: if someone removes it or turns versioning off, nothing tells you and the copy quietly stops. We check, and we tell you.

Mirror or backup? The question that decides it is: *if someone deletes a file by mistake, should it disappear from the copy too?*

  • Yes — you want a Mirror. It protects you from losing a region or a bucket.
  • No — you want a Backup. It never deletes at the destination, so a mistake can be undone.

Most teams end up with both. The new Protection panel in Analytics shows, per bucket, whether it's mirrored, backed up, or neither — including the buckets nobody has covered yet.

Mirroring needs both buckets to be Amazon S3 with versioning on; where a pair doesn't qualify, BucketPilot now says exactly which condition failed. And note that replication only covers objects written after you switch it on — run a one-off backup to bring across what's already there.

Jobs

Back up tens of millions of objects — fast

Large S3-to-S3 backups now run in a flat, steady memory footprint, copy small files in parallel for dramatically faster syncs, survive a deploy mid-run, and tell you how long each run took.

Big buckets deserve big backups. This release makes S3-to-S3 replication ready for serious scale:

  • No size ceiling. A backup now streams its source-vs-destination comparison through the database instead of holding the whole object list in memory, so a job over tens of millions of objects runs in a small, steady footprint — the out-of-memory wall on very large buckets is gone.
  • Small files fly. Runs made of many small objects — logs, thumbnails, documents — now copy in parallel, scaling the number of transfers to the work at hand. In our own testing a large sync went from roughly 1.6 MB/s to 30+ MB/s on the same hardware.
  • Deploy-safe. If we ship an update while your backup is running, the job hands off cleanly and resumes on its own — a routine deploy no longer interrupts a long-running copy.
  • Know how long it took. Backup and migration completion emails now include a "Finished after" time (e.g. "1h 30m").

There's nothing to turn on — every replication and migration job is faster and sturdier automatically.

Billing

Business now indexes 50 million objects

The Business plan's indexed-object allowance is now 50 million — double the previous 25 million — so larger estates stay fully searchable.

We've doubled the Business plan's indexed-object allowance to 50 million objects, up from 25 million.

Every indexed object is instantly searchable with wildcards and filterable by extension, size, and storage class — now across twice the footprint. Pro stays at 10 million and Starter at 1 million.

If you're on Business there's nothing to do: the higher ceiling is already live and your buckets keep indexing right up to it.

Platform

See which plan every feature is on

Every Pro and Business feature now shows a small badge across the app, so you always know what's included in your plan — especially handy during a trial.

It wasn't always obvious which capabilities belonged to which plan — particularly during a trial, when everything on Pro is unlocked and nothing told you so.

Now every plan-gated feature carries a clear badge: an amber Pro or a violet Business tag, shown consistently in the sidebar, on the analytics sections, in Settings, on the Credentials and Backups screens, and in the create-job forms.

The badges are purely informational — they tell you *which* plan a feature belongs to. If something isn't in your plan yet, you'll still find a one-click upgrade right where you need it.

Buckets

A cleaner console: sharper imports and clearer forms

Import buckets one account at a time and land right on them, see a live count of your imported buckets, and enjoy a tidier Buckets page and a redesigned replication form.

A round of polish across the console:

  • Import one account at a time. Connecting buckets is now scoped to a single account, and the moment an import finishes we take you straight to that account's buckets — no more scanning a long, unfiltered list to find what you just added.
  • A live bucket count. The Buckets page header now shows how many buckets you've imported, and how many match your current filter.
  • SFTP servers, tucked away. "Browse an SFTP server as a bucket" now sits behind a button instead of taking up space on the page — open it when you need it.
  • A clearer replication form. Creating a replication job now uses the same side-by-side Source and Destination layout as migrations, so both read the same way.
Platform

Manage objects over the API — with a BucketPilot key, not S3 keys

The REST API can now upload, delete, copy, and tag objects — and every key is read-only or read/write, so you decide what it can touch.

The API was read-mostly: list buckets, search objects, presigned downloads, trigger jobs. It can now manage the objects themselves, so your scripts and integrations use a BucketPilot key instead of handling raw S3 credentials.

New object endpoints on buckets & objects:

  • Read — object metadata (HEAD), tags, and version history.
  • Write — upload (a presigned PUT, so bytes go straight to your own storage and never through us), delete, copy/move, create folders, set tags, and change storage class.

Anything you upload or delete over the API shows up in search and cost exactly like an action taken in the app — the index stays in sync automatically.

Keys now carry permissions. When you create a key you choose Read-only (list and download, read jobs and usage) or Read/write (also change objects and trigger jobs). Every existing key is read-only, so nothing gained new power on its own — mint a read/write key when you need one. A read-only key that hits a write endpoint gets a clear 403 READ_ONLY_KEY.

Note for existing integrations: triggering a job run, pause, cancel, or a reindex now needs a read/write key (a backup run writes data), so reissue those keys if a script does more than read.

Buckets

Auto-sync keeps a bucket's index up to date on its own

Turn on an hourly or daily background re-scan so objects added straight to your storage appear in BucketPilot without a manual re-index.

A bucket's search index only reflected objects added *through* BucketPilot or captured by the last manual Re-index. Files written directly to your storage — by another app, a pipeline, or the AWS console — sat invisible until someone re-indexed by hand.

Auto-sync closes that gap. On a bucket's page, flip on Auto-sync new objects and pick hourly or daily; a background job then re-scans on that cadence, picking up new and changed objects and dropping ones that were deleted — no clicks, no stale listings.

It's opt-in per bucket and available on Pro and Business. Buckets that already use daily S3 Inventory indexing don't need it — they refresh from the inventory report every day already.

Teams

Support conversations now follow the workspace you're in

Switching workspaces used to leave the support panel on the previous workspace's conversation — each workspace now keeps its own support thread.

If you belong to more than one workspace, the support panel had a confusing habit: it always reopened your most recently updated conversation, no matter which workspace it belonged to. Switch from one workspace to another, open support, and you'd be looking at the *other* workspace's transcript.

Support conversations are now scoped to the workspace they were started in. Switch workspaces and the panel shows that workspace's own thread — or a fresh one if you haven't talked to support there yet. Your history still belongs to you; the active workspace just decides which conversation is in front of you.

Platform

Fixes: mark-all-read, the email sender name, and a few navigation snags

A round of fixes — every notification clears at once, emails come from BucketPilot, jobs sort by last run, and switching workspace from a bucket no longer 404s.

A batch of smaller improvements that shipped together:

  • “Mark all as read” now clears everything. It previously only touched the notifications currently on screen, so if you had more than a screenful, some stayed unread no matter how many times you clicked. It now marks every unread notification read in one go.
  • Emails come from “BucketPilot.” Notification and account emails were showing up as “noreply” in some inboxes; they now carry the BucketPilot sender name.
  • The Jobs page sorts by last run. Index, backup, and migration lists each order newest-run-first, so what ran most recently is at the top and never-run jobs sit at the bottom.
  • Your name links to Settings. Clicking your name and email at the bottom of the sidebar now opens account settings.
  • Switching workspace from inside a bucket no longer dead-ends. If you changed workspaces while viewing a bucket (or a job) that the new workspace doesn't have, you'd land on a “not found” page. Now you land on the bucket (or jobs) list instead.
Migrations

Migrations built for millions of objects — and re-syncs that finish in seconds

A migration now runs at any tree size without exhausting a worker's memory, scans a server in seconds, and re-syncs by looking only at what actually changed.

A migration used to hold two things in the worker's memory at once: the entire list of files at the source, and the record of everything it had already copied. On a tree with millions of files that adds up to gigabytes — enough to run a worker out of memory before a byte moved. Both now stream through the database instead of the worker, so a job of tens of millions of objects runs in flat, predictable memory.

Two more improvements you'll feel immediately:

  • Scanning a server is now near-instant. Instead of walking a deep tree one directory at a time, we ask the machine for its whole file list in a single pass. A scan that took minutes on a large tree now takes seconds. (Servers that don't support it fall back to the previous method automatically.)
  • Re-syncs only look at what changed. A scheduled job now asks the source for the files modified since its last run, so a re-sync where nothing changed finishes almost instantly rather than re-listing the entire tree. A full scan still runs at least daily so deletions are noticed.

Nothing about how you set up a job changes — existing jobs simply get faster and lighter on their next run.

Migrations

Copy only the files you want, by date

A migration can now copy only objects modified within a window you choose — the last N days, or between two dates.

New optional filter on the migration form. Leave it off and everything is copied, as before. Turn it on and choose either:

  • In the last N days — a rolling window, so a scheduled job always carries the recent tail and nothing older.
  • Between two dates — a fixed *from* and *to*, both days included.

It works for every source: your own servers, Amazon S3, Cloudflare R2, Google Cloud Storage, and Azure. Useful for seeding a destination with just this quarter's data, or moving a specific date range without hand-picking files.

The filter reads each object's last-modified time and never drops anything it can't evaluate, so it can only ever narrow the copy — never silently skip data.

Jobs

Migrations that recover on their own

A job interrupted by a restart now resumes by itself, and large jobs no longer fail files against a server that limits how many connections it accepts.

If the worker running a job was restarted mid-run — a deploy, a busy spike — the job was marked failed and you had to start it again by hand. Now it resumes on its own: the run is re-queued automatically, and because a re-run only copies what's still missing, it picks up where it left off rather than starting over. A job that repeatedly loses its worker still stops after a few attempts, so a genuinely broken job can't loop forever.

Two related fixes:

  • A job copying many small files could open more connections than a server was willing to accept and start failing files. Jobs now sense a server's real limit and ease off instead of failing — the run just takes a little longer.
  • Busy migrations were occasionally cut short by an over-eager internal health check that mistook "working hard" for "stuck". That's been given the headroom a long transfer needs.
Migrations

Server-to-server migrations, an order of magnitude faster

Transfers now pipeline their SFTP requests instead of waiting for one round trip at a time, and a large tree starts copying in seconds rather than minutes.

Copying between two of your own machines was bound by network latency, not by bandwidth or by our workers: every chunk waited for the previous one to be acknowledged, on both hops. On a link with 30 ms of latency that caps a transfer at roughly 1 MB/s no matter how fast the pipe is.

Transfers now keep 64 requests in flight per file, in both directions. Measured on a zero-latency loopback — the floor of the improvement, not the ceiling — throughput went from 28.9 MB/s to 66.4 MB/s with byte-identical output. Over a real internet link the gain scales with the round trips removed.

Three more things that were quietly costing whole minutes:

  • Scanning a deep tree walked one directory at a time and asked the server to describe every file it had already described. Directories are now scanned in parallel, and the live view reports Scanning /var/www — 12,480 files so far instead of a silent "Scanning remote directory…".
  • Re-runs checked the destination one file at a time. The destination is now read once, up front, and compared in memory — on a 24,000-file job that replaces 24,000 sequential round trips with a few hundred concurrent ones.
  • Server-to-server runs use 4× the thread count you pick. Those runs wait on two remote machines rather than on our workers, so the plan's number is a floor, not a ceiling: Pro copies 16 files at once, Business 32. The picker tells you what the run will actually do.

See the migration guide for how jobs work.

Migrations

Migrations copy the whole tree — symlinks and empty folders included

Two ways a server-to-server copy could silently leave holes in the destination, both closed.

If your source tree reached a directory through a symlink/var/www → /srv/app, current → releases/2026-07, the usual shapes — the listing treated it as a file, failed it, and never copied anything beneath it. Links are now followed: a linked directory is walked, a linked file is copied at its real size, and a link pointing back into the tree is skipped rather than copying the same data twice.

Empty directories appeared in no file listing at all, so they simply weren't recreated. A server destination now gets the shape of the source tree, not just its files.

If you ran a server-to-server job before today, run it again — it will copy exactly what was missed and skip everything already in place.

Jobs

Jobs that finish, and a detail page that keeps up

Runs no longer hang at 100%, the detail page updates without a refresh, and every job can be deleted from where you're standing.

A migration that hit an unreadable file could sit at running, 100% until the 24-hour reaper stopped it. The underlying cause was an error that our SSH library reports on the connection rather than on the file's stream, where nothing was listening — the transfer then waited forever on a source that would never speak again. Those files now fail as ordinary per-file errors and the run completes with them listed.

Also in this release:

  • The progress bar counts files that are finished, not files that have been picked up, so it no longer reads 100% while the last few are still transferring.
  • The job detail page polls while a run is queued or running — the status and log used to freeze until you reloaded the page.
  • Opening a job from the Jobs table used to be able to show you a different job: backup and migration ids are issued independently, so the two could collide. Links now carry the job's kind.
  • Delete is available on the Jobs table and on the job detail page, not only on the job cards.
  • The thread count, schedule, source server, paths and destination of a migration job are all editable now. Previously changing where a job pointed meant deleting it and building it again.
Analytics

Per-server activity in Analytics

Every machine your migration jobs touch, with the jobs attached to it, its runs and the bytes it moved in the last 30 days.

Analytics gains a Your servers table: one row per machine, showing the jobs attached to it (as a source or a destination), how many runs it took part in over the last 30 days, how much data moved through it, and when it last ran with the result.

This is built from data we already keep about your jobs — we don't connect to your machines to collect metrics, and nothing new is stored.

The same release adds activity cards by category — uploads, jobs and migrations, buckets, credentials and servers, security, billing — so the page reflects what the workspace has been doing, not only what it's storing.

Platform

A getting-started checklist that reads your actual account

Four steps, derived from what you've really done rather than from a tour you clicked through.

New accounts land on a dashboard panel with four steps — connect a credential, add a bucket, index it, run a job — each derived from live account state. Finish a step anywhere in the product and it ticks itself; dismiss the panel and it stays dismissed.

Each step hands off to the next: saving your first credential forwards you to bucket discovery, and importing buckets points at indexing. Sign-up no longer asks for a username either — one is minted for you and can be changed in Settings whenever you like.

Platform

"SFTP server" is now just "Server"

The machines you migrate to and from are called servers throughout the product, and the firewall address you need to allow is on screen where you need it.

The protocol was leaking into the product's vocabulary. A machine you own is a Server now — in the migration form, on the Credentials page (the tab is Servers), on the landing page, in the plan comparison and throughout the docs. The API is untouched: sourceType: "ssh", sshServerId and the rest are unchanged, and the API reference still documents them as they are.

Alongside it: jobs always connect out from one fixed address, and that address is now shown where you'd want it — in the migration form, in the Add Server dialog and in the migration guide. You can allow a single IP on your SSH port instead of opening it to the internet.

Migrations

Pull an offline copy of a bucket onto your own server

A migration job can now read from a managed bucket and write to a machine you own.

Migration jobs have always moved data into a bucket. On the Business plan they now go the other way too: pick a bucket as the source, a server as the destination, and an optional key prefix to limit what's copied.

It rides the same machinery as every other job — incremental re-runs, streamed transfers so file size isn't a limit, atomic writes that never leave a half-written file, and host-key pinning on the destination.

Bucket → bucket is deliberately not offered here: that's a backup job, which compares ETags and does it better.

Buckets

Connecting a bucket: discover or create, nothing in between

One guided flow for existing buckets, with honest handling of credentials that can't list them.

Adding a bucket used to offer two doors that did nearly the same thing. Now there are two that don't overlap: Discover finds what a credential can see, and Create Bucket makes a new one.

Credentials that legitimately can't list buckets — Google Cloud Storage, scoped R2 tokens, least-privilege AWS policies — get a guided card with an inline "add it by name" form instead of an empty list. A bucket added by name is verified before it's registered, so a typo returns a clear error rather than a bucket that appears to exist and fails on first use.

Other rough edges from the same release: a bucket deleted at the provider now says so plainly, browser uploads apply their CORS rule automatically on first use when none exists, and creating a bucket on a provider without versioning no longer offers it.

Teams

Audit log export with date ranges

Export the activity feed as CSV for the last 24 hours, 7 or 30 days, all time, or any range you pick.

The Business audit export gained a range picker: quick presets for 24 hours, 7 days and 30 days, all-time, or a custom range from the calendar. The file is named with the dates it covers, so a folder of exports stays legible.

The CSV carries timestamp, actor, action, bucket and job for every event in the workspace — including denied permission attempts. See Security & privacy.