Audit Logging and Compliance in Metabase
Metabase's audit logging feature records a tamper-evident log of every significant action taken in the system — who queried what data, who changed whi...
Audit Logging and Compliance in Metabase
Metabase's audit logging feature records a tamper-evident log of every significant action taken in the system — who queried what data, who changed which dashboard, who modified permissions, and who accessed Metabase from where. The audit log is available in Metabase Enterprise and is the primary mechanism for meeting compliance requirements (SOC 2, HIPAA, GDPR) that demand evidence of data access controls and the ability to investigate unauthorized access.
---
What the Audit Log Records
User Activity
| Event | What's recorded |
|---|---|
| Login / logout | User, timestamp, IP address, authentication method |
| Failed login attempts | User email, timestamp, IP address, failure reason |
| Password change | User, timestamp |
| User created / deactivated | Admin who made the change, affected user, timestamp |
| Group membership changes | Admin, user added/removed, group name, timestamp |
Data Access
| Event | What's recorded |
|---|---|
| Question executed | User, question ID/name, database queried, execution time, row count |
| Dashboard viewed | User, dashboard ID/name, timestamp |
| Ad-hoc query run | User, database, full SQL query text, execution time |
| Data export (CSV/Excel) | User, question/dashboard, timestamp, format |
| API data access | API key used, endpoint, timestamp |
Content Changes
| Event | What's recorded |
|---|---|
| Dashboard created/edited/deleted | User, dashboard name, change details, timestamp |
| Question created/edited/deleted | User, question name, old/new query, timestamp |
| Collection created/moved/deleted | User, collection name, timestamp |
| Database connection added/modified | Admin, connection name, timestamp (not credentials) |
Permission Changes
| Event | What's recorded |
|---|---|
| Permission group created/deleted | Admin, group name, timestamp |
| Permissions graph updated | Admin, what changed (before/after), timestamp |
| Collection permissions changed | Admin, group, collection, old/new access level |
| Sandbox policy created/modified | Admin, group, table, policy details, timestamp |
Accessing the Audit Log
Admin UI
Navigate to Admin → Audit to browse audit events interactively. The UI provides:
- Time range filtering
API Access
The audit log is queryable via API:
bash
<h1 class="text-4xl font-bold mb-6 text-slate-900">Get recent audit log entries</h1> GET /api/audit-app/user/:user-id/query_views GET /api/audit-app/dashboard/:dashboard-id/views GET /api/audit-app/question/:question-id/views
For bulk export or integration with external SIEM tools:
javascript
async function exportAuditLog(startDate, endDate) { const events = []; let offset = 0; const limit = 1000;
while (true) { const res = await fetch( ${METABASE_URL}/api/audit-app/query/audit_log? + start=${startDate}&end=${endDate}&limit=${limit}&offset=${offset}, { headers: { "x-api-key": API_KEY } } ); const data = await res.json();
events.push(...data.rows);
if (data.rows.length < limit) break; offset += limit; }
return events; }
---
Forwarding Audit Logs to External Systems
For compliance programs, audit logs typically need to flow into a centralized SIEM (Security Information and Event Management) system like Splunk, Datadog, Elastic Security, or AWS Security Hub.
Option 1: PostgreSQL-Based Log Shipping
Metabase stores audit events in its application database. You can query the audit_log table directly and ship events to your SIEM:
sql
-- Query recent audit events from the application database SELECT al.id, al.topic, al.timestamp, al.end_timestamp, al.details, u.email as user_email, u.first_name, u.last_name FROM audit_log al LEFT JOIN core_user u ON al.user_id = u.id WHERE al.timestamp > NOW() - INTERVAL '24 hours' ORDER BY al.timestamp DESC;
Set up a scheduled job (cron, Lambda, or Kubernetes CronJob) to export and forward these events.
Option 2: Splunk HEC Integration
javascript
// Ship Metabase audit events to Splunk via HTTP Event Collector async function shipToSplunk(events) { const SPLUNK_HEC_URL = process.env.SPLUNK_HEC_URL; const SPLUNK_TOKEN = process.env.SPLUNK_HEC_TOKEN;
const payload = events.map(event => ({ time: new Date(event.timestamp).getTime() / 1000, source: "metabase:audit", sourcetype: "metabase:audit_log", index: "security", event: { topic: event.topic, user_email: event.user_email, details: event.details, timestamp: event.timestamp, }, }));
await fetch(SPLUNK_HEC_URL, { method: "POST", headers: { Authorization: Splunk ${SPLUNK_TOKEN}, "Content-Type": "application/json", }, body: payload.map(e => JSON.stringify(e)).join("\n"), }); }
Option 3: Datadog Log Forwarding
javascript
// Forward to Datadog Logs API async function shipToDatadog(events) { await fetch("https://http-intake.logs.datadoghq.com/api/v2/logs", { method: "POST", headers: { "DD-API-KEY": process.env.DATADOG_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(events.map(event => ({ ddsource: "metabase", ddtags: "env:production,service:metabase", hostname: "metabase", service: "metabase-audit", message: JSON.stringify(event), }))), }); }
---
Compliance Use Cases
SOC 2 Type II
SOC 2 requires evidence that access to sensitive data is logged and reviewed. Metabase audit logs support:
CC6.2 (Logical access provisioning): Audit log records user creation, group membership changes, and permission modifications with admin identity and timestamps.
CC6.3 (Access removal): Audit log records user deactivation with timestamp and admin identity.
CC7.2 (Monitoring of system components): Audit log provides a complete record of data access, including failed queries and permission violations.
What to show auditors: Export 3–6 months of audit log events showing a representative sample of access activity, user provisioning/deprovisioning events, and permission changes. Show that you have a process for reviewing the logs periodically.
HIPAA
HIPAA requires audit controls for PHI (Protected Health Information) access. If your Metabase instance connects to databases containing PHI:
Required: Every access to PHI must be logged with user identity, timestamp, and what was accessed.
Metabase provides: User-level query logging for every executed question, including the SQL and which tables were accessed.
Additional requirements:
For HIPAA, configure log shipping to an immutable log store (AWS CloudTrail Logs, Splunk with write-once settings) and establish a 6-year retention policy.
GDPR
GDPR Article 30 requires records of processing activities. For Metabase:
Data access logging: The audit log provides evidence of who accessed personal data and when, which is required for demonstrating accountability and responding to data subject access requests.
Data export logging: Every CSV/Excel export is logged, helping trace when personal data left the system.
Third-party processing: If using Metabase Cloud, document it as a data processor in your Article 30 records.
---
Retention and Archival
Retention Recommendations
| Compliance framework | Minimum log retention |
|---|---|
| SOC 2 | 12 months (accessible) + additional archival |
| HIPAA | 6 years |
| GDPR | As long as necessary; document retention period |
| PCI DSS | 12 months (3 months immediately available) |
| Internal policy | Minimum 90 days, 12 months recommended |
Archival Strategy
bash
<h1 class="text-4xl font-bold mb-6 text-slate-900">Monthly archival job: export previous month's audit logs to S3</h1> #!/bin/bash
MONTH=$(date -d "last month" +%Y-%m) START="${MONTH}-01T00:00:00Z" END=$(date -d "${MONTH}-01 + 1 month" +%Y-%m-01T00:00:00Z)
<h1 class="text-4xl font-bold mb-6 text-slate-900">Export from Metabase application DB</h1> psql "$DATABASE_URL" -c \ "COPY (SELECT * FROM audit_log WHERE timestamp BETWEEN '$START' AND '$END') \ TO STDOUT WITH CSV HEADER" \ | gzip > /tmp/audit-${MONTH}.csv.gz
<h1 class="text-4xl font-bold mb-6 text-slate-900">Upload to S3 with long-term storage</h1> aws s3 cp /tmp/audit-${MONTH}.csv.gz \ s3://your-compliance-bucket/metabase-audit/${MONTH}.csv.gz \ --storage-class GLACIER
echo "Archived audit logs for $MONTH"
---
Alerting on Suspicious Activity
Configure alerts for audit events that warrant immediate attention:
javascript
// Check audit log for suspicious patterns async function checkForAnomalies() { const onehourago = new Date(Date.now() - 3600000).toISOString();
// Alert: admin permission changes const permissionChanges = await queryAuditLog({ topic: "permissions-graph-revision", since: onehourago, }); if (permissionChanges.length > 0) { await sendAlert("Permission changes detected", permissionChanges); }
// Alert: bulk data exports const exports = await queryAuditLog({ topic: "card-query", export: true, since: onehourago, }); if (exports.length > 10) { await sendAlert(Unusual export volume: ${exports.length} exports in 1 hour, exports); }
// Alert: multiple failed logins const failedLogins = await queryAuditLog({ topic: "user-login", success: false, since: onehourago, }); if (failedLogins.length > 5) { await sendAlert(${failedLogins.length} failed login attempts, failedLogins); } }
---
Audit Log Limitations
The audit log records Metabase-level events, not database-level events. If someone accesses your database directly (bypassing Metabase), those accesses are not in the Metabase audit log. For complete database access auditing, supplement Metabase logs with database-level audit logging (PostgreSQL's pgaudit extension, RDS Enhanced Monitoring, Snowflake Access History, etc.).
Audit logs are stored in the application database. If the application database is compromised, audit logs could potentially be modified. For tamper-evident logging, ship events to an external system immediately.
API key actions are attributed to the key, not a named individual. Create separate API keys per service/integration (not one shared key) so API-sourced actions can be traced to specific integrations.
---
Summary
Metabase Enterprise's audit logging records comprehensive data access and administrative activity — who queried what, who changed permissions, who exported data, and who logged in — with user identity and timestamps. For compliance programs, ship logs to an external SIEM and establish retention policies matching your framework (12 months for SOC 2, 6 years for HIPAA). Supplement Metabase-level audit logs with database-level audit logging for complete coverage. Alert on high-signal events: permission changes, bulk exports, and repeated failed logins.