Home

Self-Hosting Analytics

The Supabase Analytics server is a Logflare self-hostable instance that manages the ingestion and query pipelines for searching and aggregating structured analytics events.

When self-hosting the Analytics server, the full logging experience matching that of the Supabase Platform is available in the Studio instance, allowing for an integrated and enhanced development experience. However, it's important to note that certain differences may arise due to the platform's infrastructure.

Logflare Technical Docs

All logflare technical docs are available at https://docs.logflare.app

Getting Started#

Pre-requisites#

Logflare currently requires BigQuery usage. You will need to create a Google Cloud project with billing enabled.

The requirements are as follows after creating the project:

  • Project ID
  • Project number
  • A service account key

Setting up BigQuery Service Account

To ensure that you have sufficient permissions to insert into your Google Cloud BigQuery, ensure that you have created a service account with either:

  • BigQuery Admin role; or
  • The following permissions:
    • bigquery.datasets.create
    • bigquery.datasets.get
    • bigquery.datasets.getIamPolicy
    • bigquery.datasets.update
    • bigquery.jobs.create
    • bigquery.routines.create
    • bigquery.routines.update
    • bigquery.tables.create
    • bigquery.tables.delete
    • bigquery.tables.get
    • bigquery.tables.getData
    • bigquery.tables.update
    • bigquery.tables.updateData

You can create the service account via the web console or gcloud, as per the Google Cloud documentation. In the web console, you can create the key by navigating to IAM > Service Accounts > Actions (dropdown) > Manage Keys

We recommend setting the BigQuery Admin role, as it simplifies permissions setup.

Downloading the Service Account key

After the service account is created, you will need to create a key for the service account. This key will sign the JWTs for API requests that the Analytics server makes with BigQuery.

Docker Compose#

Using the example self-hosting stack based on docker-compose, you include the logging related services using the following command

You will first need to update the .env.example file with the necessary environment variables.

  • GOOGLE_PROJECT_ID
  • GOOGLE_PROJECT_NUMBER

You will need to place your Service Account key in your present working directory with the filename gcloud.json.

Thereafter, you can run the docker-compose commands and include the logging-specific compose file.

1# assuming you clone the supabase/supabase repo.
2cd docker
3docker compose -f docker-compose.yml -f docker-compose-logging.yml up

This would include two additional docker services to your compose stack: Logflare and Vector.

Self-hosting

Read more about self-hosting Logflare as your analytics server.

Vector Usage

In the Docker Compose example, we utilize vector to coordinate the logging pipeline between the services. However, if you peer into the vector configuration file, you will be able to see that Vector sends logs to the Analytics ingestion endpoint.

If you need to customize the logging pipeline for your own needs, you must ensure that the payloads matches the expected event schema structure. Without the correct structure, it would cause the Studio Logs UI features to break.

Standalone Docker Container#

If desired, you can utilize the standalone docker-container. Please refer to the docker-compose file for required docker configuration.

Additional supplementary technical documentation on self-hosting and using the supabase/logflare image for a full Logflare experience is available at the official Logflare documentation.

Differences#

API logs rely on Kong instead of the Supabase Cloud API Gateway. Logs from Kong are not enriched with platform-only data.

Within the self-hosted setup, all logs are routed to Logflare via Vector. As Kong routes API requests to PostgREST, self-hosted or local deployments will result in Kong request logs instead. This would result in differences in the log event metadata between self-hosted API requests and Supabase Platform requests.

BigQuery#

All log event data is stored in and queried from BigQuery. To use the Analytics server with Supabase you'll need a Google Cloud Platform account for access to BigQuery.

Make sure to set the GOOGLE_DATASET_ID_APPEND, GOOGLE_PROJECT_ID and GOOGLE_PROJECT_NUMBER environment variables.

Download your Google Cloud API JWT and store it under gcloud.json in your working directory.

note

You must also enable billing on your Google Cloud project, as the streaming inserts feature is required.

Production Recommendations#

To self-host in a production setting, we recommend performing the following for a better experience.

