Skip to main content

Reserved Collections

pb-ext creates the following PocketBase system collections automatically on startup. Do not create collections with these names in your own code.
Creating collections with these reserved names will cause conflicts and may break pb-ext functionality.

Collections Table

CollectionPurpose
_analyticsDaily aggregated page view counters (one row per path/date/device/browser). Retention: 90 days.
_analytics_sessionsRing buffer of the 50 most recent visits for the Recent Activity display. No PII stored.
_job_logsCron job execution logs (start time, end time, duration, status, output). Retention: 72 hours.

_analytics

Purpose: Stores daily aggregated visitor analytics data. Type: System collection (hidden from PocketBase Collections UI) Retention: Automatically purged after 90 days

Schema

FieldTypeDescription
pathstringPage path (e.g., /api/v1/todos)
datedateAggregation date (day-level)
devicestringDevice category: desktop, mobile, tablet, or unknown
browserstringBrowser family: Chrome, Firefox, Safari, etc.
viewsintegerTotal page views for this combination
unique_visitorsintegerEstimated unique visitors (deduplicated by session)

GDPR Compliance

No PII stored:
  • No IP addresses
  • No user agents
  • No visitor IDs
  • No session tokens
Only aggregated counts by device/browser category.

Example Query

records, err := app.FindRecordsByFilter(
    "_analytics",
    "date >= {:today}",
    "-views",
    100,
    0,
    dbx.Params{"today": time.Now().Format("2006-01-02")},
)

_analytics_sessions

Purpose: Ring buffer of the 50 most recent visitor sessions for the “Recent Activity” dashboard widget. Type: System collection (hidden from PocketBase Collections UI) Retention: Only the 50 most recent records are kept (ring buffer)

Schema

FieldTypeDescription
timestampdatetimeVisit timestamp
pathstringVisited page path
devicestringDevice category
browserstringBrowser family
referrerstringReferrer URL (sanitized, no query params)
utm_sourcestringUTM source parameter
utm_mediumstringUTM medium parameter
utm_campaignstringUTM campaign parameter

GDPR Compliance

No PII stored:
  • No IP addresses
  • No user agents
  • No cookies
  • No fingerprinting

Ring Buffer Behavior

When the 51st record is inserted:
  1. Oldest record is deleted
  2. New record is inserted
  3. Collection always contains ≤ 50 records

_job_logs

Purpose: Stores cron job execution logs. Type: System collection (hidden from PocketBase Collections UI) Retention: Automatically purged after 72 hours

Schema

FieldTypeDescription
job_idstringUnique job identifier
job_namestringHuman-readable job name
start_timedatetimeExecution start timestamp
end_timedatetimeExecution end timestamp
duration_msintegerExecution duration in milliseconds
statusstringsuccess, failed, or running
outputtextStructured JSON log output
errortextError message if status is failed
progressintegerProgress percentage (0-100)
statisticsjsonCustom job statistics (optional)

Example Query

// Get logs for specific job
records, err := app.FindRecordsByFilter(
    "_job_logs",
    "job_id = {:jobId}",
    "-start_time",
    50,
    0,
    dbx.Params{"jobId": "myCustomJob"},
)

// Get failed jobs from last 24 hours
records, err := app.FindRecordsByFilter(
    "_job_logs",
    "status = 'failed' && start_time >= {:since}",
    "-start_time",
    100,
    0,
    dbx.Params{"since": time.Now().Add(-24 * time.Hour)},
)

System Jobs

pb-ext registers these system jobs automatically:
Job IDScheduleDescription
__pbExtLogClean__0 0 * * * (daily midnight)Purge _job_logs records older than 72 hours
__pbExtAnalyticsClean__0 3 * * * (daily 3 AM)Purge _analytics rows older than 90 days
They appear in the dashboard with the “System” badge.

Auto-Migration on Upgrade

Schema notes:
  • All three collections are system collections (hidden from the PocketBase Collections UI)
  • On upgrade from an old pb-ext version, incompatible schemas are automatically migrated at startup
  • No manual steps required

Migration Process

  1. pb-ext checks if collection exists
  2. If exists, validates schema matches expected
  3. If schema mismatch, runs migration
  4. If doesn’t exist, creates with correct schema

Logs

Migration logs appear in server output:
[INFO] pb-ext: migrating _analytics collection schema
[INFO] pb-ext: _job_logs collection created
[INFO] pb-ext: all system collections ready

Accessing Collections Programmatically

Read Analytics Data

package main

import (
    "github.com/pocketbase/pocketbase/core"
)

func getTopPages(app core.App) ([]*core.Record, error) {
    return app.FindRecordsByFilter(
        "_analytics",
        "date >= {:week_ago}",
        "-views",
        10,
        0,
        dbx.Params{
            "week_ago": time.Now().AddDate(0, 0, -7).Format("2006-01-02"),
        },
    )
}

Read Job Logs

func getJobHistory(app core.App, jobID string) ([]*core.Record, error) {
    return app.FindRecordsByFilter(
        "_job_logs",
        "job_id = {:id}",
        "-start_time",
        20,
        0,
        dbx.Params{"id": jobID},
    )
}
Do not manually create, update, or delete records in these collections. Use pb-ext’s provided APIs instead.

Dashboard Integration

pb-ext’s dashboard at /_/_ visualizes data from these collections: Analytics Tab:
  • Page view charts (from _analytics)
  • Device breakdown
  • Browser distribution
  • Recent activity feed (from _analytics_sessions)
Jobs Tab:
  • Registered cron jobs
  • Execution history (from _job_logs)
  • Job status monitoring
  • Manual job triggers

Customizing Retention

Retention is controlled by the system cleanup jobs. To customize:

Analytics Retention (default: 90 days)

// Unregister default cleanup job
server.GetJobManager().RemoveJob("__pbExtAnalyticsClean__")

// Register custom cleanup with different retention
server.GetJobManager().RegisterJob(
    "customAnalyticsClean",
    "Custom Analytics Cleanup",
    "Clean analytics older than 180 days",
    "0 3 * * *", // daily at 3 AM
    func(logger *server.JobExecutionLogger) {
        logger.Start()
        cutoff := time.Now().AddDate(0, 0, -180) // 180 days
        _, err := app.Delete("_analytics", "date < {:cutoff}", dbx.Params{"cutoff": cutoff})
        if err != nil {
            logger.Fail(err.Error())
        } else {
            logger.Success("Analytics cleaned")
        }
    },
)

Job Logs Retention (default: 72 hours)

server.GetJobManager().RemoveJob("__pbExtLogClean__")

server.GetJobManager().RegisterJob(
    "customLogClean",
    "Custom Log Cleanup",
    "Clean job logs older than 7 days",
    "0 0 * * *",
    func(logger *server.JobExecutionLogger) {
        logger.Start()
        cutoff := time.Now().AddDate(0, 0, -7) // 7 days
        _, err := app.Delete("_job_logs", "start_time < {:cutoff}", dbx.Params{"cutoff": cutoff})
        if err != nil {
            logger.Fail(err.Error())
        } else {
            logger.Success("Logs cleaned")
        }
    },
)

Best Practices

Don't Create These Collections

Never manually create collections with reserved names. Let pb-ext handle initialization.

Read-Only Access

Treat these as read-only from your application code. Write operations should go through pb-ext APIs.

Monitor Disk Usage

If analytics volume is high, consider shorter retention periods or archival strategies.

Backup Carefully

These collections change frequently. Exclude from backups or use incremental strategies.

Further Reading