System DesignMysql Galera ClusterHigh Availability14 min read

Self-Hosted Alternative to Algolia and AWS Aurora: A Free Blueprint for Scaling Past 25 Million Records with MySQL Galera cluster

Cut search infra costs by self-hosting MySQL Galera cluster, Typesense, and RabbitMQ. A proven 25M-record handling blueprint with configs.

AA
AI Agent Expert & Full-Stack Developer
Published July 22, 2026Updated July 25, 2026

Self-Hosted Alternative to Algolia and AWS Aurora: A Free Blueprint for Scaling Past 25 Million Records

Scaling a growing platform to tens of millions of records almost always ends with a painful invoice. Algolia's own 2026 pricing works out to roughly $9,960 a month in record-storage fees alone once you cross 25 million records, before a single search request is billed (checkthat.ai, 2026). Managed Elasticsearch and OpenSearch clusters aren't much kinder: they typically need 1 to 8 GB of RAM per node just to stay stable (OSSAlt, 2026).

None of this is unique to e-commerce catalogs. B2B multi-tenant directories, IoT device registries, high-throughput CMS platforms, and large asset-tracking systems all hit the same wall. A single relational database can't take on high-frequency writes and typo-tolerant search at the same time without one starving the other.

The good news is that you don't have to choose between an expensive managed stack and a database that falls over. This is the exact blueprint I use to build a highly available, write-optimized storage tier paired with a sub-50ms search layer, using MySQL Galera Cluster, Typesense, and RabbitMQ, all free and self-hosted.

Key Takeaways

  • A 25-million-record catalog on Algolia's Grow tier costs roughly $9,960/month in record fees alone, on top of search-request billing (checkthat.ai, 2026).
  • Splitting writes (MySQL Galera) from search (Typesense) and syncing them through a durable queue (RabbitMQ) removes the lock contention that kills single-node databases at scale.
  • Self-hosting isn't literally free: you're still paying for compute. What disappears is per-record and per-request licensing, which is the part that actually scales against you.

Where a Single-Node Database Actually Breaks

A lone MySQL or Postgres instance usually starts choking on concurrent full-text search long before it runs out of disk space. The problem isn't storage. It's contention: every scraper update, state change, or user edit competes for the same locks that your search queries need to read from.

I've hit this exact ceiling on a high-volume marketplace platform I built for a client. Once the catalog crossed into the tens of millions of active listings, the single primary MySQL instance became the bottleneck for both scraper-driven writes and buyer-facing search, well before storage was ever a concern. Splitting write and read paths, the same pattern below, is what got response times back under a second.

Two demands are fighting for the same resource:

DemandWhat it needsWhat breaks first
High-frequency writesFast inserts, minimal lock contentionB-tree indexes degrade, tables lock
Typo-tolerant searchFull-text matching, faceted filteringQuery latency spikes under write load

The fix isn't a bigger server. It's decoupling the two workloads into a write-optimized storage tier and a read-optimized search tier, connected by a reliable sync layer. In practice, that's CQRS applied at the infrastructure level rather than just in your application code: one system of record for writes, a separate, purpose-built index for reads.

I saw the same shape of problem building PosifyHub, a multi-tenant inventory and POS platform for SMEs: even a well-indexed Postgres table starts to choke once you're running fuzzy, cross-tenant product search on top of constant stock updates.


Building a Write Tier That Never Goes Down: MySQL Galera Cluster

Galera Cluster uses synchronous, multi-master replication, so every node can accept writes and a transaction only commits once the other nodes confirm it. That's a meaningfully different guarantee than a standard master-replica setup, where the single write node is still a point of failure. Because it relies on quorum, you need at least three nodes to survive a single node failure without risking a split-brain.

Worth noting: "Galera" itself is a replication plugin, not a shippable product on its own. In production you'll run it through Percona XtraDB Cluster or MariaDB Galera Cluster, both free and widely deployed.

Core Cluster Configuration (my.cnf)

ini
[mysqld]
binlog_format=ROW
default-storage-engine=InnoDB
innodb_autoinc_lock_mode=2
innodb_doublewrite=1

# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_name="catalog_cluster"
wsrep_cluster_address="gcomm://10.0.0.1,10.0.0.2,10.0.0.3"
wsrep_sst_method=mariabackup

# Sync parameters
wsrep_provider_options="gcache.size=2G; evs.keepalive_period=PT3S"

innodb_autoinc_lock_mode=2 isn't optional here. Without interleaved auto-increment, multi-master writes can generate conflicting IDs across nodes.

Put ProxySQL, also free and open-source, in front of the cluster to distribute writes across all active nodes. If one node drops, ProxySQL reroutes traffic without a visible outage.

A quick note on nomenclature: if you've been comparing this to AWS Aurora Multi-Master, that specific feature is gone. AWS deprecated it a few years back, tied to the MySQL 5.6-compatible Aurora version that has since reached end of life (AWS documentation, 2023). Its closest living relative is Aurora PostgreSQL Limitless Database, a sharded, serverless offering that went generally available in October 2024. It's Postgres-only, though, and still billed by usage (AWS News Blog, 2024). Galera doesn't need any of that: it's been doing synchronous multi-master on plain MySQL for over a decade.


Why Typesense Beats Elasticsearch for Self-Hosted Search at This Scale

Running full-text search against a relational database directly falls apart well before 25 million rows. Typesense, a free, open-source, in-memory search engine written in C++, is built to sidestep that entirely, and it does it on a fraction of the hardware Elasticsearch needs. A self-hosted Typesense node can run on roughly 256 MB of RAM, versus 1 to 8 GB minimum for Elasticsearch and around 512 MB for Meilisearch (OSSAlt, 2026).

Why does that gap exist? Elasticsearch runs on the JVM, and JVM heap tuning is its own discipline. Typesense skips the JVM entirely, which is most of why its footprint stays small even as your dataset grows.

(Chart: minimum RAM per self-hosted search node, Typesense vs. Meilisearch vs. Elasticsearch. Source: OSSAlt, 2026. See note below on rendering.)

Minimum RAM per self-hosted search node Lollipop chart comparing minimum RAM needs: Typesense ~256MB, Meilisearch ~512MB, Elasticsearch 1-8GB minimum. Source: OSSAlt, 2026. Typesense ~256 MB Meilisearch ~512 MB Elasticsearch 1-8 GB min. Source: OSSAlt, "Meilisearch vs Typesense vs Elasticsearch" (2026)

Schema Definition & Querying in Typesense

javascript
const schema = {
  name: 'catalog_items',
  fields: [
    { name: 'title', type: 'string' },
    { name: 'description', type: 'string' },
    { name: 'category', type: 'string', facet: true },
    { name: 'is_active', type: 'bool', facet: true },
    { name: 'popularity', type: 'int32' }
  ],
  default_sorting_field: 'popularity'
};
javascript
// Typo-tolerant, filtered search
const results = await typesenseClient.collections('catalog_items').documents().search({
  q: 'sensor controller',
  query_by: 'title, description',
  filter_by: 'category:=electronics && is_active:true',
  sort_by: 'popularity:desc',
  per_page: 20
});

For high availability, Typesense uses the Raft consensus algorithm and replicates your full dataset to every node. Like Galera, it needs a minimum of three nodes to tolerate one failure (Typesense documentation, 2026). One trade-off worth knowing up front: HA replication buys you redundancy and read throughput, not extra storage capacity, since each node holds the entire index.


Keeping MySQL and Typesense in Sync Without Losing a Single Record

Once your source of truth (Galera) and your read index (Typesense) live in separate systems, you need a sync layer that survives crashes, restarts, and out-of-order delivery. RabbitMQ paired with Celery workers does that job.

text
+---------------+     Write      +------------------------+
| Ingestion App | -------------> |  MySQL Galera Cluster   |
+---------------+                +------------------------+
        |
        | Publish event
        v
+---------------+
|   RabbitMQ    |
+---------------+
        |
        | Consume task
        v
+---------------+     Upsert     +------------------------+
| Celery Worker | -------------> |  Typesense Search Index |
+---------------+                +------------------------+