Ensure that Logflare is behind a firewall and restrict all network access to it besides safe requests.#

Self-hosted Logflare has UI authentication disabled and is intended for exposure to the internet. We recommend restricting access to the dashboard, accessible at the /dashboard path. If dashboard access is required for managing sources, we recommend having an authentication layer, such as a VPN.

Use a different Postgres Database to store Logflare data.#

Logflare requires a Postgres database to function. However, if there is an issue with you self-hosted Postgres service, you would not be able to debug it as it would also bring Logflare down together.

The self-hosted example is only used as a minimal example on running the entire stack, however it is not recommended to use the same database server for both production and observability.

List endpoints

get/api/endpoints

Responses

Endpoint List Response

1{
2  "schema": {
3    "items": {
4      "properties": {
5        "cache_duration_seconds": {
6          "type": "integer"
7        },
8        "enable_auth": {
9          "type": "boolean"
10        },
11        "inserted_at": {
12          "format": "date-time",
13          "type": "string"
14        },
15        "max_limit": {
16          "type": "integer"
17        },
18        "name": {
19          "type": "string"
20        },
21        "proactive_requerying_seconds": {
22          "type": "integer"
23        },
24        "query": {
25          "type": "string"
26        },
27        "sandboxable": {
28          "type": "boolean"
29        },
30        "source_mapping": {
31          "type": "object"
32        },
33        "token": {
34          "type": "string"
35        },
36        "updated_at": {
37          "format": "date-time",
38          "type": "string"
39        }
40      },
41      "required": [
42        "name",
43        "query"
44      ],
45      "title": "Endpoint",
46      "type": "object"
47    },
48    "type": "array"
49  }
50}

Create endpoint

post/api/endpoints

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "cache_duration_seconds": {
5        "type": "integer"
6      },
7      "enable_auth": {
8        "type": "boolean"
9      },
10      "inserted_at": {
11        "format": "date-time",
12        "type": "string"
13      },
14      "max_limit": {
15        "type": "integer"
16      },
17      "name": {
18        "type": "string"
19      },
20      "proactive_requerying_seconds": {
21        "type": "integer"
22      },
23      "query": {
24        "type": "string"
25      },
26      "sandboxable": {
27        "type": "boolean"
28      },
29      "source_mapping": {
30        "type": "object"
31      },
32      "token": {
33        "type": "string"
34      },
35      "updated_at": {
36        "format": "date-time",
37        "type": "string"
38      }
39    },
40    "required": [
41      "name",
42      "query"
43    ],
44    "title": "Endpoint",
45    "type": "object"
46  }
47}

Delete endpoint

delete/api/endpoints/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Endpoint Token

Responses

Accepted Response

Fetch endpoint

get/api/endpoints/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Endpoint Token

Responses

Endpoint Response

1{
2  "schema": {
3    "properties": {
4      "cache_duration_seconds": {
5        "type": "integer"
6      },
7      "enable_auth": {
8        "type": "boolean"
9      },
10      "inserted_at": {
11        "format": "date-time",
12        "type": "string"
13      },
14      "max_limit": {
15        "type": "integer"
16      },
17      "name": {
18        "type": "string"
19      },
20      "proactive_requerying_seconds": {
21        "type": "integer"
22      },
23      "query": {
24        "type": "string"
25      },
26      "sandboxable": {
27        "type": "boolean"
28      },
29      "source_mapping": {
30        "type": "object"
31      },
32      "token": {
33        "type": "string"
34      },
35      "updated_at": {
36        "format": "date-time",
37        "type": "string"
38      }
39    },
40    "required": [
41      "name",
42      "query"
43    ],
44    "title": "Endpoint",
45    "type": "object"
46  }
47}

Update endpoint

