Databricks on Google Cloud, Part 5: Storage, Cloud Storage and how Unity Catalog uses it
Where does Databricks actually keep data on Google Cloud? Cloud Storage from first principles for a lakehouse, the bucket settings that matter for Delta tables, why hierarchical namespace buckets cannot be external locations, the workspace storage buckets and default storage, storage credentials and external locations, managed storage at metastore, catalog and schema level, managed and external tables and volumes, what DROP really deletes, and the legacy DBFS.
Read this chapter as 5 illustrated mini-books
Flip pages horizontally like a hardcover book, or swipe up like a reel to move through all 5 topics (19 animated pages).
In Part 4 we followed
the packets: how serverless compute reaches your data and the internet, and how you control what it
can reach. This part follows the bytes. When Acme’s distribution centres drop a delivery file, when
a pipeline writes a Delta table, or when an analyst runs CREATE TABLE in a notebook, which bucket
do the files land in, whose project is that bucket in, and what happens to them when someone types
DROP?
Storage is the part of Databricks on Google Cloud that confuses people most, and in my experience it confuses both audiences of this series for different reasons. Databricks people from AWS or Azure expect an S3 bucket or an Azure storage account with a hierarchical namespace, and find neither quite where they expect it. Google Cloud people know Cloud Storage well, but have not met Unity Catalog’s vocabulary: storage credentials, external locations, managed locations, managed and external tables, and volumes. Several of these have similar names and mean very different things.
I start with what Cloud Storage gives a lakehouse and the bucket settings that matter for Delta
tables, including why Databricks does not support hierarchical namespace buckets for external
locations. Then I go through every kind of storage a workspace uses, how Unity Catalog connects to a
bucket, where Acme’s tables and volumes land, and what DROP really deletes and when. I finish with
DBFS, a bucket layout I would use, and the errors I see most often. All status claims are as of
October 2026, and the ones that changed since are updated and dated (last checked 8 November 2026).
If you prefer to watch first, here is the heart of this part as a short animated explainer.
Video: where Databricks keeps data on Google Cloud, and what DROP really deletes, in three minutes. The narration is a synthetic voice; the script and animation are mine.
What are the key ideas?
Eight terms carry the whole post. The first two are Google Cloud terms, the rest are Databricks terms.
- Bucket. A named container for objects in Cloud Storage,
Google Cloud’s object storage. It belongs to one Google Cloud project and has a location: a
region, a dual-region or a multi-region. Acme’s data bucket is
gs://acme-lake-supplyin the projectacme-lake, ineurope-west2. - Object. One file in a bucket, found by its name (its key). Prerequisite 1 showed what an object is made of and why objects cannot be edited in place.
- Workspace storage. The storage that belongs to a workspace itself, not to any table: notebook revisions, job results, logs and the legacy file system. In a classic workspace it is two or three buckets in the workspace project; in a serverless workspace it is default storage.
- Default storage. Object storage that Databricks provisions and manages inside your Databricks account, never in your Google Cloud project.
- Storage credential. A Unity Catalog object that holds the identity Databricks uses to reach a bucket. On Google Cloud that identity is a Google service account that Databricks generates.
- External location. A Unity Catalog object that pairs a Cloud Storage path with a storage credential, so that Unity Catalog can govern everything under that path.
- Managed storage location. A path, set on a metastore, a catalog or a schema, where Unity Catalog stores managed tables and managed volumes and controls their files completely.
- External table and external volume. A table or volume whose files live at a path you choose inside an external location. Unity Catalog governs access, but you control the files.
The diagram shows where each of these sits for Acme. Keep it in mind: every storage question in this post becomes which account, which project, which bucket, and which path?
where Databricks keeps data on Google Cloud: Unity Catalog in the control plane holds the storage credential, the external location and the catalog's managed location; a classic workspace has workspace storage buckets in the workspace project; Acme's own bucket gs://acme-lake-supply in the acme-lake project holds managed tables, an external volume and an external table; classic and serverless compute both reach that bucket through the credential's service account, and serverless workspaces keep their own storage in default storage
What does Cloud Storage give a lakehouse?
The short answer is: everything a table format needs, as long as you choose a few settings well. Prerequisite 2 introduced Cloud Storage as one of the services this series uses; here I go one level deeper.
A bucket lives in a project and has one location. There is no extra layer between the project
and the bucket, unlike the storage account that sits between an Azure subscription and its
containers. The bucket location is chosen
when you create the bucket. A region such as europe-west2 (London) keeps data in one region,
copied synchronously across its zones. A dual-region is a specific pair of regions with data
copied asynchronously between them, and an optional turbo replication feature that replicates
within 15 minutes. A multi-region such as EU spreads data across a large geographic area. The
location type decides price and data transfer charges, which is why it matters for Databricks: the
Databricks guide to Cloud Storage external locations
says the bucket “should be in the same region as the workspace” to avoid egress charges.
A storage class sets the price of keeping and reading an object. The storage classes are Standard, Nearline, Coldline and Archive. The colder classes cost less to store but have a minimum storage duration (you pay for at least 30, 90 or 365 days even if you delete earlier) and a retrieval fee on every read. One thing surprises people from AWS: all classes, including Archive, return data in milliseconds, with no restore step.
choosing where a lakehouse bucket lives and which storage class it uses: a region such as europe-west2 copies data across zones and has no outbound charge when compute in the same region reads it, which is Acme's choice; dual-regions and multi-regions copy across regions at a higher price; Standard has no minimum duration or retrieval fee, while Nearline, Coldline and Archive have 30, 90 and 365 day minimums and retrieval fees but still answer in milliseconds
Every read sees the latest write. Cloud Storage offers strong global consistency for object read-after-write, read-after-delete and object listing. “Consistency” here means how soon a change is visible: strongly consistent means that as soon as a write succeeds, every reader everywhere sees it, and a listing immediately includes the new object. Delta Lake depends on this. A table is a set of Parquet files plus a log, and a commit is the creation of the next log file. If a reader could miss a log file that had just been written, it would read an old version of the table without knowing.
The commit itself uses a precondition. Prerequisite 1
showed how a Delta writer creates _delta_log/00000000000000000002.json with a
generation-match precondition of 0,
which tells Cloud Storage to create the object only if no object with that name exists. That
put-if-absent is what lets two writers race safely: only one can create version 2. Keep this in
mind for the hierarchical namespace question below.
A simple way to picture both guarantees. Imagine a club’s guest book with numbered pages, kept at
the front desk. Two rules make it trustworthy. First, the moment someone writes on page 2, anyone who
opens the book sees page 2; nobody is handed an old photocopy. Second, the desk only lets you write
on a page that is still blank, so if two members arrive at once to claim page 2, one gets it and the
other is told to read the book again and try page 3. The book is the _delta_log folder, each page
is one numbered log file, the first rule is strong consistency and the second is put-if-absent,
enforced by Cloud Storage acting as the desk.
How does this apply to Acme? Suppose (a hypothetical moment) Acme’s nightly pipeline and an
analyst’s backfill both commit to supply_gold.supply_service at the same time, and both try to
create _delta_log/00000000000000000002.json. Cloud Storage lets exactly one create succeed. The
other writer’s precondition fails, so it re-reads the log, sees version 2, and tries again as
version 3 if its change does not conflict. A dashboard query that starts a second later sees version
2 straight away, because the listing already includes the new log file. Where people get tripped up
is assuming object storage is only eventually consistent, so a reader might miss a fresh commit; on
Cloud Storage that cannot happen.
Request rates scale, but gradually. The request rate guidelines say a bucket starts with roughly 1,000 object writes and 5,000 object reads per second and then autoscales by spreading load across more servers, which takes minutes. Above those rates, ramp up no faster than doubling every 20 minutes, and avoid strictly sequential object names, which concentrate load on one range of the bucket’s index. In practice Spark writes files with random suffixes, so this matters most for a big first load into a new bucket.
Which bucket settings matter for Delta tables?
The rule behind every choice: on a table path, only Delta decides which files to delete. Any bucket feature that deletes files on its own timer, or refuses to let Delta delete them, fights the Delta log.
A Cloud Storage bucket has many settings. Most are about security or cost, and a few can break a Delta table if you set them without thinking about how Delta works.
Picture a library whose catalogue lists which books are current. The librarian weeds out books the
catalogue no longer lists. Now add a cleaner who throws away every book older than 90 days without
reading the catalogue: readers soon find catalogue entries that point at empty shelves. Add a sign
saying no book may leave the building for seven years, and the librarian can no longer weed at all.
A bin by the door that keeps thrown-out books for a week before shredding them is harmless, but the
space it takes is paid for. The catalogue is the Delta log, the librarian is VACUUM, the cleaner
is a lifecycle Delete rule, the sign is a retention policy and the bin is soft delete.
How does this apply to Acme? On gs://acme-lake-supply, the table paths (managed/ and
supply_service/) get no lifecycle rules, no retention policy and no Autoclass, because a log reads
them. The landing/ path holds raw files that no log points at, so the cleaner is welcome there.
The remaining settings are about who may reach the bucket at all and where it lives. Here is each
one with what I choose for a bucket that holds Databricks tables.
Cloud Storage bucket settings and what to choose for a Databricks data bucket: same region as the workspace, hierarchical namespace off because Unity Catalog external locations do not support it, uniform bucket-level access on, public access prevention enforced, soft delete chosen deliberately because deleted Delta files are billed while retained, Object Versioning usually off, no lifecycle rules on table paths, Autoclass only for landing data, and no retention policy on table buckets
A few of these need a sentence more.
- Uniform bucket-level access turns off ACLs (access control lists, the older per-object permission system) so that only IAM decides who can reach the bucket. With Unity Catalog in front, almost nobody should have IAM on the bucket at all, so one permission system is plenty. It is also required for hierarchical namespace.
- Public access prevention
stops anyone from granting
allUsersorallAuthenticatedUsersaccess. Enforce it on every data bucket, ideally through the organisation policy. - Soft delete is on by default for new
buckets, with a 7-day retention that you can set between 7 and 90 days, or 0 to turn it off. A
soft-deleted object cannot be read but can be restored, and you pay for it while it is retained.
Since 4 November 2026 you can also
pause hard deletions on a
bucket: soft-deleted objects are then kept “indefinitely, regardless of their soft delete
retention durations”, and billed, until you resume. You can pause or resume at most once every
24 hours, and a bucket cannot be deleted while paused. For example, if someone runs a wrong
cleanup job on
acme-lake-supplyon a Friday and the restore will take longer than the 7-day window, a storage admin pauses hard deletions first, restores, then resumes. Delta tables delete files all the time:VACUUM, as Prerequisite 1 explains, removes data files that the table no longer references. Google’s own page warns that soft delete on buckets of temporary data “can result in significantly increased storage costs”. My view: keep the default on table buckets until you see the bill, and decide per bucket with your recovery policy in mind, because Delta time travel already covers most mistakes inside the retention period. - Object Versioning keeps a noncurrent copy each time an object is replaced or deleted. Delta never replaces files, it adds new ones, so versioning mostly keeps deleted files alive and billed. I leave it off on table buckets.
- Lifecycle rules delete objects or
change their storage class by age, prefix and other conditions. A
Deleterule does not know which Parquet files a Delta log still points to, so on a table path it can delete live data and leave a table whose log refers to files that no longer exist. Use them onlanding/paths for raw files, never on table paths. Let Delta and predictive optimization delete table files. - Autoclass moves objects that are not read to colder classes automatically, Nearline by default. That suits a landing bucket of raw files read once. On a table that is read unevenly it adds retrieval fees, so I avoid it there.
- A retention policy means objects can
only be deleted or replaced once they are older than the retention period, and Bucket Lock
makes the policy permanent. On a table bucket,
VACUUMand Unity Catalog’s purge would fail to delete files. Keep retention policies for archive buckets.
Here is how Acme creates its data bucket with the gcloud CLI. The flags come from the
bucket creation guide and the
gcloud storage buckets create reference;
I have not run this exact command.
# Flat namespace (no --enable-hierarchical-namespace), regional, IAM only, never public
gcloud storage buckets create gs://acme-lake-supply \
--project=acme-lake \
--location=europe-west2 \
--default-storage-class=STANDARD \
--uniform-bucket-level-access \
--public-access-prevention \
--soft-delete-duration=7d
# Labels for cost reports: who owns it and what it holds
gcloud storage buckets update gs://acme-lake-supply \
--update-labels=env=prod,domain=supply,owner=data-platform
In Terraform, the equivalent google_storage_bucket resource sets location = "europe-west2",
uniform_bucket_level_access = true, public_access_prevention = "enforced" and, if you want to
be explicit, a soft_delete_policy block with retention_duration_seconds = 604800 (7 days), per
the provider documentation.
What is hierarchical namespace, and why can Databricks not use it?
Hierarchical namespace makes folder renames atomic; Delta never renames a folder, so Databricks asks for flat buckets and loses nothing. This is the storage question I hear most from teams that come from Azure, so it deserves its own section. Where they get tripped up is expecting a Google Cloud copy of the Azure Data Lake Storage setup they know. On Google Cloud, Unity Catalog external locations need a flat bucket.
What it is. By default a Cloud Storage bucket has a flat namespace: there are no folders,
only object names that happen to contain slashes. A bucket created with
hierarchical namespace (HNS) has real
folders, which are resources of their own. Folders can be listed, deleted and, most importantly,
renamed atomically: renaming landing/ to archive/ is one operation that either happens
completely or not at all, instead of a copy and delete of every object underneath. Google also
documents up to eight times higher initial queries per second for reads and writes.
Why would anyone want that? Older Hadoop and Spark jobs, and many AI training tools, assume a file system. Some of them commit their output by writing to a temporary folder and renaming it, which on a flat bucket is slow and not atomic. People who know Azure Databricks know the same idea as Azure Data Lake Storage with a hierarchical namespace, which is why they ask for it on Google Cloud.
What it costs you. HNS must be chosen when the bucket is created and can never be changed. It requires uniform bucket-level access. And it does not support Object Versioning, Bucket Lock, Object Retention Lock, object holds, object ACLs or cross-bucket replication.
What Databricks says. The Databricks documentation is short and clear: “Google Cloud Storage hierarchical namespace (HNS) is not supported with external locations. Disable hierarchical namespace before creating an external location.” Because an HNS setting cannot be changed, in practice “disable” means using a different, flat bucket. And since every managed storage location for a catalog or schema must sit inside an external location, HNS buckets are out for Unity Catalog’s managed storage too.
Why that costs a Delta table nothing. Databricks does not publish its reasons, so what follows is my reading of how Delta works, not a quote. The headline feature of HNS is the atomic folder rename, and Delta never renames anything. A commit writes new Parquet files under new names and then creates one small log file with put-if-absent. Readers find the files of a version by reading the log, not by listing folders. So a flat bucket already gives Delta everything its commit protocol needs.
A filing room makes the difference concrete. In a flat room there are no drawers, only files with a
label such as landing/report-17. Renaming landing to archive means relabelling every file, one
at a time, and anyone who walks in halfway sees a mix of old and new labels. In a room with real
drawers (hierarchical namespace) you change the sign on the drawer once. Now picture a clerk who
never relabels anything: to change a record, they file new pages under new names and then add the
next numbered line to the index, which the desk accepts only if that line number is still free.
That clerk is Delta, and the drawer sign is of no use to them.
why hierarchical namespace does not help Delta tables: in a flat bucket, renaming the folder landing/ to archive/ means copying and deleting every object, while a bucket with hierarchical namespace renames the folder in one atomic operation; a Delta commit never renames anything: it writes new Parquet files, then creates the next _delta_log JSON file only if no object with that name exists (generation-match precondition 0); success makes the version visible, and a writer that loses the race re-reads the log and retries
How does this apply to Acme? gs://acme-lake-supply is created without
--enable-hierarchical-namespace, and supply_service commits by adding the next log file, exactly
like the clerk. Nothing in Acme’s lakehouse depends on renaming a folder.
My advice when a team asks for HNS: keep Databricks data in flat buckets and govern folders with Unity Catalog grants on external locations, volumes and tables, not with folder permissions in the bucket. If another tool genuinely needs HNS, give it its own bucket and share data with it through tables, not through a shared folder tree. Moving existing data between buckets is a migration task, and Part 10 covers it.
What are managed storage, external storage and default storage?
Who owns the bucket and who controls the files are two separate questions. Workspace storage and default storage hold the workspace’s own files; managed and external storage hold your tables and volumes, and both normally sit in a bucket you own. The most common misreading is that a “managed” table lives in Databricks’ storage. On Google Cloud a managed table usually lives in your bucket: managed means Unity Catalog controls its files, not that Databricks hosts them. Only default storage is in Databricks’ account.
With Cloud Storage itself covered, we can look at how Databricks uses it. Two questions untangle it: how many buckets are there, and in whose project? Then: where does each table or volume actually end up?
How many buckets, and whose are they? A common way to put it is “a classic workspace has four buckets and a serverless workspace has one”. That is close, with two refinements.
- A classic workspace gets two or three workspace storage buckets that Databricks creates in
the workspace’s project: the DBFS root, the system data bucket and, only if Unity Catalog was
enabled automatically (as it is for most new workspaces), a bucket for the workspace catalog. On
top of those you normally create one or more buckets of your own for your data, such as Acme’s
gs://acme-lake-supply. So “four” is typical, but it is three of Databricks’ plus however many of yours. - A serverless workspace gets no Databricks-created buckets in your project at all. Its workspace storage lives in default storage, in Databricks’ account. Your own buckets are optional, though any company with real data will have at least one.
Here is the whole picture side by side, with Acme’s names. Notice that the buckets Acme creates sit in Acme’s project in both cases, and Unity Catalog reaches them the same way: through the storage credential’s service account, which has IAM roles on each bucket.
the complete storage picture for a classic and a serverless workspace: in both, Unity Catalog in Databricks' control plane holds the storage credential and the external location, and the credential's service account has IAM roles on the buckets Acme creates for catalog managed storage, lakehouse data and landing files; a classic workspace also has three buckets that Databricks creates in Acme's project (system data, the legacy DBFS root and the workspace catalog), while a serverless workspace keeps that workspace storage and its workspace catalog in default storage in Databricks' account
Workspace storage and default storage are not the same thing. It is tempting to think that default storage is just another name for the classic workspace buckets. It is not.
- Workspace storage (classic) is the two or three buckets in your project. They hold the workspace’s own files: the legacy DBFS root, workspace system data, and the managed files of the workspace catalog.
- Default storage is “a fully managed object storage platform that provides ready-to-use storage in your Databricks account”. It lives in Databricks’ account, never in your project. “Serverless workspaces use default storage for internal and workspace storage, and for the default catalog that gets created with the workspace.” Classic workspaces use it only behind the scenes, for features such as Clean Rooms and data classification. Only serverless workspaces can create catalogs on it, and “classic compute (any compute that is not serverless) cannot interact with data assets in default storage”.
What is the third bucket for? It is the managed storage location of the workspace catalog.
When Unity Catalog is enabled automatically, “Databricks creates a workspace catalog with a managed
storage location”. That catalog “typically shares its name with your workspace name”,
it is the workspace’s default catalog, and “all workspace users can create managed tables and
volumes in the default schema of the workspace catalog”. So if an analyst runs
CREATE TABLE test_orders (...) without naming a catalog, the table goes into the workspace catalog:
in a classic workspace its files land in that third bucket, and in a serverless workspace in default
storage.
Managed and external, first as a cloakroom. Picture a venue with a doorman. The venue has a cloakroom: you hand over your coat, the attendant chooses the hook, and you get it back by your ticket, never by walking to a hook yourself. Hand back the ticket and leave, and the coat is kept for a week in case you return, then disposed of. Some guests instead have their own hook in their own hallway. The doorman decides who may enter that hallway, but if a guest’s name comes off the doorman’s list, the coat stays on the hook. The doorman carries one key that opens the hallways, and a list of which hallway that key may open.
Mapped to Unity Catalog: the cloakroom is a managed location, a coat handed in is a managed table or volume, the ticket is its name, and the week of grace is the recovery window. The private hook is an external table or volume at a path you choose. The doorman is Unity Catalog, his key is the storage credential, and “this key opens this hallway” is an external location.
How does this apply to Acme? Four more terms explain where everything else goes, each with Acme’s name for it.
- A storage credential holds the identity Databricks uses to reach a bucket. On Google Cloud it is
“a Databricks-generated Google Cloud service account”, and you grant that service account IAM
roles on your bucket. Acme’s credential is called
acme-lake-sc. - An external location “combines a cloud storage path with a storage credential that authorizes
access to the specified path”, for example
gs://acme-lake-supply/withacme-lake-sc. Acme calls itacme_lake. - A managed storage location is a path that you set on a
metastore (optional), a catalog or a schema; for a catalog or schema it must sit inside an
external location. Managed tables and managed volumes created
there are stored under paths that Unity Catalog chooses (
<storage-root>/__unitystorage/...), and you use them by name only: “path-based access to Unity Catalog managed tables is not supported”. When you drop a managed table, “Databricks deletes the data files in cloud storage after the recovery period expires (default 7 days)”. Databricks recommends assigning managed storage “at the catalog level for logical data isolation”. - An external table or external volume is one you create at a path you choose inside an external location. Dropping it removes only the Unity Catalog entry: “for external tables, you must manually delete the files from your storage bucket”, and an external volume’s data “remains in cloud storage when you drop the volume”.
A volume, if the word is new, is governed storage
for files that are not tables, such as Acme’s raw delivery JSON files, reached by a path like
/Volumes/acme_supply/supply_bronze/landing/. A managed volume keeps its files in Unity
Catalog’s managed storage; an external volume governs an existing bucket path, and the files
stay there if you drop the volume.
In cloakroom terms: acme-lake-sc is the doorman’s key, acme_lake says it opens
gs://acme-lake-supply/, a managed table such as supply_bronze.deliveries_raw is a coat handed in
(Unity Catalog picks the path, Acme uses the name), and the external table supply_service is a
coat on Acme’s own hook at gs://acme-lake-supply/supply_service/.
For a managed object, the location is resolved from the bottom up: the schema’s managed location if it has one, otherwise the catalog’s, otherwise the metastore’s.
where Unity Catalog puts a new table or volume: if you give a path inside an external location, it is external and stays at that path; otherwise it is managed and goes to the schema's managed location, or else the catalog's, or else the metastore's
Putting the four kinds of storage side by side:
the four kinds of Databricks storage on Google Cloud: workspace storage buckets in your project for classic workspaces, default storage in Databricks' account for serverless workspaces, managed storage locations in your own bucket for managed tables and volumes, and external paths for external tables and volumes; where each lives, who owns it, what goes there and what happens when you drop an object
What can default storage not do, and when should you use your own bucket?
Default storage is zero-setup storage that only Databricks serverless compute can open. Anything that needs to reach the files from outside Databricks serverless belongs in your own bucket.
Think of a hotel room safe. There is nothing to install and it works the moment you arrive, but it opens only through the hotel’s keypad, and you cannot send a courier with their own key to collect from it. A safe deposit box at your own bank takes an afternoon to arrange, but you decide who may collect. The hotel is your Databricks account, the keypad is serverless compute, the couriers are classic clusters, BigQuery and other engines, and the bank box is a bucket in your project.
Default storage is the easiest place to start in a serverless workspace, because there is nothing to set up. That convenience comes with rules, and they decide whether a catalog belongs there or in a bucket you own. All of them come from the default storage page (checked 8 November 2026) and the serverless workspaces page.
How it behaves.
- Only serverless workspaces can create catalogs on it. You run
CREATE CATALOGwithout aMANAGED LOCATION, or tick “Use default storage” in Catalog Explorer. - Only serverless compute can use it. “All interactions with default storage require serverless, Unity Catalog-enabled compute.” Serverless compute means compute Databricks runs in its own account: serverless notebooks, jobs, Lakeflow pipelines and SQL warehouses.
- It holds managed tables and managed volumes, with the same Unity Catalog privileges as any other catalog.
- It is bound to its workspace by default. Workspace-catalog binding (a setting that limits which workspaces may use a catalog) can open it to other workspaces, even classic ones, but they “must use serverless compute to access data in the catalog”.
- Dropped data goes quickly. After a drop, the files are kept for the 7-day recovery window and then “permanently deleted (purged) within 48 hours”. Billing stops when the window ends.
- Encryption and billing work differently from your buckets. If you use customer-managed keys,
the managed services key “encrypts data in the workspace catalogs and the workspace root storage”.
Usage appears in
system.billing.usage, and because default storage is “not deployed in your cloud account, your cloud-provider credits or discounts do not apply to this usage”.
What it cannot do.
- Classic compute cannot reach it. “Classic compute (any compute that is not serverless) cannot interact with data assets in default storage.”
- Outside engines cannot read the files. External Iceberg and Delta clients “cannot directly access the underlying metadata, manifest list, and data files”, and credential vending (Unity Catalog handing an outside engine short-lived credentials so it can read the files itself) “is not supported”. BI tools still work through the ODBC and JDBC drivers, and volumes through the Files API.
- BigQuery cannot see it. Google’s cross-cloud setup page supports only Unity Catalogs “that use an external location”, not those on default storage. Part 8 covers that path.
- Sharing is narrower. OpenSharing (formerly Delta Sharing) can share tables to any recipient, but other assets only to Databricks recipients on the same cloud using serverless compute, never tables with partitioning, and never to external Iceberg clients.
- One network catch. With front-end Private Service Connect (private connectivity from users to the workspace), ODBC queries on default storage tables that return more than about 5 MiB fail, because the driver’s Cloud Fetch optimisation (downloading large results straight from storage) does not support that path yet.
How does this apply to Acme? A worked example. Suppose Acme’s marketing analysts get a serverless workspace and someone
creates a sandbox catalog on default storage (a hypothetical; Acme’s real data stays in
acme_supply):
-- Serverless workspace: no MANAGED LOCATION, so the catalog uses default storage
CREATE CATALOG acme_sandbox;
CREATE TABLE acme_sandbox.default.campaign_test (campaign STRING, clicks INT);
Here is what happens when four different things try to use that table:
1. Serverless notebook: SELECT * FROM acme_sandbox.default.campaign_test
-> works; serverless compute, Unity Catalog grants checked as usual
2. Power BI over the Databricks ODBC driver
-> works; the query runs on a SQL warehouse and only results travel
3. Nightly job on a classic job cluster reading the same table
-> fails; classic compute cannot interact with default storage
4. BigQuery analyst querying it through cross-cloud Lakehouse access
-> not available; only catalogs on an external location are supported
The first two work because Databricks serverless compute does the reading. The last two need
something outside Databricks serverless to reach the data, and default storage does not allow that.
If Acme wants the table in BigQuery or in a classic job, it belongs in a catalog like acme_supply,
whose managed location is Acme’s own bucket.
who can reach a catalog on default storage compared with a catalog in your own bucket: serverless compute, BI tools over ODBC or JDBC and the Files API reach both; classic clusters, BigQuery cross-cloud access, external Iceberg or Delta clients and credential vending reach only the catalog in your own bucket
Which do I need? Four quick tests, decided per catalog rather than per workspace:
- A training catalog that only serverless notebooks touch: default storage is fine.
- Gold tables that BigQuery analysts query through cross-cloud access: your own bucket.
- Data that a classic job cluster still reads overnight: your own bucket.
- Regulated data where the customer must control retention, lifecycle rules and bucket audit logs: your own bucket.
As a litmus test, asked per catalog with the cheapest option first:
1. Will only Databricks serverless compute ever read or write it? no -> your own bucket
2. Must you control retention, lifecycle rules or bucket audit logs? yes -> your own bucket
3. Only if question 1 is yes and question 2 is no: default storage, with no setup at all
My view: start on default storage if it gets a team moving, but put anything shared, regulated or read outside Databricks in your own bucket from day one. Changing a catalog’s managed location later “affects only new managed tables and volumes created after the change”, as Can you move a managed location later? explains.
What does a classic workspace put in your project?
Part 1 listed the workspace storage buckets among the things workspace creation leaves behind. Here they are in depth.
A classic workspace has three workspace storage buckets in your project. They belong to the workspace, not to any table: Databricks creates them in the workspace’s Google Cloud project when the workspace is created, one set per workspace.
databricks-<workspace id>: the root storage for DBFS (Databricks File System), a legacy file system underdbfs:/that Databricks no longer recommends for storing data.databricks-<workspace id>-system: workspace system data, “internal data generated by Databricks features”, such as job run results, SQL query results, notebook revisions and cluster logs.- A third bucket, if the workspace was enabled for Unity Catalog automatically, holding the managed
storage of the workspace catalog, where all users of the workspace can create assets in its
defaultschema. The documentation does not give this bucket a fixed name.
The documentation is blunt about them: do not delete or modify them, because “if workspace storage is deleted, the workspace cannot be recovered”. (The bucket names come from the workspace creation guide.) Your own table data normally lives elsewhere: in buckets you register in Unity Catalog as external locations (a bucket path plus a storage credential, which for Cloud Storage is a service account), or in the managed storage location you set for a metastore, catalog or schema. A serverless workspace has none of these buckets in your project. It uses default storage, “a fully managed storage location for internal workspace system data and Unity Catalog data assets”, and can still connect to your own buckets. What are managed storage, external storage and default storage? puts all of these side by side.
the three workspace storage buckets of a classic workspace and what is in each: databricks-<workspace-id> is the legacy DBFS root, readable by every workspace user and deprecated for data; databricks-<workspace-id>-system holds workspace system data such as notebook revisions, job run details, command results and Spark logs; a third bucket holds the managed storage of the workspace catalog when Unity Catalog was enabled automatically; never delete or modify them, review their IAM members, enable Data Access audit logs, and disable DBFS root and mounts once nothing uses them
How should you look after them? The workspace creation guide asks for three things, and they are worth doing on day one.
- Review who can reach them. Open each bucket’s permissions and check every member. Some entries, such as the one named “Databricks service account for workspace”, carry IAM Conditions (rules that limit a grant to certain resources) that restrict them to certain buckets; the console does not evaluate the condition, so it can show access that does not really exist. When you grant Cloud Storage roles at project level or above, use IAM Conditions to exclude the Databricks buckets, and prefer narrow roles such as Storage Viewer over Storage Admin.
- Turn on Data Access audit logs for these buckets. Databricks “strongly recommends” it, while noting that it adds to the Google Cloud bill.
- Keep business data out of them. The DBFS root bucket “is not intended for storage of production customer data”.
Two more facts help in operations. The workspace admin settings have a Storage section where you can permanently purge deleted notebooks and other workspace objects, notebook revision history and cluster logs, which is how you remove that data from workspace storage on purpose. And if you use customer-managed keys, the workspace storage key encrypts these buckets; that belongs to Part 6.
What is DBFS, and why is it legacy?
DBFS root is a shared drawer that everyone in the workspace can open; a volume is a locked cabinet with a sign-out log. That difference in governance is why Databricks deprecated DBFS.
DBFS stands for Databricks File System: the layer that lets code on a cluster use paths such as
dbfs:/FileStore/report.csv as if cloud storage were a local disk. It has two parts. The DBFS
root is the dbfs:/ area backed by the databricks-<workspace id> bucket. DBFS mounts are
the dbfs:/mnt/<name> paths that attach other buckets with stored credentials.
Both are deprecated. The DBFS page says “Both DBFS root
and DBFS mounts are deprecated and not recommended by Databricks. New accounts are provisioned
without access to these features.” The reason is governance: “the DBFS root is accessible to all
users in a workspace”, so anything stored there is readable by everyone, and neither the root nor a
mount goes through Unity Catalog’s grants, audit or lineage. Volumes replace both. The dbfs:
scheme itself lives on, because dbfs:/Volumes/... is an accepted way to write a volume path.
In office terms: a shared drawer is convenient, but nobody can tell who took a file or stop a colleague from reading it. A locked cabinet with a sign-out log answers both questions. The drawer is the DBFS root (and a mount is a drawer wired to another room), the cabinet is a volume, the lock is Unity Catalog’s grants, and the sign-out log is its audit and lineage.
How do you switch it off? There are two settings.
- The account setting Disable legacy features creates new workspaces without DBFS root and mounts, the Hive metastore, no-isolation shared clusters and Databricks Runtime versions before 13.3 LTS. As of 8 November 2026 the page marks the setting deprecated: “Starting October 8, 2026, Databricks provisions all new workspaces in all accounts without access to legacy features”, and the setting “is enabled and locked, and you can’t change it”. So any workspace Acme creates now starts without them; only older workspaces need the next step.
- For an existing workspace, an admin can
disable DBFS root and mounts.
After that every interface blocks them (UI, APIs, CLI and FUSE, the local file path), reads and
writes fail with errors such as
Public DBFS root is disabled, mounts andFileStorestop working, and runtimes below 13.3 LTS are disabled. Volumes keep working.
So the order for an older workspace is: move files and libraries to volumes and workspace files, point Hive metastore tables at Unity Catalog, then flip the setting. How does this apply to Acme? Acme’s workspace was created recently, so it never had DBFS root access in the first place.
How do you connect Unity Catalog to a bucket?
Unity Catalog holds the only key to the bucket, and people ask Unity Catalog, never the bucket. Everything in this section sets up that one arrangement.
Picture an office building whose storeroom belongs to the landlord. Instead of cutting a key for every employee, the landlord gives one key to the facilities firm, and the facilities desk keeps a list of who may ask for what and logs every request. The landlord’s lock only knows about the firm; the desk’s list knows about people. The landlord is Google Cloud IAM on the bucket, the firm’s key is the storage credential’s service account, “this key opens this storeroom” is the external location, and the desk with its list and log is Unity Catalog with its grants and audit. Where teams go wrong is cutting extra keys: granting analysts a storage role on the bucket “so their queries work”. Queries never need it, and it lets people walk past the desk, as the end of this section explains.
Part 2
explained why this path is keyless: Databricks creates the service account in its own project and
impersonates it, so no JSON key ever exists. How does this apply to Acme? Here are the five steps in
full, on Acme’s bucket: gs://acme-lake-supply is the storeroom, acme-lake-sc the firm’s key,
acme_lake the pairing, and groups such as supply-engineers are on the desk’s list.
connecting Unity Catalog to Acme's bucket in five steps: create the storage credential acme-lake-sc in Databricks, which generates a Google service account in Databricks' project with no key; copy its email; grant it Storage Legacy Bucket Reader and Storage Object Admin on gs://acme-lake-supply, plus the file events role; create the external location acme_lake for the bucket with that credential and test it; grant Unity Catalog privileges to groups; from then on Sam's queries are checked by Unity Catalog and the credential's service account reads the bucket
Step 1: create the storage credential. In Catalog Explorer, choose Create a credential and the type GCP Service Account, or use Terraform. You need the CREATE STORAGE CREDENTIAL privilege on the metastore, which account admins and metastore admins have by default. Two options are worth knowing: Limit to read-only use, which makes every external location built on this credential read-only, and workspace binding, which lets only chosen workspaces use the credential. The storage credential management page notes that administrators often bind production credentials to production workspaces.
Step 2: copy the service account email. Databricks shows it when the credential is created. It is a Google service account in Databricks’ project; you only grant it roles.
Step 3: grant it roles on the bucket. This is the only change on the Google Cloud side. The
external location guide
asks for two roles on the bucket: Storage Legacy Bucket Reader (roles/storage.legacyBucketReader,
which lets it list objects and read bucket metadata) and Storage Object Admin
(roles/storage.objectAdmin, which lets it read, write and delete objects). With the gcloud CLI:
UC_SA=<email shown when the credential is created>
for ROLE in roles/storage.legacyBucketReader roles/storage.objectAdmin; do
gcloud storage buckets add-iam-policy-binding gs://acme-lake-supply \
--member="serviceAccount:$UC_SA" --role="$ROLE"
done
The same page recommends also granting the permissions for file events, which the next section
explains: a custom role on the bucket’s project with the pubsub.subscriptions.* and
pubsub.topics.* permissions it lists plus storage.buckets.update, granted to the same service
account, and the Pub/Sub Publisher role for the project’s Cloud Storage service agent. The Pub/Sub
API must be enabled in that project.
Step 4: create the external location. You need CREATE EXTERNAL LOCATION on both the
metastore and the credential. Paths may contain only standard ASCII characters. Databricks tests
access when you create it, and you can check by listing the path from a notebook with
dbutils.fs.ls('gs://acme-lake-supply/').
Step 5: grant Unity Catalog privileges. Nobody can use an external location until you grant privileges on it. The ones that matter, from the privileges reference, are CREATE MANAGED STORAGE (use a path here as a managed location for a catalog or schema), CREATE EXTERNAL TABLE and CREATE EXTERNAL VOLUME, and READ FILES and WRITE FILES (read or write files directly by path). Grant them to groups, never to individuals.
Steps 4 and 5 in SQL look like this. I have not run these exact statements:
-- Step 4: register the bucket as an external location
CREATE EXTERNAL LOCATION IF NOT EXISTS acme_lake
URL 'gs://acme-lake-supply/'
WITH (STORAGE CREDENTIAL `acme-lake-sc`)
COMMENT 'Acme supply lake, europe-west2';
-- Step 5: who may do what with it
GRANT CREATE MANAGED STORAGE ON EXTERNAL LOCATION acme_lake TO `data-platform-admins`;
GRANT CREATE EXTERNAL TABLE, CREATE EXTERNAL VOLUME ON EXTERNAL LOCATION acme_lake TO `supply-engineers`;
GRANT READ FILES ON EXTERNAL LOCATION acme_lake TO `supply-engineers`;
The backticks around acme-lake-sc are needed because the name contains hyphens. In Terraform, the
whole chain is three Databricks resources and one Google resource, following the
databricks_storage_credential
and databricks_external_location
documentation (not run here):
resource "databricks_storage_credential" "lake" {
name = "acme-lake-sc"
databricks_gcp_service_account {} # Databricks creates the service account
}
resource "google_storage_bucket_iam_member" "lake" {
for_each = toset(["roles/storage.legacyBucketReader", "roles/storage.objectAdmin"])
bucket = "acme-lake-supply"
role = each.value
member = "serviceAccount:${databricks_storage_credential.lake.databricks_gcp_service_account[0].email}"
}
resource "databricks_external_location" "lake" {
name = "acme_lake"
url = "gs://acme-lake-supply/"
credential_name = databricks_storage_credential.lake.id
depends_on = [google_storage_bucket_iam_member.lake]
}
Users never get roles on the bucket. This is the point of the whole design. The Unity Catalog
cloud storage overview
warns: “Do not give non-Unity Catalog identities storage-level access to Unity Catalog managed tables
or volumes.” Anyone with direct bucket access can bypass Unity Catalog’s grants, is invisible to its
audit and lineage, and can corrupt tables by writing outside Databricks. The only identity with IAM
on gs://acme-lake-supply should be the credential’s service account.
A side note for completeness: on Google Cloud, Unity Catalog can also register Amazon S3 buckets for cross-cloud read-only access and Cloudflare R2 buckets, which the documentation positions mainly for sharing data without egress fees. Cloud Storage is the right choice for nearly everything else.
What are file events, and why turn them on?
File events replace listing a folder over and over with one notification per new file. They grant no new access to your data, and they spare Auto Loader and file arrival triggers from listing big folders.
Compare checking your letterbox every five minutes with fitting a doorbell. Checking works, but the more post you get, the longer each check takes, and most checks find nothing new. A doorbell tells you the moment something arrives. Listing a path is checking the letterbox; a Cloud Storage notification to Pub/Sub is the doorbell; and with file events Databricks installs one doorbell per external location and keeps a note of every delivery.
Auto Loader is the Databricks feature that ingests new files from a path incrementally, and file arrival triggers start a job when files land. Both need to discover new files. Listing a big folder over and over is slow, so cloud storage can instead send a notification for every new object. On Google Cloud, a Cloud Storage notification publishes a message to a Pub/Sub topic (Google’s messaging service), and a subscriber reads it.
With file events,
Databricks sets up and manages that pipeline once per external location, using the permissions from
step 3, and caches the file metadata. Auto Loader streams that set cloudFiles.useManagedFileEvents
read from this cache instead of listing. One shared queue serves every stream on the bucket, which
avoids the per-bucket notification limits of the older per-stream approach. Databricks recommends
running such streams at least once every seven days; otherwise the stored position expires and the
stream falls back to a full listing. The external location guide stresses that the permissions “do
not grant any new access to your data; file contents are never transmitted through this
infrastructure”.
File events are on by default for new external locations, and validation fails if the permissions are missing. You can force-create the location without them, but you then lose those features for that location.
How does this apply to Acme? The distribution centres drop delivery files into landing/, the
external volume supply_bronze.landing inside the external location acme_lake. A stream that
ingests that folder with cloudFiles.useManagedFileEvents (an illustration; this part does not
build it) reads new file names from the cached events instead of listing every file the centres have
ever dropped, and keeps working that way as long as it runs at least weekly.
Where do Acme’s tables and volumes land?
Wherever their managed location or external path points. They do not go into “the fourth bucket”
automatically; you send them there. Here is the SQL that sets up Acme’s catalog, from the
managed storage page,
the CREATE VOLUME reference
and the external tables page (not run here):
-- Catalog with its own managed location (inside external location acme_lake)
CREATE CATALOG acme_supply
MANAGED LOCATION 'gs://acme-lake-supply/managed';
CREATE SCHEMA acme_supply.supply_bronze;
CREATE SCHEMA acme_supply.supply_silver;
CREATE SCHEMA acme_supply.supply_gold;
-- Managed table: no path, Unity Catalog picks it under managed/__unitystorage/...
CREATE TABLE acme_supply.supply_bronze.deliveries_raw (
delivery_id STRING, dc STRING, sku STRING, units INT, delivered_at TIMESTAMP);
-- External volume over the folder where distribution centres drop files
CREATE EXTERNAL VOLUME acme_supply.supply_bronze.landing
LOCATION 'gs://acme-lake-supply/landing';
-- External table at a fixed path
CREATE TABLE acme_supply.supply_gold.supply_service (
brand STRING, region STRING, week_start DATE,
units_ordered BIGINT, units_delivered BIGINT, otif_pct DOUBLE)
LOCATION 'gs://acme-lake-supply/supply_service';
So, for Acme:
- The catalog
acme_supplywas created withMANAGED LOCATION 'gs://acme-lake-supply/managed', so managed tables such assupply_bronze.deliveries_raware stored undergs://acme-lake-supply/managed/__unitystorage/.... - The volume
supply_bronze.landingis an external volume atgs://acme-lake-supply/landing/, where the distribution centres drop their delivery files. supply_gold.supply_serviceis an external table atgs://acme-lake-supply/supply_service/, which keeps a stable path that other engines, such as BigQuery in Part 8, can be pointed at. That is a choice for this example: Iceberg reads work for managed and external tables alike.- Anything someone creates in the workspace catalog without a location goes to the third workspace bucket (classic) or to default storage (serverless).
What does that look like inside the bucket? When you set a managed location, Unity Catalog records
it as the storage root and adds hashed subfolders so that every managed object gets a unique
path: <storage-root>/__unitystorage/catalogs/<uuid> for a catalog, and .../schemas/<uuid> for a
schema. Below that, Unity Catalog chooses the path of each table. A quick snapshot of Acme’s bucket:
what gs://acme-lake-supply looks like inside: the catalog acme_supply has its storage root at managed/, under which Unity Catalog creates __unitystorage/catalogs/<catalog id>/ and chooses the rest of each managed table's path; landing/ is the external volume supply_bronze.landing with the distribution centres' JSON files; supply_service/ is the external table supply_gold.supply_service with its own _delta_log; managed paths are reached by name only
supply_gold.supply_service is Acme’s gold table, so here is a quick look at what those Parquet
files hold. Prerequisite 2
introduces Acme, and the
business and data prerequisite
explains every column.
supply_gold.supply_service: one row per brand, region and week, owned by supply, in Databricks; stored as an external Delta table at gs://acme-lake-supply/supply_service/
The same holds in a serverless workspace. acme_supply still points at Acme’s bucket, so its tables
and volumes stay in Acme’s project; only the workspace’s own storage and the workspace catalog move
to default storage.
Compute and storage sit in different places. Classic compute runs in your project; serverless
compute runs in Databricks’ account. Both read and write gs://acme-lake-supply through the same
storage credential, so Acme can move a job from classic to serverless without moving any data. The
data bucket does not have to be in the workspace project; it only needs to grant the credential’s
service account access. How the bytes travel, over Private Google Access for classic and over
Google’s backbone for serverless (never the public internet in either case), is the subject of
Part 3 and
Part 4.
where Acme's objects are stored in a classic and in a serverless workspace: in both, the acme_supply catalog's managed tables, the external landing volume and the external supply_service table live in Acme's bucket gs://acme-lake-supply, reached through the storage credential's service account; what differs is the workspace's own storage and the workspace catalog, which are buckets in Acme's project for classic and default storage in Databricks' account for serverless, and where the compute runs
My view: give every production catalog a managed location in a bucket you own, keep the workspace catalog for experiments, and treat the workspace storage buckets as Databricks’ internals that nobody should touch.
Can you move a managed location later?
Yes, with limits. ALTER CATALOG ... SET MANAGED LOCATION and ALTER SCHEMA ... SET MANAGED LOCATION need Databricks Runtime 18.1 or above (or Databricks SQL), and “affects only new managed
tables and volumes created after the change. Existing managed objects are not moved.” The location
string, including gs://, must be 150 characters or fewer. In Terraform, changing storage_root on
a databricks_catalog forces a new catalog, which is rarely what you want. Choose the catalog’s
location carefully on day one.
Managed or external?
Choose managed unless something outside Databricks must reach the files by path, or the data already sits in a format managed tables do not support.
The beginner question is: if both kinds live in my bucket, why does it matter which I choose? It matters because it decides who owns the files. In the cloakroom from earlier, handing in your coat means the venue looks after it and clears it away when you leave; your own hook means nobody tidies up for you, but anyone you let into the hallway can reach it.
managed versus external tables and volumes in Unity Catalog: managed objects live at a path Unity Catalog chooses under the managed location and are used by name only, support Delta Lake and Iceberg and are maintained by predictive optimization, and their files are deleted after the recovery period when dropped; external objects live at a path you choose inside an external location, can be read by path and by other engines, support more file formats, need your own maintenance, and keep their files when dropped
The managed tables page calls them “the
default and recommended table type”, and says they “cost less to store and query, maintain and
optimize themselves automatically”. Some features exist only for managed tables, including
predictive optimization, which runs OPTIMIZE, VACUUM and ANALYZE for you and is on by default
for accounts created on or after 11 November 2024. External tables still have two clear uses, per
the external tables page: registering existing
data in a format managed tables do not support, such as JSON or Avro, and giving non-Databricks
clients direct access to the files. If another engine changes an external table’s metadata,
Databricks recommends running MSCK REPAIR TABLE <table> SYNC METADATA to bring Unity Catalog up to
date.
For volumes, the volumes page makes the same split: managed volumes are the “simplest option for Databricks-only workloads”, and external volumes add governance “where data already resides, without requiring a data copy”. Volumes need Databricks Runtime 13.3 LTS or above, and you cannot register files in a volume as a table.
A litmus test, cheapest to run first:
1. Does a managed table or managed volume do the job? yes -> managed
2. Does a client outside Databricks need to read the files by path? yes -> external
3. Does the data already live somewhere, in a format such as JSON or Avro? yes -> external
Only then is external the answer, and you take on maintenance and deletion.
How does this apply to Acme? supply_bronze.deliveries_raw passes question 1, so it is managed.
The landing volume is external because the distribution centres already write files to
gs://acme-lake-supply/landing/, which is question 3. supply_service is external by choice, for a
stable path, even though, as noted above, Iceberg reads would work for a managed table too.
What happens when you drop a table or a volume?
Dropping a managed table starts a countdown to deletion; dropping an external table only removes its name. Neither lowers the storage bill on the day you drop, which is the misconception that causes most surprises.
Deleting a file on a laptop works the same way. It first goes to the recycle bin, where you can
restore it. Later, an automatic clean-up empties the bin, at a time you do not choose. If the office
then keeps the emptied rubbish in a bin by the door for a week before collection, it can still be
fished out, and it still takes space. And
deleting a desktop shortcut never touches the file it points to. The recycle bin is the recovery
window and restore is UNDROP; the automatic clean-up is the purge; the bin by the door is Cloud
Storage soft delete; the shortcut is an external table.
This is where managed and external really differ, and where storage bills surprise people. The object storage lifecycle page describes three phases for a managed table or volume in your own bucket.
- Recovery window. For 7 days after the drop, Unity Catalog keeps the files and the metadata.
You can bring back a table with
UNDROP(and, on recent runtimes, a volume withUNDROP VOLUME), and storage billing continues. - Purge. After the window, an asynchronous process deletes the files. It does not run at once: it “can delete the data files at any point from 7 days up to about 30 days after the delete”.
- Your bucket’s own policies. Once Unity Catalog deletes the files, soft delete, Object Versioning or lifecycle rules on the bucket may keep them longer, and Google Cloud bills for them.
what happens after DROP, on a timeline that is not to scale: for a managed table in Acme's bucket, the files are kept for a 7-day recovery window in which UNDROP works, then an asynchronous purge deletes them at any point up to about 30 days after the drop, then Cloud Storage soft delete keeps the deleted objects for 7 more days by default and bills for them; a managed volume follows the same file lifecycle and, on Databricks Runtime 19.11 or above, can be recovered with UNDROP VOLUME within 7 days; for an external table or volume only the metadata goes and the files stay until you delete them
Three details catch people out. First, not everything comes back. Tables, materialized views and
streaming tables return with UNDROP, and, as of 8 November 2026, volumes return too: the
lifecycle page
now says to “use UNDROP VOLUME within 7 days of the delete”, and the
UNDROP VOLUME reference
needs Databricks Runtime 19.11 or above and the volume’s ID, because “there is no form that
recovers a volume by name”. For a managed volume the files come back too. What stays
unrecoverable is “Catalogs, schemas, views, functions, models: Cannot be recovered after delete.”
Second, DROP CATALOG and DROP SCHEMA default to RESTRICT, which refuses to drop
anything that is not empty; CASCADE drops the contents, each according to its own managed or
external rules. Third, for default storage the
default storage page says the files
are purged within 48 hours after the 7-day window and storage billing stops when the window ends.
How does this apply to Acme? If someone drops the managed table
acme_supply.supply_bronze.deliveries_raw, its files under managed/__unitystorage/... stay for 7
days and can come back with UNDROP, are purged somewhere between day 7 and about day 30, and then
sit in soft delete for the 7 days Acme set with --soft-delete-duration=7d, billed throughout. If
someone drops the external table supply_service, only its Unity Catalog entry goes; the files at
gs://acme-lake-supply/supply_service/ stay until someone deletes them.
Here is how Acme finds and recovers a table that someone dropped by mistake, using
SHOW TABLES DROPPED
(Public Preview) and UNDROP
(not run here):
SHOW TABLES DROPPED IN acme_supply.supply_silver;
-- If several dropped tables share a name, use the table ID from the list
UNDROP TABLE acme_supply.supply_silver.deliveries_clean;
UNDROP TABLE WITH ID '<table-id>';
-- Volumes (Databricks Runtime 19.11 or above): find the ID, then undrop by ID
SHOW VOLUMES DROPPED IN acme_supply.supply_bronze;
UNDROP VOLUME WITH ID '<volume-id>';
-- Public Preview: keep dropped gold tables recoverable for 30 days
ALTER SCHEMA acme_supply.supply_gold RETAIN DROPPED TO 30 DAYS;
The recovery period is configurable at catalog or schema level, in Public Preview: 0 hours (no
recovery) or 7 to 30 days. A schema setting overrides the catalog setting, and it only applies to
tables dropped after you set it. UNDROP restores privileges, columns and properties, but not
primary and foreign key constraints, which you add again with ALTER TABLE ADD CONSTRAINT.
How should Acme lay out its buckets?
There is no single right layout, but the rules below keep grants, keys and bills simple. They are my recommendations, built on the documented constraints above.
- One bucket per environment and data domain, so that IAM, keys and cost reports follow the
bucket:
gs://acme-lake-supplyfor production supply data,gs://acme-lake-supply-devfor development. Bind the production credential and external location to the production workspaceacme-prod-euw2-supplyonly. - One managed location per catalog, as Databricks recommends, and a schema-level location only when a schema needs its own bucket, for example because it uses a different encryption key.
- A regional bucket in the workspace’s region,
europe-west2, flat namespace, uniform bucket-level access, public access prevention, labels. - Landing and tables in separate paths, so that lifecycle rules and Autoclass can apply to
landing/without ever touching a table path. A separate landing bucket is even cleaner if other systems write to it. - No metastore-level managed location unless you have a reason. New workspaces enabled for Unity Catalog automatically are created without one, and catalog-level locations make ownership clear.
It also helps to know the resource limits before you design: 1,000 storage and service credentials combined per metastore, 10,000 external locations per metastore, 1,000 catalogs per metastore, 10,000 schemas per catalog, and 10,000 tables and 10,000 volumes per schema. A credential per bucket and an external location per bucket fit comfortably.
What can go wrong?
Most storage problems show up as a failed external location test, a slow or expensive read, or a bill that does not fall when you expect. Here are the ones I see most, with their causes and fixes.
storage troubleshooting for Databricks on Google Cloud: failed external location tests from missing bucket roles or file events permissions, hierarchical namespace buckets that cannot be external locations, cross-region buckets, storage bills that lag behind drops because of the recovery window, purge delay and soft delete, tables broken by lifecycle rules, classic compute that cannot read default storage, and workspaces broken by a deleted workspace storage bucket
- Deleting a workspace storage bucket breaks the workspace for good. It can look like an unused bucket in your project, so label it and restrict who can delete it.
- Someone gets “temporary” bucket access for a migration and keeps it. Direct IAM on a managed storage bucket bypasses Unity Catalog’s grants and audit. Use a time-bound IAM Condition, or better, do the work through an external volume.
- A managed location is set on the wrong catalog.
ALTER ... SET MANAGED LOCATIONdoes not move existing tables, so a wrong choice leaves data in two places. Decide locations before the first table is created. - An HNS bucket is created by a platform team template. The setting cannot be changed later, so
check templates and Terraform modules for
hierarchical_namespacebefore Databricks buckets are created from them.
What would I do now?
- Create each Databricks data bucket with the settings in the bucket settings diagram above, and add a check to your infrastructure pipeline that rejects hierarchical namespace, public access and retention policies on those buckets.
- Create one storage credential and one external location per bucket, bind them to the right workspaces, grant the two bucket roles and the file events permissions, and grant Unity Catalog privileges to groups only.
- Give every production catalog a managed location in a bucket you own, and keep the workspace catalog for experiments.
- Find the workspace storage buckets in each workspace project, label them, review their IAM, turn on Data Access audit logs, and disable DBFS root and mounts once nothing depends on them.
- Agree a recovery policy: the Unity Catalog recovery period for important schemas and the soft delete duration for each bucket, so that you know how long a dropped table can come back and how long its files are billed.
What did we learn?
- Cloud Storage gives Delta tables what they need, strong consistency and put-if-absent commits, as long as the bucket is regional in the workspace’s region and has no lifecycle deletes, retention policy or hierarchical namespace on table paths.
- Hierarchical namespace buckets cannot be Unity Catalog external locations, and Delta does not need them, because a Delta commit creates one log object and never renames a folder.
- A classic workspace has two or three workspace storage buckets in its project that must never be deleted, and a serverless workspace uses default storage in Databricks’ account instead. Default storage suits catalogs that only serverless compute uses, but classic clusters, BigQuery and other outside engines cannot reach it, so shared or regulated data belongs in your own bucket.
- Unity Catalog reaches your bucket through a storage credential (a Databricks-generated service account with two roles on the bucket) and an external location (that credential plus a path), and users never need IAM on the bucket.
- Managed tables and volumes live under a managed location that Unity Catalog controls and resolves from schema to catalog to metastore, while external ones live at paths you choose and keep their files when dropped.
- Dropping a managed table keeps its files for a 7-day recovery window, then purges them within about 30 days, after which the bucket’s soft delete can keep and bill them for longer.
What comes next?
We now know where every byte of Acme’s lakehouse lives and who can reach it. In Databricks on Google Cloud, Part 6: Governance and security, we go deep into Unity Catalog itself: the privileges and ownership model behind the grants we used here, row filters and column masks, lineage and system tables, customer-managed keys for workspace storage and managed services, secrets, and the audit logs that show who touched what.
Sources
- Cloud Storage: About buckets
- Cloud Storage: Bucket locations
- Cloud Storage: Storage classes
- Cloud Storage: Consistency
- Cloud Storage: Request preconditions
- Cloud Storage: Request rate and access distribution guidelines
- Cloud Storage: Uniform bucket-level access
- Cloud Storage: Public access prevention
- Cloud Storage: Soft delete overview
- Cloud Storage: Object Versioning
- Cloud Storage: Object Lifecycle Management
- Cloud Storage: Autoclass
- Cloud Storage: Bucket Lock
- Cloud Storage: Hierarchical namespace
- Cloud Storage: Create buckets
- gcloud storage buckets create
- Terraform: google_storage_bucket
- Microsoft: Azure Data Lake Storage hierarchical namespace
- Databricks: Connect to a Google Cloud Storage (GCS) external location
- Databricks: Connect to cloud object storage using Unity Catalog
- Databricks: Manage storage credentials
- Databricks: Specify a managed storage location in Unity Catalog
- Databricks: Unity Catalog managed tables
- Databricks: Work with external tables
- Databricks: What are Unity Catalog volumes?
- Databricks: CREATE VOLUME
- Databricks: Object storage lifecycle in Unity Catalog
- Databricks: UNDROP
- Databricks: UNDROP VOLUME
- Databricks: SHOW TABLES DROPPED
- Databricks: Unity Catalog privileges reference
- Databricks: Default storage
- Databricks: Serverless workspaces
- Google Cloud Lakehouse: Set up cross-cloud connection for Databricks Unity Catalog
- Databricks: What are catalogs?
- Databricks: High-level architecture
- Databricks: Create a classic workspace
- Databricks: Manage workspace storage
- Databricks: What is DBFS?
- Databricks: Disable access to DBFS root and mounts
- Databricks: Disable legacy features
- Databricks: Auto Loader with file events
- Databricks: Predictive optimization for Unity Catalog managed tables
- Databricks: Resource limits
- Terraform: databricks_storage_credential
- Terraform: databricks_external_location
Opinions are my own. Every fact in this post comes from the public documentation linked above. Acme Consumer Brands and its data are fictional.