Reliable Ingestion Mechanics

  1. Persistent messages. Publish with delivery_mode=2 so messages are written to disk and survive a broker restart.
  2. Dead-letter exchanges (DLX). A worker that fails on a temporary network timeout routes its message to a dead-letter queue for retry instead of losing it silently.
  3. Idempotency and version checks. Because messages can arrive out of order, every update carries a version or timestamp. The worker only applies an update if it's newer than what's already indexed.

One honest caveat: delivery_mode=2 marks a message as persistent, but RabbitMQ still batches writes to disk on its own schedule. That means it isn't an absolute guarantee against loss on its own (RabbitMQ Reliability Guide, 2026). For anything you can't afford to lose, pair persistent messages with quorum queues, RabbitMQ's replicated queue type, and publisher confirms. Publisher confirms wait for an acknowledgment that the message actually made it to disk before your app moves on.

python
# Idempotent Celery task processing updates safely
@app.task(bind=True, max_retries=3)
def sync_record_to_typesense(self, record_id, updated_data, version_timestamp):
    try:
        existing = typesense.collections('catalog_items').documents(record_id).retrieve()
        if existing and existing.get('version') >= version_timestamp:
            return  # Stale, out-of-order message; ignore it

        typesense.collections('catalog_items').documents().upsert({
            'id': record_id,
            'version': version_timestamp,
            **updated_data
        })
    except Exception as exc:
        raise self.retry(exc=exc, countdown=5)

On Virtual Investor Pal, a real estate intelligence platform I built that processes 10TB+ of property data across PostgreSQL and MongoDB, a stale search result isn't just an annoyance. It means showing an investor a rental estimate that no longer matches the underlying comps. That's the kind of bug that erodes trust fast, and it's exactly what the version check above is designed to prevent.


What This Actually Costs, Compared to Algolia and Managed Elasticsearch

Free software doesn't mean free infrastructure. You're still paying for compute, just not for every record or every search request. A single Hetzner CCX33 instance (8 dedicated vCPUs, 32GB RAM), enough to run one node comfortably, is listed at €138.49/month after Hetzner's June 2026 price adjustment (costgoat.com, 2026). Run six of those, a 3-node Galera cluster plus a 3-node Typesense cluster, and you're looking at roughly €830/month (about $900). That number stays flat whether you're storing 5 million records or 50 million.

Algolia's bill doesn't stay flat. Here's how its published Grow-tier record fee ($0.40 per 1,000 records beyond the included 100,000) scales as a catalog grows, based on Algolia's 2026 self-serve rates (checkthat.ai, 2026):

RecordsAlgolia record-storage fee (monthly)
1,000,000~$360
5,000,000~$1,960
10,000,000~$3,960
15,000,000~$5,960
20,000,000~$7,960
25,000,000~$9,960
Algolia record fees vs. one self-hosted VM, at scale Algolia's record-storage fee climbs linearly with catalog size, from about $360/month at 1M records to about $9,960/month at 25M records. A single commodity VM (Hetzner CCX33, ~$150/month) stays flat regardless of record count. Sources: Algolia 2026 pricing (checkthat.ai), Hetzner June 2026 pricing (costgoat.com). $0 $2.5k $5k $7.5k $10k Algolia 1 VM 1M 5M 10M 15M 20M 25M records Sources: Algolia 2026 pricing via checkthat.ai; Hetzner June 2026 pricing via costgoat.com

That comparison only covers record-storage fees. Algolia also bills separately for search requests, so the real gap at scale is larger than the chart shows.

This is the same calculus I walk clients through as part of a cloud infrastructure and FinOps audit: the win from self-hosting isn't that it's free, it's that your bill stops scaling against your own growth. I ran a similar cost-modeling exercise on Cloud Auditor, a concurrent cost-auditing framework for AWS and Azure, and the pattern holds everywhere: managed convenience is usually billed per unit, and infrastructure you operate yourself is usually billed per node.


When This Architecture Is the Wrong Choice