patch/api/endpoints/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Endpoint Token

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "cache_duration_seconds": {
5        "type": "integer"
6      },
7      "enable_auth": {
8        "type": "boolean"
9      },
10      "inserted_at": {
11        "format": "date-time",
12        "type": "string"
13      },
14      "max_limit": {
15        "type": "integer"
16      },
17      "name": {
18        "type": "string"
19      },
20      "proactive_requerying_seconds": {
21        "type": "integer"
22      },
23      "query": {
24        "type": "string"
25      },
26      "sandboxable": {
27        "type": "boolean"
28      },
29      "source_mapping": {
30        "type": "object"
31      },
32      "token": {
33        "type": "string"
34      },
35      "updated_at": {
36        "format": "date-time",
37        "type": "string"
38      }
39    },
40    "required": [
41      "name",
42      "query"
43    ],
44    "title": "Endpoint",
45    "type": "object"
46  }
47}

List sources

get/api/sources

Responses

Source List Response

1{
2  "schema": {
3    "items": {
4      "properties": {
5        "api_quota": {
6          "type": "integer"
7        },
8        "bigquery_table_ttl": {
9          "type": "integer"
10        },
11        "bq_table_id": {
12          "type": "string"
13        },
14        "bq_table_schema": {
15          "type": "object"
16        },
17        "custom_event_message_keys": {
18          "type": "string"
19        },
20        "favorite": {
21          "type": "boolean"
22        },
23        "has_rejected_events": {
24          "type": "boolean"
25        },
26        "id": {},
27        "inserted_at": {
28          "format": "date-time",
29          "type": "string"
30        },
31        "metrics": {
32          "type": "object"
33        },
34        "name": {
35          "type": "string"
36        },
37        "notifications": {
38          "items": {
39            "properties": {
40              "other_email_notifications": {
41                "type": "string"
42              },
43              "team_user_ids_for_email": {
44                "allOf": {
45                  "type": "string"
46                },
47                "type": "array"
48              },
49              "team_user_ids_for_schema_updates": {
50                "allOf": {
51                  "type": "string"
52                },
53                "type": "array"
54              },
55              "team_user_ids_for_sms": {
56                "allOf": {
57                  "type": "string"
58                },
59                "type": "array"
60              },
61              "user_email_notifications": {
62                "type": "boolean"
63              },
64              "user_schema_update_notifications": {
65                "type": "boolean"
66              },
67              "user_text_notifications": {
68                "type": "boolean"
69              }
70            },
71            "title": "Notification",
72            "type": "object"
73          },
74          "type": "array"
75        },
76        "public_token": {
77          "type": "string"
78        },
79        "slack_hook_url": {
80          "type": "string"
81        },
82        "token": {
83          "type": "string"
84        },
85        "updated_at": {
86          "format": "date-time",
87          "type": "string"
88        },
89        "webhook_notification_url": {
90          "type": "string"
91        }
92      },
93      "required": [
94        "name"
95      ],
96      "title": "Source",
97      "type": "object"
98    },
99    "type": "array"
100  }
101}

Create source

post/api/sources

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "api_quota": {
5        "type": "integer"
6      },
7      "bigquery_table_ttl": {
8        "type": "integer"
9      },
10      "bq_table_id": {
11        "type": "string"
12      },
13      "bq_table_schema": {
14        "type": "object"
15      },
16      "custom_event_message_keys": {
17        "type": "string"
18      },
19      "favorite": {
20        "type": "boolean"
21      },
22      "has_rejected_events": {
23        "type": "boolean"
24      },
25      "id": {},
26      "inserted_at": {
27        "format": "date-time",
28        "type": "string"
29      },
30      "metrics": {
31        "type": "object"
32      },
33      "name": {
34        "type": "string"
35      },
36      "notifications": {
37        "items": {
38          "properties": {
39            "other_email_notifications": {
40              "type": "string"
41            },
42            "team_user_ids_for_email": {
43              "allOf": {
44                "type": "string"
45              },
46              "type": "array"
47            },
48            "team_user_ids_for_schema_updates": {
49              "allOf": {
50                "type": "string"
51              },
52              "type": "array"
53            },
54            "team_user_ids_for_sms": {
55              "allOf": {
56                "type": "string"
57              },
58              "type": "array"
59            },
60            "user_email_notifications": {
61              "type": "boolean"
62            },
63            "user_schema_update_notifications": {
64              "type": "boolean"
65            },
66            "user_text_notifications": {
67              "type": "boolean"
68            }
69          },
70          "title": "Notification",
71          "type": "object"
72        },
73        "type": "array"
74      },
75      "public_token": {
76        "type": "string"
77      },
78      "slack_hook_url": {
79        "type": "string"
80      },
81      "token": {
82        "type": "string"
83      },
84      "updated_at": {
85        "format": "date-time",
86        "type": "string"
87      },
88      "webhook_notification_url": {
89        "type": "string"
90      }
91    },
92    "required": [
93      "name"
94    ],
95    "title": "Source",
96    "type": "object"
97  }
98}

Delete source

delete/api/sources/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Source Token

Responses

Accepted Response

Fetch source

get/api/sources/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Source Token

Responses

Source Response

1{
2  "schema": {
3    "properties": {
4      "api_quota": {
5        "type": "integer"
6      },
7      "bigquery_table_ttl": {
8        "type": "integer"
9      },
10      "bq_table_id": {
11        "type": "string"
12      },
13      "bq_table_schema": {
14        "type": "object"
15      },
16      "custom_event_message_keys": {
17        "type": "string"
18      },
19      "favorite": {
20        "type": "boolean"
21      },
22      "has_rejected_events": {
23        "type": "boolean"
24      },
25      "id": {},
26      "inserted_at": {
27        "format": "date-time",
28        "type": "string"
29      },
30      "metrics": {
31        "type": "object"
32      },
33      "name": {
34        "type": "string"
35      },
36      "notifications": {
37        "items": {
38          "properties": {
39            "other_email_notifications": {
40              "type": "string"
41            },
42            "team_user_ids_for_email": {
43              "allOf": {
44                "type": "string"
45              },
46              "type": "array"
47            },
48            "team_user_ids_for_schema_updates": {
49              "allOf": {
50                "type": "string"
51              },
52              "type": "array"
53            },
54            "team_user_ids_for_sms": {
55              "allOf": {
56                "type": "string"
57              },
58              "type": "array"
59            },
60            "user_email_notifications": {
61              "type": "boolean"
62            },
63            "user_schema_update_notifications": {
64              "type": "boolean"
65            },
66            "user_text_notifications": {
67              "type": "boolean"
68            }
69          },
70          "title": "Notification",
71          "type": "object"
72        },
73        "type": "array"
74      },
75      "public_token": {
76        "type": "string"
77      },
78      "slack_hook_url": {
79        "type": "string"
80      },
81      "token": {
82        "type": "string"
83      },
84      "updated_at": {
85        "format": "date-time",
86        "type": "string"
87      },
88      "webhook_notification_url": {
89        "type": "string"
90      }
91    },
92    "required": [
93      "name"
94    ],
95    "title": "Source",
96    "type": "object"
97  }
98}

Update source

patch/api/sources/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Source Token

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "api_quota": {
5        "type": "integer"
6      },
7      "bigquery_table_ttl": {
8        "type": "integer"
9      },
10      "bq_table_id": {
11        "type": "string"
12      },
13      "bq_table_schema": {
14        "type": "object"
15      },
16      "custom_event_message_keys": {
17        "type": "string"
18      },
19      "favorite": {
20        "type": "boolean"
21      },
22      "has_rejected_events": {
23        "type": "boolean"
24      },
25      "id": {},
26      "inserted_at": {
27        "format": "date-time",
28        "type": "string"
29      },
30      "metrics": {
31        "type": "object"
32      },
33      "name": {
34        "type": "string"
35      },
36      "notifications": {
37        "items": {
38          "properties": {
39            "other_email_notifications": {
40              "type": "string"
41            },
42            "team_user_ids_for_email": {
43              "allOf": {
44                "type": "string"
45              },
46              "type": "array"
47            },
48            "team_user_ids_for_schema_updates": {
49              "allOf": {
50                "type": "string"
51              },
52              "type": "array"
53            },
54            "team_user_ids_for_sms": {
55              "allOf": {
56                "type": "string"
57              },
58              "type": "array"
59            },
60            "user_email_notifications": {
61              "type": "boolean"
62            },
63            "user_schema_update_notifications": {
64              "type": "boolean"
65            },
66            "user_text_notifications": {
67              "type": "boolean"
68            }
69          },
70          "title": "Notification",
71          "type": "object"
72        },
73        "type": "array"
74      },
75      "public_token": {
76        "type": "string"
77      },
78      "slack_hook_url": {
79        "type": "string"
80      },
81      "token": {
82        "type": "string"
83      },
84      "updated_at": {
85        "format": "date-time",
86        "type": "string"
87      },
88      "webhook_notification_url": {
89        "type": "string"
90      }
91    },
92    "required": [
93      "name"
94    ],
95    "title": "Source",
96    "type": "object"
97  }
98}

List teams

get/api/teams

Responses

Team List Response

1{
2  "schema": {
3    "items": {
4      "properties": {
5        "name": {
6          "type": "string"
7        },
8        "team_users": {
9          "items": {
10            "properties": {
11              "email": {
12                "type": "string"
13              },
14              "name": {
15                "type": "string"
16              }
17            },
18            "required": [
19              "email",
20              "name"
21            ],
22            "title": "TeamUser",
23            "type": "object"
24          },
25          "type": "array"
26        },
27        "token": {
28          "type": "string"
29        },
30        "user": {
31          "properties": {
32            "api_key": {
33              "type": "string"
34            },
35            "api_quota": {
36              "type": "integer"
37            },
38            "bigquery_dataset_id": {
39              "type": "string"
40            },
41            "bigquery_dataset_location": {
42              "type": "string"
43            },
44            "bigquery_project_id": {
45              "type": "string"
46            },
47            "company": {
48              "type": "string"
49            },
50            "email": {
51              "type": "string"
52            },
53            "email_me_product": {
54              "type": "boolean"
55            },
56            "email_preferred": {
57              "type": "string"
58            },
59            "image": {
60              "type": "string"
61            },
62            "name": {
63              "type": "string"
64            },
65            "phone": {
66              "type": "string"
67            },
68            "provider": {
69              "type": "string"
70            },
71            "token": {
72              "type": "string"
73            }
74          },
75          "required": [
76            "email",
77            "provider",
78            "token",
79            "provider_uid",
80            "api_key"
81          ],
82          "title": "User",
83          "type": "object"
84        }
85      },
86      "required": [
87        "name"
88      ],
89      "title": "Team",
90      "type": "object"
91    },
92    "type": "array"
93  }
94}

Create Team

post/api/teams

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "name": {
5        "type": "string"
6      },
7      "team_users": {
8        "items": {
9          "properties": {
10            "email": {
11              "type": "string"
12            },
13            "name": {
14              "type": "string"
15            }
16          },
17          "required": [
18            "email",
19            "name"
20          ],
21          "title": "TeamUser",
22          "type": "object"
23        },
24        "type": "array"
25      },
26      "token": {
27        "type": "string"
28      },
29      "user": {
30        "properties": {
31          "api_key": {
32            "type": "string"
33          },
34          "api_quota": {
35            "type": "integer"
36          },
37          "bigquery_dataset_id": {
38            "type": "string"
39          },
40          "bigquery_dataset_location": {
41            "type": "string"
42          },
43          "bigquery_project_id": {
44            "type": "string"
45          },
46          "company": {
47            "type": "string"
48          },
49          "email": {
50            "type": "string"
51          },
52          "email_me_product": {
53            "type": "boolean"
54          },
55          "email_preferred": {
56            "type": "string"
57          },
58          "image": {
59            "type": "string"
60          },
61          "name": {
62            "type": "string"
63          },
64          "phone": {
65            "type": "string"
66          },
67          "provider": {
68            "type": "string"
69          },
70          "token": {
71            "type": "string"
72          }
73        },
74        "required": [
75          "email",
76          "provider",
77          "token",
78          "provider_uid",
79          "api_key"
80        ],
81        "title": "User",
82        "type": "object"
83      }
84    },
85    "required": [
86      "name"
87    ],
88    "title": "Team",
89    "type": "object"
90  }
91}

