Xata Postgres Platform
Xata Postgres 平台
Skill by
ara.so — Daily 2026 Skills collection.
Xata is an open-source, cloud-native Postgres platform built on Kubernetes that provides copy-on-write (CoW) branching, scale-to-zero, auto-scaling, high-availability, PITR backups, and a serverless SQL driver. It sits on top of
CloudNativePG and
OpenEBS.
由
ara.so提供的技能——Daily 2026技能合集。
Xata是一个基于Kubernetes构建的开源云原生Postgres平台,提供写时复制(CoW)分支、缩容至零、自动扩容、高可用、PITR备份以及无服务器SQL驱动。它构建在
CloudNativePG和
OpenEBS之上。
Architecture Overview
架构概述
┌─────────────────────────────────────────────────────┐
│ Xata Platform │
│ CLI ──► REST API (clusters/projects services) │
│ SQL Gateway (routing, scale-to-zero wakeup) │
│ Branch Operator (manages K8s resources per branch) │
│ Auth Service (Keycloak-based, RBAC API keys) │
├─────────────────────────────────────────────────────┤
│ CloudNativePG (HA, failover, backups, pooling) │
│ OpenEBS (local NVMe or Mayastor replicated storage) │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Xata Platform │
│ CLI ──► REST API (clusters/projects services) │
│ SQL Gateway (routing, scale-to-zero wakeup) │
│ Branch Operator (manages K8s resources per branch) │
│ Auth Service (Keycloak-based, RBAC API keys) │
├─────────────────────────────────────────────────────┤
│ CloudNativePG (HA, failover, backups, pooling) │
│ OpenEBS (local NVMe or Mayastor replicated storage) │
└─────────────────────────────────────────────────────┘
Local Development Setup
本地开发环境搭建
Step 1: Create Kind Cluster
步骤1:创建Kind集群
bash
kind create cluster --wait 10m
bash
kind create cluster --wait 10m
Step 2: Deploy with Tilt
步骤2:使用Tilt部署
Wait for all resources to become ready. First run downloads images and takes longer; subsequent runs are fast.
等待所有资源就绪。首次运行会下载镜像,耗时较长;后续运行速度较快。
Step 3: Install and Authenticate the CLI
步骤3:安装并认证CLI
Install Xata CLI
安装Xata CLI
Authenticate to local profile
认证到本地配置文件
Default credentials: email=dev@xata.tech, password=Xata1234!
默认凭据:email=dev@xata.tech,password=Xata1234!
xata auth login --profile local --env local --force
xata auth login --profile local --env local --force
Switch to the local profile
切换到本地配置文件
Step 4: Create Project and Branch
步骤4:创建项目和分支
Create a new project
创建新项目
xata project create --name my-project
xata project create --name my-project
Create the main branch (done automatically with project)
创建主分支(创建项目时自动完成)
Create a child branch (CoW copy of parent)
创建子分支(父分支的CoW副本)
Login to Xata Cloud
登录Xata云平台
Login to self-hosted local instance
登录自托管本地实例
xata auth login --profile local --env local --force
xata auth login --profile local --env local --force
Switch between profiles
在配置文件间切换
xata auth switch local
xata auth switch default
xata auth switch local
xata auth switch default
xata project create --name my-project
xata project create --name my-project
Get project details
获取项目详情
xata project get <project-id>
xata project get <project-id>
xata project delete <project-id>
xata project delete <project-id>
Create a branch (CoW snapshot of parent, completes in seconds even for TB of data)
创建分支(父分支的CoW快照,即使是TB级数据也能在数秒内完成)
Create a named branch from a specific parent
从指定父分支创建命名分支
xata branch create --name feature-xyz --from main
xata branch create --name feature-xyz --from main
xata branch get <branch-id>
xata branch get <branch-id>
xata branch delete <branch-id>
xata branch delete <branch-id>
Connecting to a Branch
连接到分支
Get connection string for a branch
获取分支的连接字符串
xata branch connection-string <branch-id>
xata branch connection-string <branch-id>
psql "$(xata branch connection-string <branch-id>)"
psql "$(xata branch connection-string <branch-id>)"
Environment Variables
环境变量
Xata API endpoint (for self-hosted)
Xata API端点(自托管环境)
API key for authentication
用于认证的API密钥
export XATA_API_KEY=your_api_key_here
export XATA_API_KEY=your_api_key_here
export XATA_PROJECT_ID=your_project_id
export XATA_PROJECT_ID=your_project_id
API Key Management (RBAC)
API密钥管理(RBAC)
Create an API key with specific permissions
创建具有特定权限的API密钥
xata apikey create --name ci-key --role branch:read,branch:write
xata apikey create --name ci-key --role branch:read,branch:write
xata apikey delete <key-id>
xata apikey delete <key-id>
Serverless SQL Driver (SQL over HTTP/WebSockets)
无服务器SQL驱动(基于HTTP/WebSockets的SQL)
Xata exposes a serverless driver for executing SQL over HTTP or WebSockets, useful for edge functions and environments without persistent TCP connections.
Xata提供了无服务器驱动,可通过HTTP或WebSockets执行SQL,适用于边缘函数和无持久TCP连接的环境。
HTTP SQL Query (Go)
HTTP SQL查询(Go)
go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
type SQLRequest struct {
Query string `json:"query"`
Params []any `json:"params,omitempty"`
}
type SQLResponse struct {
Records []map[string]any `json:"records"`
Error string `json:"error,omitempty"`
}
func queryXata(query string, params ...any) (*SQLResponse, error) {
apiURL := os.Getenv("XATA_API_URL")
apiKey := os.Getenv("XATA_API_KEY")
branchID := os.Getenv("XATA_BRANCH_ID")
reqBody := SQLRequest{Query: query, Params: params}
data, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
url := fmt.Sprintf("%s/branches/%s/sql", apiURL, branchID)
req, err := http.NewRequest("POST", url, bytes.NewReader(data))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var sqlResp SQLResponse
if err := json.NewDecoder(resp.Body).Decode(&sqlResp); err != nil {
return nil, err
}
return &sqlResp, nil
}
func main() {
result, err := queryXata("SELECT id, name FROM users WHERE active = $1", true)
if err != nil {
panic(err)
}
for _, record := range result.Records {
fmt.Printf("User: %v\n", record)
}
}
go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
type SQLRequest struct {
Query string `json:"query"`
Params []any `json:"params,omitempty"`
}
type SQLResponse struct {
Records []map[string]any `json:"records"`
Error string `json:"error,omitempty"`
}
// queryXata通过Xata的HTTP API执行SQL查询
func queryXata(query string, params ...any) (*SQLResponse, error) {
apiURL := os.Getenv("XATA_API_URL")
apiKey := os.Getenv("XATA_API_KEY")
branchID := os.Getenv("XATA_BRANCH_ID")
reqBody := SQLRequest{Query: query, Params: params}
data, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
url := fmt.Sprintf("%s/branches/%s/sql", apiURL, branchID)
req, err := http.NewRequest("POST", url, bytes.NewReader(data))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var sqlResp SQLResponse
if err := json.NewDecoder(resp.Body).Decode(&sqlResp); err != nil {
return nil, err
}
return &sqlResp, nil
}
func main() {
result, err := queryXata("SELECT id, name FROM users WHERE active = $1", true)
if err != nil {
panic(err)
}
for _, record := range result.Records {
fmt.Printf("用户: %v\n", record)
}
}
Direct Postgres Connection (Go)
直接Postgres连接(Go)
go
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
connStr := os.Getenv("XATA_DATABASE_URL")
// e.g. postgresql://user:pass@sql-gateway-host:5432/dbname
conn, err := pgx.Connect(context.Background(), connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
rows, err := conn.Query(context.Background(), "SELECT id, name FROM users LIMIT 10")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
panic(err)
}
fmt.Printf("id=%d name=%s\n", id, name)
}
}
go
package main
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
connStr := os.Getenv("XATA_DATABASE_URL")
// 示例:postgresql://user:pass@sql-gateway-host:5432/dbname
conn, err := pgx.Connect(context.Background(), connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "无法连接: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
rows, err := conn.Query(context.Background(), "SELECT id, name FROM users LIMIT 10")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
panic(err)
}
fmt.Printf("id=%d name=%s\n", id, name)
}
}
Preview Environment Workflow (CI/CD)
预览环境工作流(CI/CD)
Create a per-PR branch for isolated testing, then destroy it after merge:
create-preview.sh
create-preview.sh
set -euo pipefail
PR_NUMBER="${1:?PR number required}"
BRANCH_NAME="pr-${PR_NUMBER}"
set -euo pipefail
PR_NUMBER="${1:?需要PR编号}"
BRANCH_NAME="pr-${PR_NUMBER}"
Create a CoW branch from main (completes in seconds, even for TB of data)
从main分支创建CoW分支(数秒内完成,即使是TB级数据)
BRANCH_ID=$(xata branch create --name "$BRANCH_NAME" --from main --output json | jq -r '.id')
echo "Created branch: $BRANCH_ID"
BRANCH_ID=$(xata branch create --name "$BRANCH_NAME" --from main --output json | jq -r '.id')
echo "已创建分支: $BRANCH_ID"
Get connection string for tests
获取测试用的连接字符串
CONN_STR=$(xata branch connection-string "$BRANCH_ID")
echo "::set-output name=database_url::${CONN_STR}"
echo "::set-output name=branch_id::${BRANCH_ID}"
```bash
#!/usr/bin/env bash
CONN_STR=$(xata branch connection-string "$BRANCH_ID")
echo "::set-output name=database_url::${CONN_STR}"
echo "::set-output name=branch_id::${BRANCH_ID}"
```bash
#!/usr/bin/env bash
cleanup-preview.sh
cleanup-preview.sh
BRANCH_ID="${1:?Branch ID required}"
xata branch delete "$BRANCH_ID"
echo "Deleted branch $BRANCH_ID"
BRANCH_ID="${1:?需要分支ID}"
xata branch delete "$BRANCH_ID"
echo "已删除分支 $BRANCH_ID"
GitHub Actions Integration
GitHub Actions集成
yaml
name: Preview Environment
on:
pull_request:
types: [opened, synchronize]
jobs:
create-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Xata CLI
run: curl -fsSL https://xata.io/install.sh | bash
- name: Authenticate
env:
XATA_API_KEY: ${{ secrets.XATA_API_KEY }}
run: xata auth login --api-key "$XATA_API_KEY"
- name: Create Preview Branch
id: branch
run: |
BRANCH_ID=$(xata branch create \
--name "pr-${{ github.event.number }}" \
--from main \
--output json | jq -r '.id')
echo "branch_id=$BRANCH_ID" >> "$GITHUB_OUTPUT"
CONN=$(xata branch connection-string "$BRANCH_ID")
echo "database_url=$CONN" >> "$GITHUB_OUTPUT"
- name: Run Tests
env:
DATABASE_URL: ${{ steps.branch.outputs.database_url }}
run: go test ./...
cleanup:
runs-on: ubuntu-latest
needs: create-preview
if: always()
steps:
- name: Delete Preview Branch
env:
XATA_API_KEY: ${{ secrets.XATA_API_KEY }}
run: |
xata auth login --api-key "$XATA_API_KEY"
xata branch delete "${{ needs.create-preview.outputs.branch_id }}"
yaml
name: 预览环境
on:
pull_request:
types: [opened, synchronize]
jobs:
create-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装Xata CLI
run: curl -fsSL https://xata.io/install.sh | bash
- name: 认证
env:
XATA_API_KEY: ${{ secrets.XATA_API_KEY }}
run: xata auth login --api-key "$XATA_API_KEY"
- name: 创建预览分支
id: branch
run: |
BRANCH_ID=$(xata branch create \
--name "pr-${{ github.event.number }}" \
--from main \
--output json | jq -r '.id')
echo "branch_id=$BRANCH_ID" >> "$GITHUB_OUTPUT"
CONN=$(xata branch connection-string "$BRANCH_ID")
echo "database_url=$CONN" >> "$GITHUB_OUTPUT"
- name: 运行测试
env:
DATABASE_URL: ${{ steps.branch.outputs.database_url }}
run: go test ./...
cleanup:
runs-on: ubuntu-latest
needs: create-preview
if: always()
steps:
- name: 删除预览分支
env:
XATA_API_KEY: ${{ secrets.XATA_API_KEY }}
run: |
xata auth login --api-key "$XATA_API_KEY"
xata branch delete "${{ needs.create-preview.outputs.branch_id }}"
Programmatic Branch Management (Go)
程序化分支管理(Go)
go
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
type XataClient struct {
BaseURL string
APIKey string
Project string
http *http.Client
}
func NewXataClient() *XataClient {
return &XataClient{
BaseURL: os.Getenv("XATA_API_URL"),
APIKey: os.Getenv("XATA_API_KEY"),
Project: os.Getenv("XATA_PROJECT_ID"),
http: &http.Client{},
}
}
type Branch struct {
ID string `json:"id"`
Name string `json:"name"`
ParentID string `json:"parentId"`
State string `json:"state"`
ConnectionURL string `json:"connectionUrl"`
}
type CreateBranchRequest struct {
Name string `json:"name"`
ParentID string `json:"parentId"`
}
func (c *XataClient) CreateBranch(ctx context.Context, name, parentID string) (*Branch, error) {
payload := CreateBranchRequest{Name: name, ParentID: parentID}
data, _ := json.Marshal(payload)
url := fmt.Sprintf("%s/projects/%s/branches", c.BaseURL, c.Project)
req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(string(data)))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var branch Branch
if err := json.NewDecoder(resp.Body).Decode(&branch); err != nil {
return nil, err
}
return &branch, nil
}
func (c *XataClient) DeleteBranch(ctx context.Context, branchID string) error {
url := fmt.Sprintf("%s/projects/%s/branches/%s", c.BaseURL, c.Project, branchID)
req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+c.APIKey)
_, err = c.http.Do(req)
return err
}
func main() {
client := NewXataClient()
ctx := context.Background()
branch, err := client.CreateBranch(ctx, "feature-test", "main-branch-id")
if err != nil {
panic(err)
}
fmt.Printf("Created branch %s, connect at: %s\n", branch.ID, branch.ConnectionURL)
// ... run migrations, tests, etc. ...
if err := client.DeleteBranch(ctx, branch.ID); err != nil {
panic(err)
}
fmt.Println("Branch cleaned up")
}
go
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
type XataClient struct {
BaseURL string
APIKey string
Project string
http *http.Client
}
// NewXataClient创建Xata客户端实例
func NewXataClient() *XataClient {
return &XataClient{
BaseURL: os.Getenv("XATA_API_URL"),
APIKey: os.Getenv("XATA_API_KEY"),
Project: os.Getenv("XATA_PROJECT_ID"),
http: &http.Client{},
}
}
type Branch struct {
ID string `json:"id"`
Name string `json:"name"`
ParentID string `json:"parentId"`
State string `json:"state"`
ConnectionURL string `json:"connectionUrl"`
}
type CreateBranchRequest struct {
Name string `json:"name"`
ParentID string `json:"parentId"`
}
// CreateBranch创建新分支
func (c *XataClient) CreateBranch(ctx context.Context, name, parentID string) (*Branch, error) {
payload := CreateBranchRequest{Name: name, ParentID: parentID}
data, _ := json.Marshal(payload)
url := fmt.Sprintf("%s/projects/%s/branches", c.BaseURL, c.Project)
req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(string(data)))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var branch Branch
if err := json.NewDecoder(resp.Body).Decode(&branch); err != nil {
return nil, err
}
return &branch, nil
}
// DeleteBranch删除指定分支
func (c *XataClient) DeleteBranch(ctx context.Context, branchID string) error {
url := fmt.Sprintf("%s/projects/%s/branches/%s", c.BaseURL, c.Project, branchID)
req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+c.APIKey)
_, err = c.http.Do(req)
return err
}
func main() {
client := NewXataClient()
ctx := context.Background()
branch, err := client.CreateBranch(ctx, "feature-test", "main-branch-id")
if err != nil {
panic(err)
}
fmt.Printf("已创建分支 %s,连接地址: %s\n", branch.ID, branch.ConnectionURL)
// ... 运行迁移、测试等操作 ...
if err := client.DeleteBranch(ctx, branch.ID); err != nil {
panic(err)
}
fmt.Println("分支已清理")
}
Branches automatically hibernate after inactivity and wake up on the next connection. The SQL Gateway handles the wakeup transparently — clients may experience a short delay (~seconds) on the first connection after hibernation.
分支在闲置后会自动休眠,下次连接时唤醒。SQL网关会透明处理唤醒操作——客户端在休眠后的首次连接可能会遇到短暂延迟(约数秒)。
Manually hibernate a branch
手动休眠分支
xata branch hibernate <branch-id>
xata branch hibernate <branch-id>
xata branch wake <branch-id>
xata branch wake <branch-id>
Check branch state (running, hibernated, starting)
检查分支状态(运行中、已休眠、启动中)
xata branch get <branch-id> --output json | jq '.state'
xata branch get <branch-id> --output json | jq '.state'
Tilt/Kind Issues
Tilt/Kind问题
Check all pods are running
检查所有Pod是否运行
Check Xata-specific pods
检查Xata相关Pod
View logs for a specific component
查看特定组件的日志
kubectl logs -n xata -l app=sql-gateway --tail=100
kubectl logs -n xata -l app=branch-operator --tail=100
kubectl logs -n xata -l app=clusters --tail=100
kubectl logs -n xata -l app=sql-gateway --tail=100
kubectl logs -n xata -l app=branch-operator --tail=100
kubectl logs -n xata -l app=clusters --tail=100
Restart tilt if resources are stuck
如果资源卡住,重启Tilt
Branch Stuck in Provisioning
分支卡在配置中
Check branch operator logs
查看分支Operator日志
kubectl logs -n xata -l app=branch-operator -f
kubectl logs -n xata -l app=branch-operator -f
Check CloudNativePG cluster status
检查CloudNativePG集群状态
kubectl get clusters -A
kubectl describe cluster <cluster-name> -n <namespace>
kubectl get clusters -A
kubectl describe cluster <cluster-name> -n <namespace>
Check OpenEBS storage
检查OpenEBS存储
kubectl get pvc -A
kubectl describe pvc <pvc-name> -n <namespace>
kubectl get pvc -A
kubectl describe pvc <pvc-name> -n <namespace>
Connection Refused to SQL Gateway
SQL网关连接被拒绝
Verify SQL gateway is running
验证SQL网关是否运行
kubectl get svc -n xata sql-gateway
kubectl get svc -n xata sql-gateway
Port-forward for local testing
端口转发用于本地测试
kubectl port-forward -n xata svc/sql-gateway 5432:5432
kubectl port-forward -n xata svc/sql-gateway 5432:5432
psql "postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME"
psql "postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME"
CLI Authentication Failures
CLI认证失败
xata auth login --profile local --env local --force
xata auth login --profile local --env local --force
Verify current profile
验证当前配置文件
Check API key validity
检查API密钥有效性
Checking Scale-to-Zero Plugin
检查缩容至零插件
Verify CNPG plugin is installed
验证CNPG插件是否安装
kubectl get plugins -A | grep scale-to-zero
kubectl get plugins -A | grep scale-to-zero
Check hibernation annotations on a cluster
检查集群上的休眠注解
kubectl get cluster <name> -n <namespace> -o jsonpath='{.metadata.annotations}'
kubectl get cluster <name> -n <namespace> -o jsonpath='{.metadata.annotations}'
Key Components Reference
核心组件参考
| Component | Role |
|---|
| SQL Gateway | Connection routing, scale-to-zero wakeup, HTTP/WS serverless driver |
| Branch Operator | Kubernetes resource lifecycle per branch |
| clusters service | REST API for cluster management |
| projects service | REST API for project management |
| Auth service | Keycloak-based auth, RBAC API keys |
| CloudNativePG | HA Postgres, failover, backups, connection pooling |
| OpenEBS | Cloud-native storage (local NVMe or Mayastor replicated) |
| 组件 | 角色 |
|---|
| SQL Gateway | 连接路由、缩容至零唤醒、HTTP/WS无服务器驱动 |
| Branch Operator | 每个分支的Kubernetes资源生命周期管理 |
| clusters service | 集群管理REST API |
| projects service | 项目管理REST API |
| Auth service | 基于Keycloak的认证、RBAC API密钥 |
| CloudNativePG | 高可用Postgres、故障转移、备份、连接池 |
| OpenEBS | 云原生存储(本地NVMe或Mayastor复制存储) |
Use Case Decision Guide
使用场景决策指南
| Scenario | Use Xata OSS? |
|---|
| Internal Postgres-as-a-Service | ✅ Yes |
| Preview/testing/dev environments with CoW | ✅ Yes |
| Single Postgres instance | ❌ Use plain Postgres or managed service |
| Public PGaaS for end customers | ⚠️ Contact Xata for BYOC offering |
| 场景 | 是否使用Xata开源版? |
|---|
| 内部Postgres即服务 | ✅ 是 |
| 支持CoW的预览/测试/开发环境 | ✅ 是 |
| 单一Postgres实例 | ❌ 使用原生Postgres或托管服务 |
| 面向终端客户的公共PGaaS | ⚠️ 联系Xata获取BYOC方案 |