PostgreSQL Database Schemas for Sites, Users, and Memberships in Plausible Analytics
Plausible Analytics stores core account data in three main PostgreSQL tables—users, sites, and team_memberships—defined by the Ecto schemas Plausible.Auth.User, Plausible.Site, and Plausible.Teams.Membership.
The plausible/analytics repository implements a multi-tenant architecture where teams own analytics sites and users gain access through role-based memberships. Understanding these PostgreSQL database schemas is essential for anyone self-hosting Plausible or contributing to its Elixir/Phoenix codebase.
The users Table (Authentication and Identity)
The users table persists account data and authentication credentials through the Plausible.Auth.User schema defined in lib/plausible/auth/user.ex.
Core Fields
id–bigserialprimary keyemail–varcharstoring the unique user addresspassword_hash–byteacontaining the Bcrypt-hashed passwordname–varcharfor the full display namelast_seen–timestamptracking last activitytheme– PostgreSQLenum('system','light','dark')for UI preferencesemail_verified–booleanflag for verification statusprevious_email–varcharholding the former address during pending changesnotes–textfield for CRM annotationstotp_enabled–booleantoggling TOTP 2FAtotp_secret– encryptedbyteafor token generationtotp_token– temporaryvarcharfor current tokenstotp_last_used_at–timestampof last authenticationlast_team_identifier–uuidreferencing the last active team
Enterprise-Only Fields
The following columns are created only in the Enterprise Edition (EE) build:
type–enum('standard','sso')authentication methodsso_identity_id–varcharfrom the SSO providerlast_sso_login–timestampof the most recent SSO sign-insso_integration_id–bigintforeign key toauth_sso_integrationssso_domain_id–bigintforeign key toauth_sso_domains
Key Associations
The schema defines several Ecto relationships:
has_many :sessions→Plausible.Auth.UserSessionhas_many :team_memberships→Plausible.Teams.Membershiphas_many :api_keys→Plausible.Auth.ApiKeyhas_one :google_auth→Plausible.Site.GoogleAuth
The sites Table (Analytics Targets)
Defined in lib/plausible/site.ex, the sites table stores configuration for each tracked domain through the Plausible.Site schema.
Configuration Fields
id–bigserialprimary keydomain– uniquevarcharhostname (e.g.,example.com)timezone– IANA timezone string (e.g.,Etc/UTC)public–booleanvisibility flag for shared dashboardsstats_start_date–datemarking the first day of retained statisticsnative_stats_start_at–timestampfor precise native stats calculationallowed_event_props–text[]array whitelisting custom event propertiesconversions_enabled,props_enabled,funnels_enabled–booleantoggles for feature flags
Rate Limiting and Domain History
The table includes fields for API governance and domain changes:
ingest_rate_limit_scale_seconds–integertime window for rate limitingingest_rate_limit_threshold–integermax requests per windowdomain_changed_from–varcharprevious hostname after renamesdomain_changed_at–timestampof the rename operation
Team Ownership
team_id–bigintforeign key referencingteams.id, establishing the multi-tenant ownership model
Site Associations
has_many :guest_memberships→Plausible.Teams.GuestMembershiphas_many :goals→Plausible.Goalhas_one :tracker_script_configuration→Plausible.Site.TrackerScriptConfigurationhas_one :weekly_report/has_one :monthly_report→ reporting configurations
The team_memberships Table (Access Control)
The junction table team_memberships implements the many-to-many relationship between users and teams via the Plausible.Teams.Membership schema in lib/plausible/teams/membership.ex.
Membership Fields
id–bigserialprimary keyuser_id–bigintforeign key tousers.idteam_id–bigintforeign key toteams.idrole–enum('owner','admin','member')defining permission levelsis_autocreated–booleanset totruefor the initial membership when a user creates their first teaminserted_at/updated_at–timestampaudit columns
Database Indices
PostgreSQL enforces data integrity through:
- Unique index on
(user_id, team_id)preventing duplicate memberships - Index on
team_idfor fast team member lookups - Index on
rolefor efficient permission-based queries
How the Schemas Relate
These three tables form the backbone of Plausible's permission model. The users table authenticates individuals, sites belongs to a team through the team_id foreign key, and team_memberships grants users access to those sites with granular roles. When a user creates a new site, Plausible auto-creates a team and an owner membership with is_autocreated set to true.
Migration files in priv/repo/migrations/ (such as those adding notes to users or creating segments) maintain this schema structure across deployments.
Summary
usersstores authentication credentials, 2FA settings, and SSO metadata (EE only) inlib/plausible/auth/user.exsitescontains domain configuration, feature toggles, rate limits, and ateam_idforeign key inlib/plausible/site.exteam_membershipslinks users to teams with role-based access control and unique constraints inlib/plausible/teams/membership.ex- All tables use
bigserialprimary keys and support the multi-tenant architecture where teams own sites and users access them through memberships
Frequently Asked Questions
What PostgreSQL data type does Plausible use for primary keys?
Plausible uses bigserial (auto-incrementing 64-bit integers) for the id columns in users, sites, and team_memberships tables. This provides sufficient range for high-volume analytics installations while maintaining compatibility with Ecto's default id generation.
How are user passwords stored in the database?
Passwords are hashed using Bcrypt and stored in the password_hash column as bytea data. The raw password is never persisted; only the cryptographic hash remains in the PostgreSQL users table.
What is the difference between owner, admin, and member roles in team_memberships?
The role enum in team_memberships defines three permission tiers: owner provides full administrative rights including deletion, admin allows management of sites and members, and member grants basic viewing and editing capabilities. The is_autocreated flag specifically marks the initial owner membership created upon team creation.
Are the SSO-related columns available in the open-source version?
No. The type, sso_identity_id, last_sso_login, sso_integration_id, and sso_domain_id columns are only created in the Enterprise Edition (EE) build of Plausible Analytics. The open-source version uses standard email and password authentication exclusively.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →