Security & Auth

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...

📅
📖8 min read

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

EventWhat's recorded
Login / logoutUser, timestamp, IP address, authentication method
Failed login attemptsUser email, timestamp, IP address, failure reason
Password changeUser, timestamp
User created / deactivatedAdmin who made the change, affected user, timestamp
Group membership changesAdmin, user added/removed, group name, timestamp

Data Access

EventWhat's recorded
Question executedUser, question ID/name, database queried, execution time, row count
Dashboard viewedUser, dashboard ID/name, timestamp
Ad-hoc query runUser, database, full SQL query text, execution time
Data export (CSV/Excel)User, question/dashboard, timestamp, format
API data accessAPI key used, endpoint, timestamp

Content Changes

EventWhat's recorded
Dashboard created/edited/deletedUser, dashboard name, change details, timestamp
Question created/edited/deletedUser, question name, old/new query, timestamp
Collection created/moved/deletedUser, collection name, timestamp
Database connection added/modifiedAdmin, connection name, timestamp (not credentials)

Permission Changes

EventWhat's recorded
Permission group created/deletedAdmin, group name, timestamp
Permissions graph updatedAdmin, what changed (before/after), timestamp
Collection permissions changedAdmin, group, collection, old/new access level
Sandbox policy created/modifiedAdmin, 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
  • User filtering
  • Event type filtering
  • Full-text search across event details
  • 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:

  • Logs must be retained for 6 years
  • Logs must be protected from modification (immutable)
  • Breach investigation capability (can you trace who accessed specific records?)
  • 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 frameworkMinimum log retention
    SOC 212 months (accessible) + additional archival
    HIPAA6 years
    GDPRAs long as necessary; document retention period
    PCI DSS12 months (3 months immediately available)
    Internal policyMinimum 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.