This stack isn't the right default for every team. It's worth being upfront about when to skip it.

  • You don't have dedicated ops capacity. Patching Galera nodes, watching Raft consensus health on Typesense, and rotating RabbitMQ credentials is real, ongoing work that a managed service would otherwise absorb for you, at a price.
  • Your workload is analytical, not transactional. Put timeseries aggregation and log analytics in TimescaleDB or ClickHouse instead.
  • You're nowhere near this scale yet. Under a couple million records, six VMs and three separate systems to operate is complexity you don't need yet. A single well-tuned Postgres or MySQL instance, properly indexed, will still outperform it.
  • You need a vendor SLA. Some regulated environments simply require a support contract and audit trail that only a managed provider offers.

Frequently Asked Questions

Why not just use Elasticsearch instead of Typesense?

You can, but it costs more to run. Elasticsearch needs significant JVM heap tuning and typically 1 to 8 GB of RAM per node to stay stable, versus roughly 256 MB for Typesense (OSSAlt, 2026). Elasticsearch still makes sense if you're already running the ELK stack for log analytics or need its deeper aggregation features. For app and catalog search, though, Typesense usually wins on cost and simplicity.

Does MySQL Galera actually replace AWS Aurora Multi-Master?

Not exactly, because Aurora Multi-Master doesn't exist anymore. AWS deprecated it years ago, tied to an Aurora MySQL version that's since reached end of life (AWS, 2023). Galera gives you the synchronous multi-master write model that Aurora Multi-Master used to promise, on plain MySQL, without a managed-service bill attached.

How does Galera protect against data loss if a node crashes?

Galera's replication is synchronous: a write only commits once every active node in the cluster has confirmed it. If a node goes down, the remaining nodes keep serving traffic without losing any committed transaction.

Is delivery_mode=2 alone enough to guarantee RabbitMQ never loses a message?

No, and this is a common misconception. Persistent delivery mode gets messages written to disk, but RabbitMQ doesn't confirm that write instantly by default (RabbitMQ Reliability Guide, 2026). For a real guarantee, combine persistent messages with quorum queues and publisher confirms.

Can this stack handle IoT telemetry or SaaS log analytics?

For the transactional and search side, yes. Galera scales to millions of writes and Typesense stays fast across tens of millions of records. For the heavy analytical aggregations that timeseries telemetry usually needs, pair this stack with a dedicated engine like TimescaleDB or ClickHouse rather than trying to force that workload into Galera or Typesense.

Is this setup actually free, or does the cost just move somewhere else?

The software licensing cost goes away. The compute cost doesn't. You're still paying for VMs, and that pricing isn't static either: Hetzner raised its dedicated-vCPU pricing by well over 100% in June 2026 alone (Northflank, 2026). What you avoid is the part of the bill that scales with your record count and search volume, which is what actually punishes growth under Algolia or a managed search API.


Strategic Takeaways

You don't need a five-figure monthly bill to run a highly available database and search layer at 25 million records and beyond.

  • Decoupling beats scaling up. Splitting writes (Galera) from reads (Typesense) removes the lock contention that kills single-node databases, and it's the same CQRS pattern used at the application layer, just applied one level down.
  • "Free" means no per-unit licensing, not zero cost. Budget for compute anyway.
  • Durability needs more than one setting. delivery_mode=2 is a start, not a finish. Pair it with quorum queues, publisher confirms, and idempotent consumers if you genuinely can't afford to lose data, the same version-check pattern shown earlier.

If you're weighing whether to build this yourself, hire a freelancer, or bring in an agency, I've written separately about how to make that call. And if you're scoping the multi-tenant SaaS platform that sits on top of this data layer, that's the kind of AI-SaaS build I take on end to end, architecture through deployment.

More Insights

Hire Remote Fullstack Developers: Key Tips 2026

Discover the best strategies for how to hire a remote fullstack developer the right way. Learn tips for sourcing, assessing skills, and ensuring a perfect cultural fit.

Read Insight

Freelance developer vs Agency: how to choose wisely

Freelance developer vs agency: Compare real costs, compliance risks, MVP timelines, and senior consultant options to choose the right tech partner for your build.

Read Insight
Ready to Build?

Need this architecture implemented for your SaaS?

I help US & UK SaaS founders ship production-grade systems — multi-tenant architectures, workflow engines, and custom platforms — without the guesswork.

Schedule Free ConsultationView My Work