# Ask HN: Semantic Search Demo Source: https://docs.rivestack.io/ask-hn-demo Live demo: semantic search over 30 days of Hacker News stories and comments, powered by Rivestack pgvector ## Live Demo A single-page semantic search app over Hacker News, showcasing Rivestack pgvector in action. Search 30 days of HN stories and comments with natural language. Full source code: backend, frontend, and Kubernetes deployment. ## What it does Type a question, get semantically relevant Hacker News posts and comments, with real-time performance stats on every query. * **\~4,000 stories** and **\~40,000 chunks** indexed from the last 30 days of HN * **HNSW index** for fast cosine similarity search * **Performance stats** displayed on every query: vector query time, embedding time, chunks searched, index type * **Daily cron job** keeps a rolling 30-day window of fresh data ## Architecture ``` User query → OpenAI embedding → pgvector HNSW search → ranked results ``` | Component | Stack | | -------------- | ------------------------------------------- | | **Database** | Rivestack PostgreSQL + pgvector | | **Backend** | Python, FastAPI, SQLAlchemy, asyncpg | | **Embeddings** | OpenAI `text-embedding-3-small` (1536 dims) | | **Frontend** | Vue 3, Vite, Tailwind CSS v4, shadcn-vue | | **Deployment** | Kubernetes, Helm, GitHub Actions CI/CD | ## Performance Typical query performance on a **shared** Rivestack instance: | Metric | Time | | ----------------------- | ---------- | | Vector query (pgvector) | **\~12ms** | | OpenAI embedding | \~150ms | | Total API response | \~180ms | These numbers are from a shared Rivestack instance. Dedicated instances deliver significantly faster performance. ## Run it yourself ### 1. Create a Rivestack database Sign up at [app.rivestack.io](https://app.rivestack.io) and create a database. pgvector is enabled by default on the initial `appdb` database. For new databases, add it through the dashboard or Terraform. ### 2. Clone and configure ```bash theme={null} git clone https://github.com/Rivestack/rag-starter.git cd rag-starter cp .env.example backend/.env ``` Edit `backend/.env` with your Rivestack `DATABASE_URL` and an `OPENAI_API_KEY`. ### 3. Run with Docker Compose ```bash theme={null} docker compose up ``` * Backend: [http://localhost:8000](http://localhost:8000) * Frontend: [http://localhost:8080](http://localhost:8080) ### 4. Load data ```bash theme={null} curl -X POST http://localhost:8000/api/ingest/initial ``` This fetches 30 days of HN stories, generates embeddings, and stores them in your Rivestack database. The first load runs in the background and takes a few minutes. ### 5. Search Open [http://localhost:8080](http://localhost:8080) and start searching. # Backups & Restore Source: https://docs.rivestack.io/backups Automated backups, retention policies, and point-in-time recovery ## Overview Rivestack Solo and HA clusters include automated daily backups stored in S3-compatible object storage. Backups use [pgBackRest](https://pgbackrest.org/), the industry-standard backup tool for PostgreSQL, with support for full backups and point-in-time recovery (PITR). Automated backups are available on **Solo and HA clusters**. Free tier databases do not include backups. [Upgrade to Solo](/pricing) for daily backups and point-in-time recovery. ## How backups work * **Backup tool**: pgBackRest * **Storage**: S3-compatible object storage (off-site, separate from your database nodes) * **Backup type**: Full backups * **WAL archiving**: Continuous WAL (Write-Ahead Log) archiving for point-in-time recovery * **Transport encryption**: Backup traffic is sent to object storage over TLS * **Repository encryption**: The current pgBackRest configuration does not add client-side backup encryption Every backup captures a consistent snapshot of your entire PostgreSQL cluster, including all databases, users, and extensions. ## Automated schedule By default, backups run **daily at 3:00 AM UTC**. You can customize the schedule from the dashboard. ### View backup schedule 1. Navigate to your HA cluster in the dashboard. 2. Open the **Backup** tab. 3. The current schedule is shown at the top. ### Change backup schedule 1. Open the **Backup** tab of your cluster. 2. Click on the backup schedule configuration. 3. Set your preferred cron schedule. 4. Save changes. ## Retention | Setting | Default | | ----------------------------------- | ------- | | Full backup retention (Solo) | 7 days | | Full backup retention (HA clusters) | 14 days | pgBackRest is configured to expire repository backups outside the retention window. Protected copies of manually triggered backups can remain visible after the original repository backup expires; contact support before relying on a protected copy for restore. Rivestack does not currently advertise customer-managed or client-side encryption for the backup repository. If your workload requires a specific object-storage encryption or compliance control, contact us before relying on the service for that requirement. ## Manual backups You can trigger a backup at any time: 1. Navigate to your cluster's **Backup** tab. 2. Click **Create Backup**. 3. The backup starts immediately. You can monitor progress in real time. Each backup record shows: * Backup name and type * Size (in MB) * Status (running, success, failed) * Timestamp ## Restore Rivestack supports three restore methods: ### Restore from latest backup Restores your cluster to the most recent successful backup. 1. Open the **Backup** tab. 2. Click the restore icon on the desired backup. 3. Select **Latest** as the restore type. 4. Type "I confirm" to proceed. 5. Click **Restore**. ### Point-in-time recovery (PITR) Restore your cluster to any specific moment within your backup retention window. This is useful for recovering from accidental data deletion or corruption. 1. Open the **Backup** tab. 2. Click the restore icon. 3. Select **PITR** as the restore type. 4. Choose the target **date and time** (UTC). 5. Type "I confirm" to proceed. 6. Click **Restore**. PITR requires continuous WAL archiving. The target timestamp must fall within the retention window of your backups. ### Restore from a specific backup Choose a specific backup from your history to restore from. 1. Open the **Backup** tab. 2. Click the restore icon. 3. Select **Specific backup** as the restore type. 4. Choose the backup from the dropdown. 5. Type "I confirm" to proceed. 6. Click **Restore**. ## What to expect during a restore * The restore runs as a background job. You can monitor progress in real time on the dashboard. * Your cluster will show a **Restoring** status during the operation. * Existing data on the cluster will be replaced with the restored data. * Connections may be briefly interrupted during the restore. * You'll receive an email notification when the restore completes. ## Restore history View all past restore operations in the **Backup** tab. Each record shows: * Source backup * Restore type (latest, PITR, specific) * Status (success, failed, running) * Start and finish timestamps * Error details (if failed) # Changelog Source: https://docs.rivestack.io/changelog New features and improvements to Rivestack ## July 2026 ### Moved to the AMD CPX line, with updated pricing Every EU tier now runs on Hetzner's AMD CPX line (shared vCPU on AMD EPYC): faster cores and far better availability than the older Intel CX line. Pricing and Solo specs changed with the move: * **Solo** is now **\$29/month** in every region, on a 1 vCPU / 2 GB / 30 GB box (`cpx12`; US East's `cpx11` over-delivers at 2 vCPU). * **HA tiers** are **$49 Starter / $85 Growth / \$159 Scale** per node in EU Central (US East higher). vCPU, RAM, and storage per tier are unchanged. * Every customer database is a single-tenant VM. CPX is shared vCPU, not CPU-isolated; the win over CX is faster cores and stock availability. See [Pricing](/pricing) for the full comparison. ## June 2026 ### Solo plan: dedicated Postgres from \$15/month A new tier between the free shared tier and HA clusters: a **single-tenant single-VM PostgreSQL** instance with no HA overhead. * 2 vCPU · 4 GB RAM, 55 GB NVMe storage (EU) at launch * Your own VM, not shared with other customers' databases * `pgvector` enabled * Daily backups with 7-day point-in-time recovery * SSL enforced First month is free, applied at checkout. See [Pricing](/pricing) for the full plan comparison. ### Table Editor: browse and edit your data like a spreadsheet A new **Table Editor** in the dashboard lets you work with your tables visually, without writing SQL: * Browse tables with row-count and size estimates * View paginated, sortable rows in a spreadsheet-style grid * Edit cells inline, insert rows, and delete rows * Works on both Solo/shared databases and HA clusters Pagination uses the keyset ("seek") method where a primary key is available, so paging stays fast even on large tables. Tables without a primary key open in read-only mode to keep edits safe. # Connection Guide Source: https://docs.rivestack.io/connection-guide Connect to your Rivestack PostgreSQL database from any language ## Quick connect Copy your connection string from the dashboard and use it directly: ```bash theme={null} pip install psycopg2-binary ``` ```python theme={null} import psycopg2 conn = psycopg2.connect("postgresql://user:pass@rs-xxx.eu.db.rivestack.io:6432/mydb?sslmode=verify-full&sslrootcert=system") cur = conn.cursor() cur.execute("SELECT 1") print(cur.fetchone()) conn.close() ``` ```bash theme={null} npm install pg ``` ```javascript theme={null} const { Client } = require('pg'); const client = new Client({ connectionString: 'postgresql://user:pass@rs-xxx.eu.db.rivestack.io:6432/mydb?sslmode=verify-full' }); await client.connect(); const res = await client.query('SELECT 1'); console.log(res.rows[0]); await client.end(); ``` ```bash theme={null} go get github.com/jackc/pgx/v5 ``` ```go theme={null} conn, err := pgx.Connect(context.Background(), "postgresql://user:pass@rs-xxx.eu.db.rivestack.io:6432/mydb?sslmode=verify-full") if err != nil { log.Fatal(err) } defer conn.Close(context.Background()) ``` ```bash theme={null} psql "postgresql://user:pass@rs-xxx.eu.db.rivestack.io:6432/mydb?sslmode=verify-full&sslrootcert=system" ``` The Python and psql examples carry `sslrootcert=system` because libpq does not read your operating system's trust store on its own. Leave it out and the connection fails with `root certificate file "~/.postgresql/root.crt" does not exist`. See [SSL / TLS](#ssl-tls) for the detail and for what to use on libpq older than 16. ## Connection details Find your connection credentials in the Rivestack dashboard under the **Connection** tab of your database or cluster. | Parameter | Description | Example | | --------- | ------------------------------------- | -------------------------------- | | Host | Your unique endpoint | `rs-abc12345.eu.db.rivestack.io` | | Port | PostgreSQL port | `6432` | | Database | Database name | `mydb` | | Username | Database user | `myuser` | | Password | User password | (from dashboard) | | SSL Mode | Certificate and hostname verification | `verify-full` | Port `6432` applies to every dedicated plan — Solo, Starter, Growth and Scale — on a single node and after scaling to HA alike. Free shared databases use `5432`. Copying the connection string from the dashboard always gives you the right one. ## Connection string ``` postgresql://username:password@host:6432/dbname?sslmode=verify-full ``` For psql and the Python drivers, add `sslrootcert=system`: ``` postgresql://username:password@host:6432/dbname?sslmode=verify-full&sslrootcert=system ``` Click the copy button next to each field in the dashboard to copy values to your clipboard. ## SSL / TLS All Rivestack connections **require SSL**. Unencrypted connections are rejected. Use `sslmode=verify-full` so the client also verifies the CA chain and confirms that the certificate matches the database hostname. `sslmode=require` encrypts traffic but does not authenticate the server identity. There is no CA certificate to download. Rivestack certificates are issued by Let's Encrypt, which every operating system already trusts. ### Point libpq at your system's trusted roots psql and the Python drivers are built on libpq, and libpq does **not** read your operating system's trust store on its own. With `sslmode=verify-full` it looks for a file at `~/.postgresql/root.crt`, and on most machines that file does not exist, so the connection fails before it reaches the server: ``` psql: error: connection to server at "rs-abc12345.eu.db.rivestack.io" failed: root certificate file "/home/you/.postgresql/root.crt" does not exist ``` Add `sslrootcert=system` to tell libpq to use the roots your system already trusts: ```bash theme={null} psql "postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full&sslrootcert=system" ``` `sslrootcert=system` requires libpq 16 or newer. On older clients, give it the path to your system bundle instead: `/etc/ssl/certs/ca-certificates.crt` on Debian and Ubuntu, `/etc/pki/tls/certs/ca-bundle.crt` on RHEL and Fedora. Node.js, Go and Java drivers do not need this parameter. They use their own TLS stacks, which trust the system roots by default. ## Connect with psql ```bash theme={null} psql "host=rs-abc12345.eu.db.rivestack.io port=6432 dbname=mydb user=myuser sslmode=verify-full sslrootcert=system" ``` You'll be prompted for your password. To include it inline: ```bash theme={null} psql "postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full&sslrootcert=system" ``` ## Language examples ### Using psycopg2 ```bash theme={null} pip install psycopg2-binary ``` ```python theme={null} import psycopg2 conn = psycopg2.connect( host="rs-abc12345.eu.db.rivestack.io", port=6432, dbname="mydb", user="myuser", password="mypassword", sslmode="verify-full", sslrootcert="system" ) cur = conn.cursor() cur.execute("SELECT version()") print(cur.fetchone()) cur.close() conn.close() ``` ### Using SQLAlchemy ```bash theme={null} pip install sqlalchemy psycopg2-binary ``` ```python theme={null} from sqlalchemy import create_engine engine = create_engine( "postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb", connect_args={"sslmode": "verify-full"} ) with engine.connect() as conn: result = conn.execute("SELECT version()") print(result.fetchone()) ``` ### Using pg (node-postgres) ```bash theme={null} npm install pg ``` ```javascript theme={null} const { Client } = require('pg'); const client = new Client({ host: 'rs-abc12345.eu.db.rivestack.io', port: 6432, database: 'mydb', user: 'myuser', password: 'mypassword', ssl: { rejectUnauthorized: true } }); await client.connect(); const res = await client.query('SELECT version()'); console.log(res.rows[0]); await client.end(); ``` ### Using a connection string ```javascript theme={null} const { Client } = require('pg'); const client = new Client({ connectionString: 'postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full' }); await client.connect(); ``` ### Using Prisma Set your `DATABASE_URL` in `.env`: ``` DATABASE_URL="postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full" ``` ### Using pgx ```bash theme={null} go get github.com/jackc/pgx/v5 ``` ```go theme={null} package main import ( "context" "fmt" "log" "github.com/jackc/pgx/v5" ) func main() { connStr := "postgresql://myuser:mypassword@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full" conn, err := pgx.Connect(context.Background(), connStr) if err != nil { log.Fatal(err) } defer conn.Close(context.Background()) var version string err = conn.QueryRow(context.Background(), "SELECT version()").Scan(&version) if err != nil { log.Fatal(err) } fmt.Println(version) } ``` ### Using database/sql ```go theme={null} package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" ) func main() { connStr := "host=rs-abc12345.eu.db.rivestack.io port=6432 user=myuser password=mypassword dbname=mydb sslmode=verify-full" db, err := sql.Open("postgres", connStr) if err != nil { log.Fatal(err) } defer db.Close() var version string err = db.QueryRow("SELECT version()").Scan(&version) if err != nil { log.Fatal(err) } fmt.Println(version) } ``` ## HA cluster connections For HA clusters, connections are routed through a load balancer that automatically directs traffic to the primary node. All connections use port `6432` — the same port a single-node cluster uses. Scaling up to HA adds the load balancer without changing your connection string: the hostname and port both stay as they were. The load balancer handles failover automatically, and your application reconnects to the new primary without changing connection details. ## Connection limits Dedicated plans connect through PgBouncer in transaction-pooling mode: the first number is how many client connections the pooled endpoint accepts, the second how many transactions execute on PostgreSQL at once per database. Clients beyond the slot count wait briefly instead of failing. | Plan | Client connections | Transaction slots per database | | ------- | ------------------ | ------------------------------ | | Free | 15 (direct) | 15 | | Solo | 500 | 20 | | Starter | 2,000 | 25 | | Growth | 5,000 | 30 | | Scale | 10,000 | 40 | ## Troubleshooting * Verify your host, port, and credentials are correct. * Ensure you're using `sslmode=verify-full` and the dashboard hostname, not a raw IP address. * Check that your host and port are correct. psql and the Python drivers use libpq, which does not read your operating system's trust store on its own. Add `sslrootcert=system` to the connection string so it uses the roots your system already trusts. On libpq older than 16, give it the path to your system bundle instead, such as `/etc/ssl/certs/ca-certificates.crt`. There is no CA file to download from us. See [SSL / TLS](#ssl-tls). All connections require SSL. Configure `sslmode=verify-full` and connect with the hostname shown in the dashboard, not a raw IP address, since `verify-full` checks the hostname against the certificate. Rivestack certificates are issued by Let's Encrypt, so there is no custom CA to download. Free databases allow 15 direct connections. Dedicated plans accept 500 to 10,000 pooled client connections depending on server type, with 20 to 40 transaction slots per database; extra clients queue briefly rather than failing. See the connection limits table above. # FAQ Source: https://docs.rivestack.io/faq Frequently asked questions about Rivestack * **Free (\$0)**: a shared PostgreSQL database for development and testing. 2 GB storage, 15 connections, no backups, deleted after 30 days of inactivity. * **Solo (\$29/mo)**: your own single-tenant VM (1 vCPU · 2 GB RAM · 30 GB NVMe) with daily backups, point-in-time recovery, and monitoring. Single node, so no automatic failover, but never deleted. Ideal for small production apps. * **HA Cluster (from \$49/node/mo)**: dedicated infrastructure with 1 to 3 nodes. Choose at least 2 nodes for automatic failover; applications must retry connections during a failover. Open the free database in your dashboard and click **Upgrade**. We provision your dedicated Solo VM and **migrate your data across automatically** (typically \~3 minutes). Your free database stays online and untouched until you delete it, so you can verify everything first, then just point your app at the new host and credentials. Rivestack supports **PostgreSQL 16, 17, and 18**. You choose your major version when creating a cluster. Rivestack enables the following extensions by default on new databases: * **pgvector**: Vector similarity search for AI/ML embeddings * **pgvectorscale** (`vectorscale`): StreamingDiskANN indexes and scalable vector search * **uuid-ossp**: UUID generation * **pg\_stat\_statements**: SQL execution statistics tracking * **pgcrypto**: Cryptographic functions * **pg\_trgm**: Trigram matching for similarity searches Other supported extensions can be selected when creating or configuring a database. The dashboard shows the current list. Yes. All managed endpoints require TLS and use publicly trusted Let's Encrypt certificates. Use `sslmode=verify-full` so your client both encrypts the connection and verifies the database hostname. `sslmode=require` encrypts traffic but does not securely authenticate the server. No custom CA download is needed. On HA clusters with 2 or more nodes, Patroni can promote a healthy replica and the load balancer keeps the same connection endpoint. Existing sessions disconnect, so applications must reconnect with bounded retries and backoff. Detection and promotion time depends on cluster and network conditions; Rivestack does not currently promise a fixed failover SLA. Because replication is asynchronous, a failure can also lose transactions that had not reached a replica. Single-node clusters do not have automatic failover. We recommend at least 2 nodes for production. Solo and HA clusters include automated daily backups via pgBackRest, stored in S3-compatible object storage. Retention is 7 days on Solo and 14 days on HA clusters. You can also trigger manual backups and restore to any point in time within the retention window. Free tier databases are not backed up. See the [Backups & Restore](/backups) guide for details. Yes. Rivestack supports point-in-time recovery (PITR) on Solo and HA clusters. Continuous WAL archiving lets you restore to any second within your backup retention window. Initiate a restore from the Backup tab in the dashboard. * **EU Central**: Falkenstein, Germany * **US East**: Virginia, USA * **Asia Pacific**: Singapore (Solo plan only) Free and Solo are available in all regions. The HA tiers (Starter/Growth/Scale) are available in EU Central and US East only, so Singapore offers the Solo plan only. Pricing is set per region, and **EU Central is the cheapest**. See [Pricing](/pricing#per-region-pricing). Yes. You can add or remove nodes (between 1 and 3) from the Management tab. Adding a node provisions a new replica that joins your cluster automatically. See the [Scaling](/scaling) guide. Vertical scaling (changing server type) is not yet supported. To upgrade, create a new cluster with the desired server type and migrate your data. * **Free**: 15 direct PostgreSQL connections per database * **Solo**: 500 pooled client connections, 20 transaction slots per database * **Starter**: 2,000 pooled client connections, 25 transaction slots * **Growth**: 5,000 pooled client connections, 30 transaction slots * **Scale**: 10,000 pooled client connections, 40 transaction slots Dedicated plans connect through PgBouncer in transaction-pooling mode, so clients beyond the slot count queue briefly instead of failing. Adding HA nodes improves availability, not connection capacity. See [Limits](/limits) for the full explanation. Solo is a flat $29/month. HA clusters are billed monthly per node (from $49/node). Payment is processed via credit card through our payment provider. Scaling changes (adding/removing nodes) are prorated within your billing cycle. You can cancel anytime from the Billing page. Yes. Cancel anytime from the **Billing** page in the dashboard. Your cluster remains active until the end of the current billing period. Dedicated-plan data is stored on servers in the region you selected (EU, US, or Singapore), and backup traffic is sent to S3-compatible object storage over TLS. Database connections are encrypted in transit. Rivestack does not currently advertise application-level, customer-managed, or pgBackRest repository encryption at rest; contact us before relying on a specific at-rest encryption or compliance requirement. Not as read-serving replicas yet. HA clusters with 2 or more nodes run streaming-replication standbys for **automatic failover**. All connections go through port `6432`, and the load balancer routes every query (reads included) to the primary. A separate read-only replica endpoint that lets you offload reads is on our roadmap. There is no free trial for HA clusters, but the **Free tier** lets you explore Rivestack at no cost with a shared PostgreSQL database (2 GB storage, 15 connections). The dashboard provides real-time metrics including TPS, connections, CPU, memory, disk I/O, cache hit ratio, and replication lag. View historical data over 1 hour, 24 hours, or 7 days. See the [Monitoring](/monitoring) guide. # Getting Started Source: https://docs.rivestack.io/getting-started Create your first managed PostgreSQL cluster on Rivestack ## Overview Rivestack is a managed PostgreSQL service that gives you production-ready databases in minutes. Start on the **free shared tier**, move to a **dedicated Solo VM** when you go to production, and step up to an **HA cluster** when you need automatic failover. Shared PostgreSQL for development and testing. 2 GB storage, up to 15 connections. No credit card required. Your own dedicated VM with daily backups, point-in-time recovery, and monitoring. \$29/month, never deleted. Dedicated infrastructure with backups and monitoring. Choose 2 or more nodes for automatic failover. Starting at \$49/node/month. Already have a free database? You can [upgrade it to Solo](/pricing#upgrading-from-free) in one click, and we migrate your data across automatically. ## Create your first cluster ### Step 1: Sign up Create your Rivestack account at [app.rivestack.io](https://app.rivestack.io). You can sign up with email or your existing GitHub or Google account. ### Step 2: Create a database 1. From the dashboard, click **Create Database**. 2. Enter a **database name** (or use the auto-generated suggestion). 3. Select **Free** as the plan. 4. Choose your **region** (EU, US, or Singapore). 5. Click **Create**. Your database is ready immediately. You'll see it on your dashboard with connection details. 1. From the dashboard, click **Create Database** (or **Upgrade** on an existing free database). 2. Enter a **name**. 3. Select **Solo** as the plan. 4. Choose your **region** (EU, US, or Singapore). Pricing varies by region, and EU is cheapest. See [Pricing](/pricing#per-region-pricing). 5. Review the cost (from \$29/month) and complete checkout. Your dedicated VM provisions in \~3 minutes. If you upgraded a free database, we migrate its data across automatically, and the free database stays online until you delete it. 1. From the dashboard, click **Create Database**. 2. Enter a **cluster name**. 3. Select **HA Cluster** as the plan. 4. Choose your **region**: * EU Central (Falkenstein, Germany) * US East (Virginia) HA clusters are available in EU Central and US East only. Singapore (Asia Pacific) offers the Solo plan only. 5. Select a **server type** (prices shown are EU Central, the cheapest region, detailed under [Per-region pricing](/pricing#per-region-pricing)): | Type | vCPUs | RAM | Storage | Price (EU Central) | | ------- | ----- | ----- | ------- | ------------------ | | Starter | 2 | 4 GB | 55 GB | \$49/node/mo | | Growth | 4 | 8 GB | 135 GB | \$85/node/mo | | Scale | 8 | 16 GB | 295 GB | \$159/node/mo | Storage listed is fully available for your PostgreSQL database. 25 GB is reserved for the OS on each node. 6. Choose the **number of nodes** (1-3). We recommend 2 or more for high availability. 7. Review your total monthly cost and click **Create**. 8. Complete checkout via our payment provider. Provisioning typically takes 7-10 minutes. You can watch the progress in real time on the dashboard. ### Step 3: Connect Once your database is ready, navigate to the **Connection** tab to find your credentials: * **Host**: Your unique endpoint (e.g., `rs-abc12345.eu.db.rivestack.io`) * **Port**: `6432` * **Database**: Your database name * **Username**: Your database user * **Password**: Generated on creation (copy it from the dashboard) * **SSL Mode**: `verify-full` (recommended and shown in the dashboard) Your connection string looks like this: ``` postgresql://username:password@rs-abc12345.eu.db.rivestack.io:6432/mydb?sslmode=verify-full ``` See detailed connection examples for Python, Node.js, Go, and more. ## What's included pgvector and pgvectorscale are enabled by default on new databases, including StreamingDiskANN index support. Default extensions include uuid-ossp, pg\_stat\_statements, pgcrypto, and pg\_trgm. The dashboard lists additional supported extensions. All connections are encrypted with TLS. No unencrypted connections are allowed. Solo and HA clusters include daily automated backups with point-in-time recovery. ## Next steps Connect from your application with Python, Node.js, or Go. Store and query vector embeddings for AI applications. Learn how automatic failover keeps your database online. View metrics and health status for your clusters. # High Availability Source: https://docs.rivestack.io/high-availability How automatic failover keeps your database online ## Overview Rivestack HA clusters use [Patroni](https://github.com/patroni/patroni) for automatic failover and cluster management. When a primary node fails, an eligible replica can be promoted automatically; applications should expect a brief interruption and reconnect. Automatic failover requires an HA cluster (2+ nodes). The [Free](/pricing) and [Solo](/pricing) plans run a single node and do not include failover. Solo is a good fit when you want dedicated, backed-up PostgreSQL but don't yet need multi-node resilience. ## Architecture Each HA cluster consists of: * **1-3 PostgreSQL nodes**: one primary and zero or more streaming replicas * **1 Hetzner Load Balancer** for clusters with two or more nodes * **HAProxy on each database node**: receives PROXY protocol, applies the client IP allowlist, and passes TLS through to PgBouncer * **etcd**: distributed consensus store for leader election * **Patroni**: cluster manager that handles replication, failover, and health checks ``` ┌──────────────┐ Clients ────────►│Load Balancer │ │ TCP :6432 │ └──────┬───────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Primary │ │ Replica │ │ Replica │ │ (node 1) │ │ (node 2)│ │ (node 3)│ └─────────┘ └─────────┘ └─────────┘ │ ▲ ▲ └──── streaming replication──┘ ``` ## How failover works 1. **Detection**: Patroni continuously monitors the health of each PostgreSQL node. If the primary becomes unresponsive, Patroni detects the failure within seconds. 2. **Leader election**: Patroni uses etcd to coordinate leader election. An eligible replica is selected as the new primary. 3. **Promotion**: The selected replica is promoted to primary. It begins accepting writes immediately. 4. **Routing**: The load balancer detects the topology change and routes new connections to the new primary. Existing connections to the old primary are terminated. 5. **Recovery**: When the failed node comes back online, it automatically rejoins the cluster as a replica and begins streaming from the new primary. ## Failover expectations | Metric | Value | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | **Typical interruption** | Tens of seconds, depending on failure detection, election, and client retry behavior | | **Connection behavior** | Existing connections are dropped; new connections are routed after promotion | | **Data-loss objective** | Not zero-loss: replication is asynchronous, so acknowledged transactions not yet received by the promoted replica can be lost | Rivestack does not currently promise a zero RPO or a fixed failover SLA. If your workload requires synchronous commit or a contractual recovery objective, contact us before relying on automatic failover. During failover, active connections to the old primary will be dropped. Your application should implement connection retry logic to handle brief interruptions. ## Replication Rivestack uses PostgreSQL **streaming replication** to keep replicas in sync with the primary: * WAL (Write-Ahead Log) records are streamed from primary to replicas in real time. * Replicas are used for automatic failover. * Replication lag is monitored and visible in the dashboard Metrics tab. ### Replication lag Monitor replication lag for each replica in the dashboard: * **Lag time**: How far behind the replica is (in seconds) * **Lag bytes**: Size of un-replayed WAL data Replication lag varies with write throughput, node load, and network conditions. Monitor it for your workload; do not assume it is always sub-second. ## Node roles | Role | Reads | Writes | Count | | ----------- | ------------- | ------ | --------- | | **Primary** | Yes | Yes | Exactly 1 | | **Replica** | Failover only | No | 0-2 | ## Connection routing All connections use port `6432` — the same port a single-node cluster uses, so scaling to HA leaves your connection string untouched. The load balancer forwards only to the node whose Patroni `/primary` health check is healthy. Applications must reconnect after a failover. ## Best practices During failover, connections to the old primary are terminated. Your application should: * Catch connection errors and retry after a short delay (1-3 seconds). * Use exponential backoff for retries. * Most PostgreSQL drivers handle reconnection automatically if configured. A single-node cluster has no failover target. Run at least 2 nodes in production to ensure automatic recovery from node failures. Check the Metrics tab regularly. Sustained replication lag may indicate the replica needs more resources or there are network issues. Verify your application handles failover gracefully by observing behavior during planned maintenance windows. # Rivestack Documentation Source: https://docs.rivestack.io/index Managed PostgreSQL with pgvector, pgvectorscale, automatic failover, and daily backups ## Managed PostgreSQL, built for production Rivestack gives you production-ready PostgreSQL databases with high availability, automated backups, and pgvector plus pgvectorscale for AI applications. Deploy in minutes, scale as you grow. Create your first cluster and connect in minutes. Connect from Python, Node.js, Go, or any PostgreSQL client. Store and query vector embeddings for AI applications. Free tier for development. Dedicated Solo from $29/month. HA clusters from $49/node/month. ## Why Rivestack pgvector and pgvectorscale are enabled by default. Build RAG, semantic search, and recommendation systems with HNSW or StreamingDiskANN indexes. Multi-node clusters use Patroni-managed streaming replication and automatic primary routing. Applications should reconnect during failover, and asynchronous replication can lose recent unreplicated transactions. Automated daily backups with 7-day Solo or 14-day HA retention, plus point-in-time recovery within the retained WAL window. ## Live Demo Try our live demo: semantic search over 30 days of Hacker News, powered by Rivestack pgvector. \~12ms vector queries across 40K chunks. ## Explore the docs How automatic failover and replication keep your data safe. Automated backups, retention policies, and point-in-time recovery. Real-time metrics for CPU, memory, TPS, connections, and more. Add or remove nodes to match your workload. Common questions about Rivestack. Manage your databases as infrastructure-as-code. # Limits Source: https://docs.rivestack.io/limits Connection limits, storage, and resource quotas ## Plan limits | Resource | Free | Solo | HA Cluster | | -------------------------- | -------------------------- | --------------------------------------- | -------------------------------------------- | | **Price** | \$0 | \$29/mo | From \$49/node/mo | | **Databases** | 1 | Multiple (storage permitting) | Unlimited | | **Connections** | 15 per database (direct) | 500 pooled clients | 2,000–10,000 pooled clients (by server type) | | **Storage** | 2 GB per database | 30 GB | Depends on server type | | **Nodes** | Shared | 1 dedicated | 1–3 dedicated | | **Automatic failover** | No | No (single node) | With 2 or more healthy nodes | | **Backups** | Not included | Daily, 7-day retention | Daily, 14-day retention | | **Point-in-time recovery** | Not included | Included | Included | | **Monitoring** | Basic | Full metrics | Full metrics | | **SQL Editor** | Included | Included | Included | | **Default extensions** | pgvector and pgvectorscale | 6, including pgvector and pgvectorscale | 6, including pgvector and pgvectorscale | | **Inactivity deletion** | After 30 days | Never | Never | ## Storage per server type | Server type | Available storage | vCPUs | RAM | Price | | ----------- | ----------------- | ----- | ----- | ------------- | | **Starter** | 55 GB | 2 | 4 GB | \$49/node/mo | | **Growth** | 135 GB | 4 | 8 GB | \$85/node/mo | | **Scale** | 295 GB | 8 | 16 GB | \$159/node/mo | Storage listed is fully available for your PostgreSQL database. An additional 25 GB is reserved for the operating system on each node. All nodes in a cluster use the same server type. ## Connection limits Free databases connect directly to PostgreSQL. Dedicated plans connect through PgBouncer in transaction-pooling mode on port `6432`, which separates two numbers that are easy to conflate: how many **client connections** your applications can hold open, and how many **transaction slots** actually execute on PostgreSQL at once. Clients beyond the slot count are not rejected; they wait briefly for a free slot. | Plan | Client connections | Transaction slots per database | | ----------- | ------------------ | ------------------------------ | | **Free** | 15 (direct) | 15 | | **Solo** | 500 | 20 | | **Starter** | 2,000 | 25 | | **Growth** | 5,000 | 30 | | **Scale** | 10,000 | 40 | An HA cluster has the capacity of its server type. Adding nodes improves availability, not connection capacity, because all traffic reaches one primary; a Scale HA cluster is also capped at 10,000 concurrent connections by its load balancer. Application-side pools (SQLAlchemy, pgx pool, Prisma) control how many connections your app opens; they do not add server-side transaction slots. A heavy analytical query can raise its own memory allowance with `SET LOCAL work_mem` inside its transaction. ### Connection pooling To keep many application workers inside your plan’s client-connection budget, use a client-side pool: ```python theme={null} from psycopg2 import pool connection_pool = pool.ThreadedConnectionPool( minconn=5, maxconn=20, dsn="postgresql://user:pass@host:6432/mydb?sslmode=verify-full&sslrootcert=system" ) conn = connection_pool.getconn() # ... use connection ... connection_pool.putconn(conn) ``` ```javascript theme={null} const { Pool } = require('pg'); const pool = new Pool({ connectionString: 'postgresql://user:pass@host:6432/mydb?sslmode=verify-full', max: 20, ssl: { rejectUnauthorized: true } }); const res = await pool.query('SELECT 1'); ``` ```go theme={null} // pgx pool handles connection pooling automatically pool, err := pgxpool.New(context.Background(), "postgresql://user:pass@host:6432/mydb?sslmode=verify-full") if err != nil { log.Fatal(err) } defer pool.Close() var result int err = pool.QueryRow(context.Background(), "SELECT 1").Scan(&result) ``` ## Cluster limits | Resource | Limit | | --------------------------- | ---------------------------------------------------- | | **Nodes per cluster** | 1–3 | | **Clusters per account** | No hard limit | | **Databases per cluster** | No hard limit (storage permitting) | | **Extensions per database** | Dashboard-supported extensions; 6 enabled by default | | **Max vector dimensions** | 16,000 (pgvector) | ## Rate limits | Action | Limit | | ----------------------------------------------- | ----------------------- | | **Cluster operations** (create, scale, restore) | 1 at a time per cluster | | **Manual backups** | 1 at a time per cluster | ## Exceeding limits * **Storage full**: Write operations will fail. Scale to a larger server type or delete unused data. * **Connection limit reached**: New connections are rejected. Close idle connections or implement connection pooling. # Monitoring Source: https://docs.rivestack.io/monitoring Real-time metrics and health monitoring for your database clusters ## Overview Rivestack provides built-in monitoring for all Solo and HA clusters. Metrics are collected via Prometheus exporters running on each node, with data available through the dashboard or API. Full monitoring is available on **Solo and HA clusters**. Free tier databases show basic usage metrics. ## Dashboard metrics Navigate to the **Metrics** tab of your HA cluster to view real-time and historical data. ### Time ranges Select from three predefined ranges: | Range | Sampling interval | | -------- | ----------------- | | 1 hour | 1 minute | | 24 hours | 10 minutes | | 7 days | 1 hour | ### PostgreSQL metrics | Metric | Description | | ---------------------- | ------------------------------------------------------- | | **TPS** | Transactions per second (commits + rollbacks) | | **Active connections** | Current number of active connections | | **Cache hit ratio** | Buffer cache effectiveness (higher is better) | | **Disk usage** | PostgreSQL data directory size and total disk usage | | **Replication lag** | Time and byte lag for replica nodes | | **Tuple operations** | Rows fetched, inserted, updated, and deleted per second | | **Deadlocks** | Number of deadlocks detected | ### System metrics (per node) | Metric | Description | | ---------------- | ----------------------------------------------- | | **CPU usage** | User, system, and total CPU percentage | | **Memory usage** | Used, free, cached, and total memory | | **Disk I/O** | Read and write throughput (KB/s) | | **Network I/O** | Received and transmitted throughput (KB/s) | | **Disk space** | Used and total disk space with usage percentage | ## Cluster health The dashboard shows a real-time health status for each cluster: | Status | Meaning | | ------------ | ----------------------------------- | | **Healthy** | All nodes are running and reachable | | **Degraded** | Some nodes are healthy, but not all | | **Critical** | No nodes are responding | Health status is also visible on the main dashboard as a badge on each cluster card, alongside a TPS sparkline chart showing recent transaction throughput. ### Node topology The **Topology** tab shows each node in your cluster with: * Node name and role (primary or replica) * Current state (streaming, in archive recovery, stopped) * Replication lag (for replicas) * Timeline number ## Accessing metrics ### Dashboard The easiest way to view metrics is through the Rivestack dashboard at [app.rivestack.io](https://app.rivestack.io). Charts update automatically with the selected time range. ## Alerts and notifications Rivestack sends email notifications for key events: * Cluster provisioning complete * Backup success or failure * Restore operations * Node scaling operations * Cluster health changes Configure notification preferences in **Settings** from the dashboard sidebar. # pgvector and pgvectorscale Source: https://docs.rivestack.io/pgvector Store and query vector embeddings with pgvector and StreamingDiskANN ## Overview pgvector adds vector similarity search to PostgreSQL, letting you store embeddings from OpenAI, Cohere, Hugging Face, or any other model alongside your relational data. pgvectorscale builds on pgvector with StreamingDiskANN indexes for larger, disk-oriented vector workloads. Both extensions are enabled by default on new Rivestack databases. Their SQL extension names are `vector` and `vectorscale`. ## Verify the extension Confirm both extensions are active on your database: ```sql theme={null} SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'vectorscale') ORDER BY extname; ``` ## Store embeddings ### Create a table with a vector column ```sql theme={null} CREATE TABLE documents ( id BIGSERIAL PRIMARY KEY, content TEXT NOT NULL, embedding VECTOR(1536) -- OpenAI ada-002 dimensions ); ``` The `VECTOR(n)` type stores n-dimensional vectors. Common dimension sizes: | Model | Dimensions | | ----------------------------- | ---------- | | OpenAI text-embedding-ada-002 | 1536 | | OpenAI text-embedding-3-small | 1536 | | OpenAI text-embedding-3-large | 3072 | | Cohere embed-english-v3.0 | 1024 | | BGE-small-en | 384 | ### Insert vectors ```sql theme={null} INSERT INTO documents (content, embedding) VALUES ( 'Rivestack is a managed PostgreSQL service', '[0.1, 0.2, 0.3, ...]' -- your embedding vector ); ``` ### From Python with OpenAI ```python theme={null} import psycopg2 import openai # Generate embedding response = openai.embeddings.create( model="text-embedding-ada-002", input="Rivestack is a managed PostgreSQL service" ) embedding = response.data[0].embedding # Store in Rivestack conn = psycopg2.connect("postgresql://user:pass@host:6432/mydb?sslmode=verify-full&sslrootcert=system") cur = conn.cursor() cur.execute( "INSERT INTO documents (content, embedding) VALUES (%s, %s)", ("Rivestack is a managed PostgreSQL service", str(embedding)) ) conn.commit() ``` ## Query vectors ### Similarity search Find the 5 most similar documents using cosine distance: ```sql theme={null} SELECT id, content, embedding <=> '[0.1, 0.2, 0.3, ...]' AS distance FROM documents ORDER BY embedding <=> '[0.1, 0.2, 0.3, ...]' LIMIT 5; ``` ### Distance operators | Operator | Distance Metric | Use Case | | -------- | --------------- | ----------------------------- | | `<->` | L2 (Euclidean) | General purpose | | `<=>` | Cosine | Text embeddings (most common) | | `<#>` | Inner product | When vectors are normalized | ## Create an index For tables with more than a few thousand rows, create an index to speed up queries: ### HNSW index (recommended) ```sql theme={null} CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 200); ``` HNSW provides fast, approximate nearest-neighbor search with good recall. Tuning parameters: | Parameter | Default | Description | | ----------------- | ------- | --------------------------------------------------------------- | | `m` | 16 | Max connections per node. Higher = better recall, more memory. | | `ef_construction` | 64 | Build-time search width. Higher = better recall, slower builds. | Set the search parameter at query time. Higher `ef_search` means better recall and higher latency (the default is `40`): ```sql theme={null} -- Per query, and the only form that survives transaction pooling (see warning): BEGIN; SET LOCAL hnsw.ef_search = 100; SELECT id FROM documents ORDER BY embedding <=> $1 LIMIT 10; COMMIT; -- Or set a persistent default for the whole database (applies to new connections): ALTER DATABASE appdb SET hnsw.ef_search = 100; ``` **A plain session `SET hnsw.ef_search` is silently ignored.** Every dedicated plan (Solo, Starter, Growth, Scale) connects through PgBouncer in **transaction-pooling** mode on port `6432`, so a bare `SET hnsw.ef_search = 100;` runs in its own transaction and is reset before your next query, leaving you with the default (`40`) instead. Always either use `SET LOCAL` *inside the same transaction* as the query, or set a persistent default with `ALTER DATABASE appdb SET hnsw.ef_search = …` (or `ALTER ROLE appdb SET …`). Free databases connect directly to PostgreSQL on port `5432`, where a plain session `SET` works normally. This holds on HA clusters too. The load balancer and source-filtering HAProxy gateway pass the TLS stream to PgBouncer rather than to PostgreSQL directly, so scaling up does not turn session-level `SET` back on. ### IVFFlat index ```sql theme={null} CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); ``` IVFFlat requires a training step and works best when you set `lists` to `rows / 1000` for tables up to 1M rows. ```sql theme={null} SET ivfflat.probes = 10; -- default is 1, higher = better recall ``` ### StreamingDiskANN index (pgvectorscale) For datasets that should not keep the entire graph in memory, use pgvectorscale's StreamingDiskANN index: ```sql theme={null} CREATE INDEX documents_embedding_diskann_idx ON documents USING diskann (embedding vector_cosine_ops); ``` The table still uses pgvector's `VECTOR` type and distance operators; pgvectorscale supplies the `diskann` index access method. Test recall, build time, memory, and latency with your own embeddings before choosing an index for production. ### Which index to use? | | HNSW | IVFFlat | StreamingDiskANN | | -------------- | --------------- | --------------------------------- | ------------------------------------------ | | Recall | High | Tunable | Tunable | | Build speed | Slower | Faster | Workload dependent | | Query speed | Fast | Fast | Designed for scalable disk-oriented search | | Memory | More | Less | Lower in-memory graph pressure | | Recommendation | General default | Simple, trained approximate index | Benchmark for larger vector datasets | ## RAG example A complete retrieval-augmented generation (RAG) pattern: ```python theme={null} import psycopg2 import openai def search(query: str, top_k: int = 5): # 1. Generate embedding for the query response = openai.embeddings.create( model="text-embedding-ada-002", input=query ) query_embedding = response.data[0].embedding # 2. Search Rivestack for similar documents conn = psycopg2.connect("postgresql://user:pass@host:6432/mydb?sslmode=verify-full&sslrootcert=system") cur = conn.cursor() cur.execute(""" SELECT content, embedding <=> %s::vector AS distance FROM documents ORDER BY embedding <=> %s::vector LIMIT %s """, (str(query_embedding), str(query_embedding), top_k)) results = cur.fetchall() conn.close() return results # 3. Use results as context for your LLM results = search("How does Rivestack handle backups?") context = "\n".join([row[0] for row in results]) ``` ## Benchmarks Measured pgvector performance across the tiers (AMD CPX, shared vCPU, local NVMe), 250,000 vectors at 1536 dimensions, HNSW `m=16` / `ef_construction=64`, cosine, from a same-region client over PgBouncer (`:6432`) and TLS, recall\@10 vs exact KNN: | Tier | vCPU / RAM | Hot capacity | Throughput | Recall\@10 | | ----------- | ---------- | ------------ | --------------------------------- | ---------- | | **Solo** | 1 / 2 GB | \~250K | \~980 QPS (4 clients), p50 3.8 ms | 0.90 | | **Starter** | 2 / 4 GB | \~350K | \~1,600 QPS (16 clients) | 0.93 | | **Growth** | 4 / 8 GB | \~600K | \~2,950 QPS (16 clients) | 0.94 | | **Scale** | 8 / 16 GB | \~1M | \~4,465 QPS (16 clients) | 0.95 | Scale also builds and serves a 1M × 1536 index (\~6 GB) hot: \~3,600 QPS at recall\@10 0.74, p50 4.2 ms, 16 clients (index builds in \~9 min). CPX is AMD EPYC shared vCPU (not CPU-isolated); the gain over the older Intel CX line is faster cores and far better availability. QPS and p50 latency trade off across the concurrency curve, so each figure carries its recall and client count. Same-region network floor is \~0.3 to 0.5 ms per round-trip. Every operating point, with one-command reproduce steps: [measured pgvector benchmarks](https://rivestack.io/pgvector-benchmarks-measured). ## Performance tips * **Always create an index** for tables over a few thousand rows. Without an index, pgvector scans every row. * **Use HNSW** as your default index type. It offers the best recall/speed trade-off. * **Benchmark StreamingDiskANN** when the vector graph is too large to keep comfortably in memory. * **Increase `ef_search`** if recall is too low. Start with 100 and increase as needed, but set it with `SET LOCAL` inside the query's transaction or via `ALTER DATABASE … SET` — every plan pools on `:6432`, and a plain session `SET` is dropped by transaction pooling. The same applies to `ivfflat.probes`. * **Batch inserts** for loading large datasets. Use `COPY` or multi-row `INSERT` for best throughput. * **Choose the right distance operator** for your use case. Cosine (`<=>`) is the most common for text embeddings. # Pricing Source: https://docs.rivestack.io/pricing Plans, regions, and what's included ## Plans **\$0/month** For development and testing. * Shared PostgreSQL instance * 2 GB storage per database * 15 concurrent connections * 1 database * pgvector + pgvectorscale on default database * SSL encryption * All regions * Deleted after 30 days of inactivity **\$29/month** Single-tenant PostgreSQL on its own VM, no HA overhead. * 1 vCPU · 2 GB RAM (EU Central) * 30 GB NVMe storage available * Your own VM, not shared with other customers * 500 pooled client connections * Daily backups (7-day retention) * SSL encryption * Never deleted for inactivity * \$29 in every region **Starting at \$49/node/month** For production workloads with automatic failover. * Dedicated infrastructure * 1-3 nodes; automatic failover with 2+ * 2,000–10,000 pooled client connections by server type * Automated daily backups (14-day retention) * Point-in-time recovery * Real-time monitoring * pgvector + pgvectorscale enabled by default * Email notifications * EU Central & US East (not Singapore) ## Per-region pricing Pricing is set **per region** (USD/month). For HA tiers (Starter, Growth, Scale) the price is **per node**; Solo is a single node. **EU Central is the cheapest region** and US East is the most expensive. The HA tiers are offered in EU Central and US East; Singapore offers the Solo plan only. | Plan | EU Central | US East | Asia Pacific (Singapore) | | ------- | ---------- | ---------- | ------------------------ | | Solo | \$29 | \$29 | \$29 | | Starter | \$49/node | \$59/node | — | | Growth | \$85/node | \$119/node | — | | Scale | \$159/node | \$229/node | — | Singapore offers the Solo plan only; the HA tiers (Starter/Growth/Scale) are available in EU-Central and US-East. The Free tier is $0 in every region, and Solo is $29 in every region. For the HA tiers, EU Central is the cheapest; US East is priced higher to reflect regional infrastructure costs. ## Solo plan Solo gives you a single-tenant PostgreSQL instance on its own VM at a fraction of the HA cluster price. It's a single node with no load balancer, making it ideal for dev/staging environments, personal projects, and small production apps that don't require high availability. ### Per-region hardware The VM type and price differ by region (see [Per-region pricing](#per-region-pricing) for the full price table, where EU Central is the cheapest): | Spec | EU Central (`cpx12`) | US East (`cpx11`) | Asia Pacific (`cpx12`) | | ---------------------------- | -------------------- | ----------------- | ---------------------- | | CPU | 1 vCPU (AMD) | 2 vCPU (AMD) | 1 vCPU (AMD) | | RAM | 2 GB | 2 GB | 2 GB | | Total disk | 40 GB NVMe | 40 GB NVMe | 40 GB NVMe | | **Available for PostgreSQL** | **30 GB** | **30 GB** | **30 GB** | | Price | \$29/mo | \$29/mo | \$29/mo | All regions run Solo on Hetzner's AMD **CPX** line (shared vCPU on AMD EPYC, faster and more reliably in stock than the older Intel CX line, not CPU-isolated). US East's `cpx11` over-delivers at 2 vCPU. Specs shown are the advertised minimums. ### What Solo includes | Feature | Details | | ---------------------------- | -------------------------------------------------------------- | | **PostgreSQL 16, 17, 18** | Choose your major version at cluster creation | | **pgvector + pgvectorscale** | Vector search with HNSW, IVFFlat, and StreamingDiskANN indexes | | **Automated backups** | Daily, with 7-day retention | | **Monitoring** | CPU, memory, disk, connections | | **SSL/TLS** | Enforced on all connections | | **SQL Editor** | Run queries directly from the dashboard | | **Email notifications** | Provisioning and health alerts | > **Note**: Solo does not include high availability, automatic failover, or a load balancer. DNS points directly to the VM's public IP. For production workloads that require HA, use a Starter or higher HA cluster. ## Upgrading from Free Already on the Free tier? You can upgrade a free database to **Solo** in one click from the dashboard. We provision your dedicated VM and **migrate your data across automatically** (typically \~3 minutes). Your free database stays online and untouched until you delete it, so you can verify everything first. ## HA cluster server types HA clusters are available in three server configurations: | | Starter | Growth | Scale | | ---------------------- | ------------ | ------------ | ------------- | | **vCPUs** | 2 | 4 | 8 | | **RAM** | 4 GB | 8 GB | 16 GB | | **Available storage** | 55 GB | 135 GB | 295 GB | | **Price (EU Central)** | \$49/node/mo | \$85/node/mo | \$159/node/mo | Prices shown are for EU Central, the cheapest region. US East is higher. See [Per-region pricing](#per-region-pricing). All regions run on Hetzner's AMD CPX line (shared vCPU). HA clusters are available in EU Central and US East only; Singapore offers the Solo plan only. (US East Scale ships 215 GB available storage rather than 295 GB.) Storage listed is fully available for your PostgreSQL database. An additional 25 GB is reserved for the OS on each node. ### Example pricing Examples below use **EU Central** rates (the cheapest region). For US East, substitute the per-node prices from the [Per-region pricing](#per-region-pricing) table. (HA clusters are not available in Singapore.) | Configuration | Monthly cost (EU Central) | | --------------- | ------------------------- | | Solo | \$29/mo | | Starter, 1 node | \$49/mo | | Growth, 2 nodes | \$170/mo | | Growth, 3 nodes | \$255/mo | | Scale, 2 nodes | \$318/mo | ## Regions | Region | Location | Free | Solo | HA | | ------------ | -------------------- | ---- | ---- | --- | | EU Central | Falkenstein, Germany | Yes | Yes | Yes | | US East | Virginia, USA | Yes | Yes | Yes | | Asia Pacific | Singapore | Yes | Yes | No | Pricing varies by region, and **EU Central is the cheapest**. See [Per-region pricing](#per-region-pricing). Solo hardware specs also vary by region; see the Solo section above. **Singapore offers the Solo plan only; the HA tiers (Starter/Growth/Scale) are available in EU-Central and US-East.** ## What's included in HA clusters Every HA cluster includes the following at no additional cost: | Feature | Details | | ---------------------------- | ----------------------------------------------------------------------------------------------------------- | | **PostgreSQL 16, 17, 18** | Choose your major version at cluster creation | | **pgvector + pgvectorscale** | Vector search with HNSW, IVFFlat, and StreamingDiskANN indexes | | **Extensions** | uuid-ossp, pg\_stat\_statements, pgcrypto, and pg\_trgm enabled by default; more available in the dashboard | | **Automated backups** | Daily, with 14-day retention | | **Point-in-time recovery** | Restore to any second within retention window | | **Monitoring** | CPU, memory, disk, TPS, connections, cache hit ratio | | **SSL/TLS** | Enforced on all connections | | **High availability** | Patroni-managed automatic failover with 2 or more nodes; applications reconnect during failover | | **Load balancing** | Automatic connection routing | | **Email notifications** | Provisioning, backups, health alerts | | **SQL Editor** | Run queries directly from the dashboard | ## Billing * **Payment**: Credit card via our payment provider. * **Billing cycle**: Monthly, starting from cluster/database creation. * **Region pricing**: Prices are set per region (EU Central cheapest, US East most expensive). See [Per-region pricing](#per-region-pricing). * **Solo**: \$29/month (all regions), a single node with no per-node scaling. * **HA clusters**: Adding or removing nodes updates your subscription immediately. Changes are prorated. The per-node rate depends on the cluster's region. * **Cancellation**: Cancel anytime from the Billing page. Your database remains active until the end of the current billing period. ## Free tier limits | Limit | Value | | ------------------------ | ------------------ | | Databases per account | 1 | | Storage per database | 2 GB | | Connections per database | 15 | | Backups | Not included | | High availability | Not included | | Monitoring | Basic metrics only | # Regions Source: https://docs.rivestack.io/regions Available regions and data residency ## Available regions Rivestack databases can be deployed in the following regions: | Region | Location | Identifier | Free | Solo | HA | | ---------------- | -------------------- | ------------ | ---- | ---- | --- | | **EU Central** | Falkenstein, Germany | `eu-central` | Yes | Yes | Yes | | **US East** | Virginia, USA | `us-east` | Yes | Yes | Yes | | **Asia Pacific** | Singapore | `ap` | Yes | Yes | No | Pricing varies by region, and EU Central is the cheapest. See [Pricing](/pricing#per-region-pricing) for details. Singapore (Asia Pacific, `ap`) offers the **Solo** plan only. The HA tiers (Starter/Growth/Scale) are available in EU-Central and US-East. ## Choosing a region Select the region closest to your application servers for the lowest latency. Once a database or cluster is created, its region cannot be changed. | Your app is hosted in | Recommended region | | --------------------- | ------------------ | | Europe | EU Central | | North America | US East | | Asia / Oceania | Asia Pacific | | Other | Choose the nearest | ## Region details ### EU Central: Falkenstein, Germany * **Data residency**: All data stored within the EU * **Compliance**: GDPR-compliant data processing * **Hostname pattern**: `rs-{id}.eu.db.rivestack.io` ### US East: Virginia, USA * **Data residency**: All data stored within the US * **Hostname pattern**: `rs-{id}.us-east.db.rivestack.io` ### Asia Pacific: Singapore * **Data residency**: All data stored within Singapore * **Hostname pattern**: `rs-{id}.ap.db.rivestack.io` * **Plan availability**: Solo only. The HA tiers (Starter/Growth/Scale) are available in EU-Central and US-East. ## Cross-region considerations * Each database lives in a single region. There is no built-in cross-region replication. * Backups are stored in the same region as your database. * For multi-region architectures, create separate clusters in each region and handle replication at the application level. ## Upcoming regions We're evaluating additional regions based on demand. If you need a specific region, reach out to [hi@rivestack.io](mailto:hi@rivestack.io). # Scaling Source: https://docs.rivestack.io/scaling Scale your PostgreSQL cluster by adding or removing nodes ## Overview Rivestack HA clusters let you add or remove nodes (1 to 3 per cluster) through the dashboard, handled by our automated provisioning system. Added nodes are streaming-replication standbys that provide **high availability and automatic failover**. They are not read-serving replicas, so all client traffic (reads included) is routed to the primary. To increase query performance or fit a larger working set, choose a larger server type (see [Server types](#server-types) below). On the **Solo** plan (a single dedicated node), failover isn't available. To add standby nodes and automatic failover, create an [HA cluster](/high-availability). ## Node limits | | Minimum | Maximum | Default | | --------- | ------- | ------- | ------- | | **Nodes** | 1 | 3 | 2 | We recommend running at least 2 nodes for production workloads to maintain high availability during failures. ## Add a node 1. Navigate to your cluster's detail page in the dashboard. 2. Open the **Management** tab. 3. Click **Add Node**. 4. Review the cost impact (adds one node at your current plan's per-node price). 5. Confirm the operation. ### What happens * A new server is provisioned in the same region as your cluster. * PostgreSQL is installed and configured to match your cluster settings. * The new node joins the cluster as a **replica** and begins streaming replication from the primary. * Prometheus exporters are configured for monitoring. * The operation typically takes 7-10 minutes. * You can track progress in real time via the dashboard. ## Remove a node 1. Navigate to your cluster's detail page. 2. Open the **Management** tab. 3. Click **Remove Node**. 4. Select the node to remove from the list. 5. Type the node name to confirm. 6. Confirm the operation. ### What happens * The selected node is removed from the cluster. * If the removed node was a replica, no failover occurs. * If the removed node was the primary, Patroni automatically promotes a replica to primary before removal. * The server is decommissioned. * Your subscription is updated to reflect the new node count. You cannot remove a node if it would bring your cluster below 1 node. You also cannot remove nodes while another scaling or configuration operation is in progress. ## Scaling constraints * **One operation at a time**: You cannot add or remove nodes while another job (backup, restore, scaling, or configuration) is in progress. * **Cluster must be active**: Scaling is only available for clusters in an `active` state. * **No vertical scaling yet**: To change server type (e.g., Starter to Growth), create a new cluster and migrate your data. Vertical scaling is on our roadmap. ## Billing impact Node changes take effect immediately on your subscription: * **Adding a node** increases your monthly cost by one unit of your plan price (e.g., +\$85/mo for Growth). * **Removing a node** decreases your monthly cost accordingly. * Billing is prorated within the current billing cycle. ## Server types If you need more resources, choose a larger server type when creating a new cluster: | Type | vCPUs | RAM | Available Storage | Price/node | | ------- | ----- | ----- | ----------------- | ---------- | | Starter | 2 | 4 GB | 55 GB | \$49/mo | | Growth | 4 | 8 GB | 135 GB | \$85/mo | | Scale | 8 | 16 GB | 295 GB | \$159/mo | Storage listed is fully available for your PostgreSQL database. An additional 25 GB is reserved for the OS on each node. # Security Source: https://docs.rivestack.io/security TLS, network access controls, and infrastructure isolation ## Encryption in transit All client connections to Rivestack databases **require SSL/TLS**. Unencrypted client connections are rejected. | Setting | Value | | -------------------- | ------------------------------------ | | Recommended SSL mode | `verify-full` | | Minimum TLS version | TLS 1.2 | | Certificate | Let's Encrypt, signed by a public CA | The certificate is renewed with ACME DNS-01 and covers the regional database hostnames. Standard PostgreSQL clients can validate it with their operating-system trust store; no Rivestack-specific CA download is needed. ```text theme={null} postgresql://user:pass@host:6432/mydb?sslmode=verify-full ``` `sslmode=require` encrypts traffic but does not authenticate the database server. Use `verify-full`, keep the hostname from the dashboard, and do not replace it with a raw IP address. ## Data at rest Rivestack does not currently advertise application-level or customer-managed encryption for the PostgreSQL data directory. Backup traffic is sent to S3-compatible object storage over TLS, but the current pgBackRest configuration does not add client-side repository encryption. Do not rely on an “encrypted at rest” claim when assessing compliance requirements. Contact [hi@rivestack.io](mailto:hi@rivestack.io) before placing data that requires a specific at-rest encryption control or customer-managed key policy. ## Network security ### Shared cloud firewall Database VMs are covered by one reconciled Hetzner Cloud Firewall selected by server labels. This avoids Hetzner's per-project firewall limit. It exposes only the two documented database service ports and restricts SSH to Rivestack operations CIDRs; all other unsolicited public inbound traffic is dropped by the cloud firewall. Dedicated hosts also drop raw public PostgreSQL traffic on `5432`, leaving that port for the free shared service and private cluster traffic. Tenant allowlists are enforced on each database host, so adding customers does not create additional Hetzner firewalls: * Direct clusters use a persistent `nftables` source policy in front of PgBouncer. * HA clusters enable Hetzner Load Balancer PROXY protocol. A private HAProxy listener reads the original client address, applies the same source policy, and passes the PostgreSQL/TLS stream through to PgBouncer unchanged. * IPv4 and IPv6 addresses and CIDR networks are supported. An empty allowlist blocks new public database connections. ### Configure an IP allowlist 1. Open the dedicated cluster in the dashboard and select **Network**. 2. Add each application, office, VPN, or CI egress address as an IP or CIDR. Both IPv4 and IPv6 are accepted; DNS names are not. 3. Save the policy. The change is applied asynchronously, and the previous policy remains authoritative until the infrastructure job succeeds. New clusters start with `0.0.0.0/0` and `::/0`, which permits any source that can authenticate. Replace both public networks to restrict access. Saving an empty list intentionally blocks all new public database connections. Before restricting a production cluster, include every stable outbound address used by your applications, administrators, migrations, and monitoring. The load balancer's network connection normally hides the client address from a backend. Rivestack enables Hetzner PROXY protocol and terminates that header on the private HAProxy listener, so the allowlist evaluates the original client address without terminating PostgreSQL TLS. Changes are applied asynchronously to every cluster node. Existing established connections may continue until they close; the new policy controls new connections. ### Private networking Cluster nodes use tenant-private networks for database traffic and a regional private network for the distributed consensus service. Private-network placement is not a substitute for authentication or encryption, so client access is still protected by TLS and database credentials. ### Public port | Port | Service | Access | | ---- | ---------------------------- | ------------------------------------------------------------- | | 5432 | Free shared PostgreSQL | Public shared endpoint; TLS and database credentials required | | 6432 | PostgreSQL through PgBouncer | Public endpoint, restricted by the cluster IP allowlist | The dashboard hostname points directly to the VM for a single-node dedicated cluster and to one Hetzner Load Balancer after the cluster is scaled to HA. ## Authentication * **Database users**: password authentication over required TLS * **Dashboard**: Auth0 authentication with email verification * **API**: authenticated application and provider requests ## Infrastructure | Component | Details | | -------------------------- | ------------------------------------------------------------------------------------- | | **Regions** | EU Central (Falkenstein), US East (Virginia), and Asia Pacific (Singapore, Solo only) | | **Dedicated plans** | Dedicated VMs per customer cluster | | **Internal communication** | Private networks between cluster components | | **Backups** | pgBackRest to S3-compatible object storage, separate from database nodes | ## Compliance language Selecting EU Central keeps the provisioned database nodes in the EU region. Data residency alone is not a compliance certification. Rivestack does not claim a compliance standard or an at-rest encryption control unless it is explicitly stated in your agreement. ## Reporting vulnerabilities If you discover a security vulnerability, report it to [hi@rivestack.io](mailto:hi@rivestack.io). # Terraform Provider Source: https://docs.rivestack.io/terraform Manage Rivestack HA PostgreSQL clusters as infrastructure-as-code The official [Rivestack Terraform provider](https://registry.terraform.io/providers/Rivestack/rivestack/latest) lets you manage clusters, databases, users, extensions, grants, and backups as code. ## Installation Add the provider to your Terraform configuration: ```hcl theme={null} terraform { required_providers { rivestack = { source = "rivestack/rivestack" version = "~> 0.1" } } } provider "rivestack" {} ``` Requires **Terraform >= 1.0**. ## Authentication Get your API key from the [Rivestack Dashboard](https://app.rivestack.io). API keys use the `rsk_` prefix. ```bash theme={null} export RIVESTACK_API_KEY="rsk_your_api_key" ``` ```hcl theme={null} provider "rivestack" { api_key = "rsk_your_api_key" } ``` ## Node capacity HA clusters run on **prepaid node capacity**, not per-cluster billing. You buy a pool of nodes once, then Terraform (or the API) provisions clusters that draw from it, so there's no checkout in your `terraform apply` loop. In the [Rivestack Dashboard](https://app.rivestack.io), go to **Billing → Subscribe to Nodes**, choose a server type (`starter`, `growth`, or `scale`) and a quantity, then complete checkout. This is the only step that involves payment. `terraform apply` draws nodes from your pool automatically, matching on `server_type`. A `growth` cluster with `node_count = 2` consumes two `growth` nodes from your `growth` pool. There is no extra charge per cluster. If your pool has no free nodes of the requested `server_type`, the API returns **402 Payment Required** (`no available nodes; subscribe to nodes from the billing page first`) and `terraform apply` fails. Buy more capacity of that server type, then re-run. ### Provision with the REST API If you don't use Terraform, call the same endpoint directly. Create an API key, buy capacity, then: ```bash theme={null} # Provision a cluster from your prepaid pool (draws a matching node automatically) curl -X POST https://api.rivestack.io/api/ha/provision \ -H "Authorization: Bearer rsk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "production-cluster", "region": "eu-central", "server_type": "growth", "node_count": 2, "postgresql_version": 18 }' # region accepts "eu-central", "us-east", or "ap" (Singapore) # → { "id": 42, "tenant_id": "rs-…", "status": "provisioning", … } # Poll until status is "active" curl https://api.rivestack.io/api/ha/42 \ -H "Authorization: Bearer rsk_your_api_key" ``` Omit `server_type` to default to `starter`. The cluster is matched to a pool of the same server type and capacity is found automatically, with no pool ID needed. ## Quick start This example creates a 2-node HA cluster with a database and user: ```hcl theme={null} terraform { required_providers { rivestack = { source = "rivestack/rivestack" version = "~> 0.1" } } } provider "rivestack" {} # Create an HA PostgreSQL cluster resource "rivestack_cluster" "production" { name = "production-cluster" region = "eu-central" server_type = "growth" node_count = 2 postgresql_version = 18 } # Create a database resource "rivestack_cluster_database" "analytics" { cluster_id = rivestack_cluster.production.id name = "analytics" owner = rivestack_cluster.production.db_user } # Create a user resource "rivestack_cluster_user" "app_user" { cluster_id = rivestack_cluster.production.id username = "app_user" } # Outputs output "cluster_host" { value = rivestack_cluster.production.host } output "connection_string" { value = rivestack_cluster.production.connection_string sensitive = true } output "app_user_password" { value = rivestack_cluster_user.app_user.password sensitive = true } ``` ```bash theme={null} terraform init terraform plan terraform apply ``` ## Resources ### rivestack\_cluster Manages an HA PostgreSQL cluster. ```hcl theme={null} resource "rivestack_cluster" "example" { name = "my-cluster" region = "eu-central" server_type = "starter" node_count = 2 postgresql_version = 18 } ``` | Attribute | Type | Required | Description | | -------------------- | ------ | -------- | ---------------------------------------------------- | | `name` | String | Yes | Display name for the cluster | | `region` | String | Yes | Region: `eu-central`, `us-east`, or `ap` (Singapore) | | `server_type` | String | No | Server size: `starter`, `growth`, or `scale` | | `node_count` | Number | No | Number of nodes (1-3) | | `postgresql_version` | Number | No | PostgreSQL major version (16, 17, or 18) | | `db_name` | String | No | Name of the default database | | `db_type` | String | No | Cluster type: `ha` or `core_solo` | | `extensions` | List | No | Extensions to install at creation time | **Read-only attributes:** `id`, `host`, `connection_string`, `db_user`, `db_password`, `status`, `tenant_id`, `created_at`, `updated_at` ### rivestack\_cluster\_database Creates a database on a cluster. ```hcl theme={null} resource "rivestack_cluster_database" "example" { cluster_id = rivestack_cluster.example.id name = "analytics" owner = rivestack_cluster.example.db_user } ``` | Attribute | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------ | | `cluster_id` | String | Yes | ID of the cluster | | `name` | String | Yes | Database name | | `owner` | String | No | Database owner username (defaults to cluster's default user) | ### rivestack\_cluster\_user Creates a database user with an auto-generated password. ```hcl theme={null} resource "rivestack_cluster_user" "example" { cluster_id = rivestack_cluster.example.id username = "app_user" } ``` | Attribute | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------ | | `cluster_id` | String | Yes | ID of the cluster | | `username` | String | Yes | Username (letters, numbers, underscores; max 63 chars) | **Read-only attributes:** `id`, `password` (sensitive) ### rivestack\_cluster\_grant Grants a user access to a database. ```hcl theme={null} resource "rivestack_cluster_grant" "example" { cluster_id = rivestack_cluster.example.id username = rivestack_cluster_user.example.username database = rivestack_cluster_database.example.name access = "read" } ``` | Attribute | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------- | | `cluster_id` | String | Yes | ID of the cluster | | `username` | String | Yes | Username to grant access to | | `database` | String | Yes | Database to grant access on | | `access` | String | No | Access level: `read` (SELECT) or `write` (SELECT, INSERT, UPDATE, DELETE) | Grant revocation is not currently supported by the API. Destroying this resource removes it from Terraform state only. ### rivestack\_cluster\_extension Installs a PostgreSQL extension on a database. ```hcl theme={null} resource "rivestack_cluster_extension" "vector" { cluster_id = rivestack_cluster.example.id extension = "vector" database = rivestack_cluster_database.example.name } ``` | Attribute | Type | Required | Description | | ------------ | ------ | -------- | -------------------------------------------------------- | | `cluster_id` | String | Yes | ID of the cluster | | `extension` | String | Yes | Extension name (e.g., `vector`, `pg_trgm`, `pgcrypto`) | | `database` | String | No | Target database (defaults to cluster's primary database) | Extensions cannot be removed from running clusters. Destroying this resource removes it from Terraform state only. ### rivestack\_cluster\_backup\_config Configures automated backups for a cluster. ```hcl theme={null} resource "rivestack_cluster_backup_config" "example" { cluster_id = rivestack_cluster.example.id enabled = true schedule = "0 3 * * *" retention_full = 14 } ``` | Attribute | Type | Required | Description | | ---------------- | ------- | -------- | ----------------------------------------------------- | | `cluster_id` | String | Yes | ID of the cluster | | `enabled` | Boolean | Yes | Whether automated backups are enabled | | `schedule` | String | No | Cron schedule (e.g., `"0 3 * * *"` for daily at 3 AM) | | `retention_full` | Number | No | Number of days to retain full backups | ## Data sources ### rivestack\_cluster Read information about an existing cluster. ```hcl theme={null} data "rivestack_cluster" "existing" { id = "42" } output "cluster_host" { value = data.rivestack_cluster.existing.host } ``` **Read-only attributes:** `name`, `region`, `server_type`, `node_count`, `postgresql_version`, `host`, `connection_string`, `db_name`, `db_user`, `db_password`, `db_type`, `status`, `health_status`, `tenant_id`, `created_at`, `updated_at` ### rivestack\_server\_types List available server configurations. ```hcl theme={null} data "rivestack_server_types" "available" {} ``` ### rivestack\_extensions List supported PostgreSQL extensions. ```hcl theme={null} data "rivestack_extensions" "available" {} ``` ## Import Import existing resources into Terraform state: ```bash theme={null} # Cluster terraform import rivestack_cluster.example CLUSTER_ID # Database terraform import rivestack_cluster_database.example CLUSTER_ID/DATABASE_NAME # User terraform import rivestack_cluster_user.example CLUSTER_ID/USERNAME # Extension terraform import rivestack_cluster_extension.example CLUSTER_ID/EXTENSION/DATABASE # Grant terraform import rivestack_cluster_grant.example CLUSTER_ID/USERNAME/DATABASE # Backup config terraform import rivestack_cluster_backup_config.example CLUSTER_ID ``` ## Links * [Terraform Registry](https://registry.terraform.io/providers/Rivestack/rivestack/latest) * [GitHub Repository](https://github.com/Rivestack/terraform-provider-rivestack) * [Examples](https://github.com/Rivestack/terraform-provider-rivestack/tree/main/examples)