Delete Team

delete/api/teams/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Team Token

Responses

Accepted Response

Fetch team

get/api/teams/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Team Token

Responses

Team Response

1{
2  "schema": {
3    "properties": {
4      "name": {
5        "type": "string"
6      },
7      "team_users": {
8        "items": {
9          "properties": {
10            "email": {
11              "type": "string"
12            },
13            "name": {
14              "type": "string"
15            }
16          },
17          "required": [
18            "email",
19            "name"
20          ],
21          "title": "TeamUser",
22          "type": "object"
23        },
24        "type": "array"
25      },
26      "token": {
27        "type": "string"
28      },
29      "user": {
30        "properties": {
31          "api_key": {
32            "type": "string"
33          },
34          "api_quota": {
35            "type": "integer"
36          },
37          "bigquery_dataset_id": {
38            "type": "string"
39          },
40          "bigquery_dataset_location": {
41            "type": "string"
42          },
43          "bigquery_project_id": {
44            "type": "string"
45          },
46          "company": {
47            "type": "string"
48          },
49          "email": {
50            "type": "string"
51          },
52          "email_me_product": {
53            "type": "boolean"
54          },
55          "email_preferred": {
56            "type": "string"
57          },
58          "image": {
59            "type": "string"
60          },
61          "name": {
62            "type": "string"
63          },
64          "phone": {
65            "type": "string"
66          },
67          "provider": {
68            "type": "string"
69          },
70          "token": {
71            "type": "string"
72          }
73        },
74        "required": [
75          "email",
76          "provider",
77          "token",
78          "provider_uid",
79          "api_key"
80        ],
81        "title": "User",
82        "type": "object"
83      }
84    },
85    "required": [
86      "name"
87    ],
88    "title": "Team",
89    "type": "object"
90  }
91}

Update team

patch/api/teams/{token}

Path Parameters
  • token
    REQUIRED
    no type

    Team Token

Responses

Created Response

1{
2  "schema": {
3    "properties": {
4      "name": {
5        "type": "string"
6      },
7      "team_users": {
8        "items": {
9          "properties": {
10            "email": {
11              "type": "string"
12            },
13            "name": {
14              "type": "string"
15            }
16          },
17          "required": [
18            "email",
19            "name"
20          ],
21          "title": "TeamUser",
22          "type": "object"
23        },
24        "type": "array"
25      },
26      "token": {
27        "type": "string"
28      },
29      "user": {
30        "properties": {
31          "api_key": {
32            "type": "string"
33          },
34          "api_quota": {
35            "type": "integer"
36          },
37          "bigquery_dataset_id": {
38            "type": "string"
39          },
40          "bigquery_dataset_location": {
41            "type": "string"
42          },
43          "bigquery_project_id": {
44            "type": "string"
45          },
46          "company": {
47            "type": "string"
48          },
49          "email": {
50            "type": "string"
51          },
52          "email_me_product": {
53            "type": "boolean"
54          },
55          "email_preferred": {
56            "type": "string"
57          },
58          "image": {
59            "type": "string"
60          },
61          "name": {
62            "type": "string"
63          },
64          "phone": {
65            "type": "string"
66          },
67          "provider": {
68            "type": "string"
69          },
70          "token": {
71            "type": "string"
72          }
73        },
74        "required": [
75          "email",
76          "provider",
77          "token",
78          "provider_uid",
79          "api_key"
80        ],
81        "title": "User",
82        "type": "object"
83      }
84    },
85    "required": [
86      "name"
87    ],
88    "title": "Team",
89    "type": "object"
90  }
91}