repository
Querying the Tableau Server repository safely (read-only, with examples)
Karan Arora · Founder · 7 min read · Last reviewed:
Tableau Server keeps a PostgreSQL database about itself — the
repository, database name workgroup — and it is the best documentation
your deployment will ever have, because it cannot be out of date. Every
job, session, schedule, workbook, and sign-in is in there. It is also a
production database that the server itself depends on, which is why most
administrators either never touch it or touch it nervously.
Both extremes waste the asset. The repository can be read safely, with a short list of disciplines that cost nothing. This guide is those disciplines, plus enough example SQL to make the first session useful. It is also, not coincidentally, exactly how our own collector works — the rules below are the rules it ships with.
What the repository is, and is not
The repository stores metadata and operational history: content
inventories, users, schedules, background jobs, HTTP request logs, audit
events. It does not store your business data — extract contents live
in files on disk, not in workgroup. What it does hold that deserves
care: user names, email addresses, and the names of workbooks, projects
and data sources. Nothing here is a row of your revenue table, but
plenty of it is organizationally sensitive — which shapes the
off-machine rules at the end of this guide.
Getting access, the documented way
Tableau provides a purpose-built account for this: the readonly user, enabled with TSM:
tsm data-access repository-access enable --repository-username readonly --repository-password <password>Notes worth knowing before you run it:
- The
readonlyaccount has SELECT grants and nothing else. The other documented account,tblwgadmin, is the superuser Tableau itself uses — never query with it, for the same reason you don't browse the web as root. - Remote connections reach the repository on port 8060 of the node running it, so a firewall rule scoped to the analyst machine is part of the setup — scoped, not open.
- Enabling access is a TSM configuration change: plan it like one, in a maintenance window, and expect TSM to apply pending changes rather than assuming it is instant.
Any PostgreSQL client works from there. For a command line:
psql -h your-server -p 8060 -d workgroup -U readonlyFor exploration with completion and saved queries, any SQL IDE with a
PostgreSQL driver does the job. And Tableau itself is a legitimate
client:
custom administrative views
are exactly this — a Tableau workbook pointed at workgroup through the
PostgreSQL connector, which needs the PostgreSQL driver installed on the
machine running Desktop. Connecting Tableau to its own repository feels
circular the first time and quickly becomes the most natural way to
share what you find with people who live in dashboards.
The session discipline
The readonly user's grants already prevent writes. The discipline below is defense in depth — it protects the repository from your tooling, your future self, and the query you didn't mean to run at that size:
SET TRANSACTION READ ONLY;
SET statement_timeout = '30s';The first line makes the session refuse any write, independent of what grants exist today or after an upgrade. The second is the one people skip: the repository serves Tableau Server in production, and a runaway analytical query holds resources the server wants. A statement timeout turns "my exploratory join was bigger than I thought" from an incident into an error message. Thirty seconds is generous for every query in this guide series; tune upward knowingly, not by default.
Three habits complete the discipline. Window every query on a timestamp
(WHERE created_at >= NOW() - INTERVAL '30 days') rather than reading
whole tables. Prefer aggregates over row dumps — you rarely need the
rows, and the rows are where the sensitive names live. And keep
transactions short: connect, query, disconnect, rather than holding a
session open across a business day.
A map of the useful tables
The full schema is large, and Tableau publishes a data dictionary for all of it — versioned per release, so match it to your Server version (every table and column named in this guide is checked against the 2025.1 edition). In practice, reviews spend most of their time in a handful of tables:
| Table | What it answers |
| --- | --- |
| background_jobs | Every background job with queue and runtime timestamps — the backbone of failure and delay analysis |
| http_requests | Request-level traffic: who loaded what, when, how long it took |
| historical_events | The audit trail — sign-ins, views, publishes, downloads |
| sites, projects, workbooks, views, datasources | The content inventory |
| users, system_users | Accounts and identities (names and emails live here) |
| schedules, tasks, subscriptions | What is set to run, when, and for whom |
| extracts | Extract metadata for size and storage analysis |
There are also convenience views with underscore-prefixed names —
_users, _views_stats and relatives — that pre-join common
combinations for administrative reporting. They are handy for
exploration; for anything you will automate, prefer the base tables and
the data dictionary, so a version upgrade changes your queries on your
schedule rather than its own.
The audit trail deserves a note of its own, because its shape trips
people up. historical_events is a narrow fact table — the data
dictionary calls it "the heart of the cluster of tables devoted to
historical event auditing" — and the detail lives in hist_ dimension
tables around it: hist_users, hist_workbooks, hist_views and
relatives, joined by id. The hist_ tables are point-in-time copies,
not live records — per the dictionary, they record what "was relevant
at the time of the event." That is exactly what an audit trail should
do, and exactly what a careless join to the live users table silently
gets wrong. Sign-in analysis, view-access
patterns and publish histories all start from this star; expect to
spend your first session with the data dictionary open beside it.
Two practical cautions from the field, both observed rather than
documented. http_requests is by far the largest table on busy
estates and is trimmed aggressively by Tableau's own cleanup — window
any query against it tightly, and check how much history yours actually
holds before designing analysis around it. And every table's history is
bounded by retention settings: SELECT MIN(created_at) against any
table tells you the real window in one line.
First queries that earn their keep
A first session should answer real questions. Three starters, in increasing order of interest.
The estate at a glance — how much content exists, per site:
SET TRANSACTION READ ONLY;
SELECT
s.name AS site,
COUNT(DISTINCT p.id) AS projects,
COUNT(DISTINCT w.id) AS workbooks,
COUNT(DISTINCT v.id) AS views
FROM sites s
LEFT JOIN projects p ON p.site_id = s.id
LEFT JOIN workbooks w ON w.site_id = s.id
LEFT JOIN views v ON v.site_id = s.id
GROUP BY s.name
ORDER BY workbooks DESC;What the platform is being asked to do — request volume by day, windowed tightly because this table is large:
SELECT
DATE_TRUNC('day', created_at) AS day,
COUNT(*) AS requests
FROM http_requests
WHERE created_at >= NOW() - INTERVAL '7 days'
GROUP BY 1
ORDER BY 1;And what is scheduled against those resources — tasks per schedule, with the priority that orders the queue:
SELECT
sch.name AS schedule,
sch.priority,
COUNT(t.id) AS tasks
FROM schedules sch
LEFT JOIN tasks t ON t.schedule_id = sch.id
GROUP BY sch.name, sch.priority
ORDER BY tasks DESC;From here, the natural next steps are the deeper analyses this series
covers: refresh failure concentration, queue-delay clustering, and
backgrounder utilization — each of them a handful of queries against
background_jobs using exactly this session discipline. Structurally,
our audit of a full Tableau deployment is this
same practice at scale, with a rule library behind it.
Surviving upgrades
The repository schema is Tableau's, not yours, and Tableau changes it between versions — tables gain columns, views get reshaped, and nothing about your queries is guaranteed across an upgrade. The data dictionary is published per version, which is the tell: treat your saved queries the way you treat any dependency on someone else's schema. Concretely, that means three habits. Keep the queries in version control with the server version they were written against. After every upgrade, re-run the set against the new version before trusting any number they produce — a renamed column that errors loudly is the good outcome; a semantic change that keeps the same name is the one to hunt for. And prefer base tables over the underscore convenience views for anything automated, precisely because the convenience views are the more likely to be reshaped for Tableau's own purposes.
The off-machine rules
Everything above concerns reading safely. The other half of repository discipline is what leaves the machine afterwards, because query results carry the sensitive part: user names, email addresses, content names.
The rules we run our own collector under, and recommend generally: aggregate before anything leaves the box — counts, durations, percentiles, never raw rows. Replace object names with codes if results must travel, and keep the code-to-name key where the data came from. And make transfer a human decision: a file someone reviews and sends, not a pipe that streams. A repository analysis that follows those three rules can be shared with an outside party — or with us — without your data ever really leaving home. That is not a compliance nicety; it is what makes the analysis possible at all in organizations where "export the user table" is correctly a firing offense.
Tableau Cloud has no repository
None of the above applies to Tableau Cloud — there is no workgroup
database to reach, by design. The equivalents live in the Admin
Insights project and the Metadata API, which cover much of the same
ground in a different shape. That mapping is
its own guide.
Small rules, compounding returns
Read-only user, read-only transaction, statement timeout, tight windows, aggregates out. Five rules, each one line or one habit, and together they turn the repository from the database everyone is afraid of into the instrument every serious Tableau review is built on. The server has been keeping honest records all along — as far back as its retention settings allow. It is only polite to read them carefully.