MariaDB · PostgreSQL · Redis

Managed Databases on Kubernetes

Most applications need more than one data store: a relational engine for records that must be correct, and an in-memory store for data that must be fast. GRN.CLOUD runs all three for you — provisioning, replication, failover, patching, backup and restore — on persistent block storage next to your workloads, in the Netherlands, under EU jurisdiction. You get a connection string and superuser access to your own data. We operate everything underneath it.

  • We run it — you use it
  • Replication and automated failover
  • Scheduled backups, snapshots & PITR
  • Dutch jurisdiction · GDPR

No credit card required · Free during the Alpha phase · View pricing

Choosing a data store

A storage engine trades durability against latency, and no engine sits at both ends of that trade. A relational database confirms a write only after the change has reached a write-ahead log on persistent storage, which is what makes the answer trustworthy and what puts a disk round trip in the commit path. An in-memory store answers from RAM and treats persistence as a tunable, which is what makes it fast and what makes it unsuitable as the only copy of anything you cannot rebuild.

That is why most systems run two. A relational engine holds the system of record — orders, invoices, accounts, anything where a lost write is a real-world problem — and an in-memory store absorbs the repetitive read traffic in front of it: sessions, rate-limit counters, rendered fragments, computed aggregates. The split is not a compromise. It removes the hot read path from the database that has to be correct, which is usually the cheapest available way to make both faster.

Scaling follows the same division. Relational engines scale vertically first — more CPU, more memory, faster storage — and then horizontally to read replicas, because a cross-shard join has no cheap implementation. Key-value stores shard by key with far less coordination, because there is no join to resolve in the first place. Which engine you can scale, and how, is generally a more useful selection criterion than raw benchmark throughput.

On this page. Each engine below is documented on its own terms: architecture, durability, replication, what it costs to operate, and where it fits. The sections are deliberately not written as a scorecard — the engines solve different problems, and the useful question is which of your problems each one solves.

MariaDB

Relational · GPLv2 · MySQL-compatible

Architecture

MariaDB separates SQL parsing, optimisation and execution from physical storage through a pluggable storage-engine API. The engine chosen for a table decides how its rows are stored, locked and recovered. InnoDB is the default and the only one most deployments need: rows live in a clustered B-tree ordered by primary key, so a primary-key lookup reaches the row directly, and secondary indexes store the primary key rather than a physical row pointer — which keeps them stable across page splits at the cost of a second traversal. Aria backs internal temporary tables, MyRocks is an LSM-tree engine for write-heavy, highly compressible data, and ColumnStore handles analytical scans. Mixing engines within a single transaction gives up the transactional guarantee, so in practice a schema picks one.

Because MariaDB keeps the MySQL wire protocol and dialect, existing MySQL clients, drivers and ORMs connect without modification. Compatibility is at the protocol and dialect level, not feature-for-feature — the two projects have diverged since the fork, and MariaDB-specific features have no MySQL equivalent.

Transactions and durability

InnoDB provides ACID semantics through a write-ahead redo log plus MVCC. A transaction is durable once its redo record is on disk; innodb_flush_log_at_trx_commit=1 forces that fsync at every commit, which is what makes durability real and also the single largest source of commit latency. Undo logs retain prior row versions so readers see a consistent snapshot without blocking writers, and the doublewrite buffer guards against torn pages when a write is interrupted mid-page. Locking is row-level, and the default isolation level is REPEATABLE READ.

The buffer pool is the setting that matters most for performance: it caches data and index pages, and it should be sized to the working set, not to the whole database. On Kubernetes it must also fit inside the pod's memory limit with headroom — a buffer pool sized against total node memory is the most common way to get a database OOM-killed.

Replication and high availability

Replication ships binary-log events from a primary to replicas. Asynchronous replication is the default and does not delay commits, which means a failover can lose transactions that had not yet reached a replica. Semi-synchronous replication makes the primary wait for at least one replica to acknowledge receipt, trading commit latency for a much smaller loss window. GTIDs make replicas repointable without hand-tracking log positions, which is what allows an operator to automate promotion. Galera is a different model again: certification-based synchronous replication across a quorum of nodes, where a transaction commits everywhere or nowhere.

Operating it here

The data directory sits on a single ReadWriteOnce volume — block storage, never a shared network filesystem. Backups are logical (mariadb-dump) or physical (mariabackup, which streams a consistent copy without stopping writes); we run both on a schedule and verify the restore rather than only the backup job's exit code.

durability & memory — the settings that matter
# commit is durable only when the redo record reaches disk
innodb_flush_log_at_trx_commit = 1
sync_binlog                    = 1

# size to the working set, and inside the pod memory limit
innodb_buffer_pool_size        = 4G

Where it fits

MariaDB is the right default when the application already targets MySQL — WordPress, Magento, Odoo, most PHP applications, and the long tail of ERP and CRM systems — or when the workload is general-purpose OLTP with straightforward queries and a team whose runbooks are already MySQL runbooks. It is efficient on modest CPU and memory, which makes it the cheaper choice at small scale. It is the weaker choice when queries are analytically complex, when you need indexed document storage (its JSON type is validated text, not a binary indexed type), or when you want the extension ecosystem described below.

PostgreSQL

Object-relational · PostgreSQL Licence

Architecture

PostgreSQL forks a backend process per connection. That gives strong isolation between sessions and makes a crashed backend survivable, but it also means each connection carries a real process cost — which is why a connection pooler stops being optional somewhere in the low hundreds of connections. Shared buffers cache pages in front of the operating system's own page cache, and each backend gets its own work_mem for sorts and hashes, allocated per operation rather than per query, so an aggressive setting multiplied across concurrent sessions is a genuine way to exhaust memory.

The type system is extensible from SQL, and this is the feature that most distinguishes PostgreSQL in practice: types, operators, index access methods and functions can be added without patching the server. That is the mechanism behind PostGIS for spatial data, pgvector for embedding similarity search, and TimescaleDB for time-series partitioning — they are not forks, they are extensions loaded into a standard server.

Concurrency, MVCC and vacuum

Concurrency is handled by MVCC: an update writes a new row version rather than overwriting the old one, so readers never block writers and writers never block readers. The cost is that superseded versions — dead tuples — accumulate and must be reclaimed. Autovacuum does this in the background, and tuning it is the single most consequential piece of PostgreSQL operations work: too passive and tables bloat, indexes lose selectivity and query plans degrade; too aggressive and vacuum competes with the workload for I/O. Autovacuum also advances the transaction-ID horizon, which is what prevents wraparound.

All four standard isolation levels are accepted, and serializable is genuinely implemented via serializable snapshot isolation rather than by locking. JSONB stores documents in a binary form that supports GIN indexing, so relational and document data can live in one engine with both indexed — a materially different position from a validated-text JSON column.

Replication and point-in-time recovery

Physical streaming replication ships write-ahead log records to replicas that stay open for read-only queries, and can be synchronous or asynchronous per standby. Logical replication decodes WAL into row changes and replays them selectively, which is what makes near-zero downtime major-version upgrades and partial replication possible. Archiving WAL continuously alongside periodic base backups gives point-in-time recovery: restore the base backup, replay WAL to a chosen timestamp or transaction, stop there. That is the capability that turns "we have backups" into "we can undo the bad migration that ran at 14:32".

recovery to a point in time
# base backup + archived WAL, replayed to a chosen instant
restore_command      = 'cp /wal_archive/%f %p'
recovery_target_time = '2026-08-05 14:31:00+02'
recovery_target_action = 'promote'

Operating it here

We run PostgreSQL under an operator that owns the cluster lifecycle: bootstrap, streaming replicas, synchronous quorum, automated promotion with fencing of the demoted primary, base backups and continuous WAL archiving to object storage. Major-version upgrades are planned work — pg_upgrade or logical replication, rehearsed against a clone first — rather than something that happens silently in a maintenance window.

Where it fits

PostgreSQL is the right choice when correctness under concurrency is a requirement rather than a preference — financial systems, regulated data, anything where a reconciliation failure is expensive — and when queries are genuinely complex: multi-table joins, window functions, CTEs, aggregation over large sets. It is also the answer when one engine has to hold relational rows, JSON documents, spatial geometry or vector embeddings at once, and when recovery to an arbitrary point in time is a stated requirement. The cost is a larger operational surface: pooling, vacuum tuning and planned major upgrades are real work, and they are the work we take on.

Redis

In-memory data structures · AGPLv3 / RSALv2

Architecture

Redis is a data structure server, not a key-blob cache. Values are typed — strings, hashes, lists, sets, sorted sets, streams, bitmaps, HyperLogLogs — and each type has native commands, so incrementing a counter, appending to a queue or updating a leaderboard is one server-side operation instead of a read-modify-write round trip with a race condition in the middle. Command execution is single-threaded on one event loop, which is why every command is atomic without any locking, and why one slow command (an unbounded KEYS, a large SORT) blocks every other client. I/O threads parallelise socket reads and writes, but not command execution.

Persistence and its limits

Two mechanisms, usable together. RDB writes a point-in-time snapshot by forking a child process; because the fork relies on copy-on-write, a write-heavy instance can transiently need substantially more memory than its dataset while the snapshot is written. AOF appends every write command to a log and replays it on restart, with appendfsync everysec as the usual compromise — bounded loss of about one second, without an fsync per command.

Redis is not a system of record. Replication is asynchronous, so a failover can lose recently acknowledged writes. MULTI/EXEC executes a batch atomically but has no rollback: if a command fails at runtime, the preceding commands stand. Treat Redis as authoritative only for data you can rebuild from the relational engine — and everything above becomes an acceptable trade rather than a risk.

Memory management

The dataset lives in RAM, so memory is the capacity limit and the cost driver. maxmemory caps usage and an eviction policy decides what happens at the cap: allkeys-lru for a pure cache, volatile-ttl when only keys with an expiry may be discarded, and noeviction — which rejects writes rather than losing data — when Redis holds queues or sessions that must not silently vanish. Native per-key TTL is what makes Redis a correct cache rather than merely a fast one: expiry is enforced by the server, not by hopeful application logic.

cache vs. queue — different policies
# cache: discard the coldest keys under pressure
maxmemory        512mb
maxmemory-policy allkeys-lru

# queue/sessions: refuse writes rather than drop data
maxmemory-policy noeviction
appendonly       yes
appendfsync      everysec

Replication, failover and sharding

Replicas follow a primary asynchronously. Sentinel adds monitoring, quorum-based failure detection and automatic promotion for a single-primary deployment. Redis Cluster is the horizontal option: the keyspace is divided into 16,384 hash slots distributed across shards, each with its own replicas. Cluster mode constrains multi-key operations to keys that hash to the same slot, which is a schema decision — hash tags — made before deployment rather than after.

Where it fits

Redis belongs in front of a relational database, not instead of one. It is the right tool for sub-millisecond reads, session and token storage, caching of query results or rendered fragments, rate limiting and counters, job queues, Pub/Sub between services, and stream processing with consumer groups. It is the wrong tool whenever you need ad-hoc queries across keys, joins, or a durable transactional guarantee — it has no query language and no rollback, by design.

How a database runs on Kubernetes

Stateful workloads need stable identity, stable storage and ordered change. Kubernetes provides all three; an operator supplies the database-specific logic on top. This is the machinery we operate on your behalf — the boundary is one layer down from where you work.

Your data & schema

Tables · indexes · queries · roles
You operate

Your schema, your data, your queries, your application roles. You connect over TLS with superuser access to your own database and manage it exactly as you would any other — psql, your ORM, your migration tool, your dashboards. We do not read it, and nothing about the managed service constrains what you can put in it.

Operator

CloudNativePG · mariadb-operator · Redis operator
GRN-managed

A controller that extends the Kubernetes API with a database-specific Custom Resource. The choices you make in the console — engine, version, replica count, storage size, backup schedule — become that resource, and the operator continuously reconciles reality to match it. It handles what a generic scheduler cannot reason about: initialising a data directory, promoting a replica, running a base backup, sequencing a minor-version upgrade. This is the layer that makes the service managed rather than merely hosted.

StatefulSet

Ordinal identity · headless Service
GRN-managed

Unlike a Deployment, a StatefulSet gives each pod a stable ordinal name and a stable DNS record that survive rescheduling, and it binds each pod to its own volume through a volumeClaimTemplate. Rolling updates proceed one pod at a time in order, which is what makes a replica-then-primary upgrade sequence possible.

Persistent storage

PVC · StorageClass · CSI
GRN-managed

Each pod claims a PersistentVolume through a PersistentVolumeClaim against a StorageClass. The CSI driver provisions it dynamically at first use, so no volume is created by hand. The classes are local NVMe (OpenEBS) for latency-sensitive data directories and Ceph RBD block (Rook) for replicated, snapshot-capable volumes. Both support online expansion — grow the PVC and the filesystem follows. Database data directories go on block storage, never on a shared network filesystem.

Scheduling & self-healing

Anti-affinity · PDB · probes
GRN-managed

Anti-affinity rules spread replicas across nodes so a single host failure cannot take a quorum with it. Liveness and readiness probes remove an unhealthy instance from the Service endpoints before clients notice, and a PodDisruptionBudget stops a node drain from evicting more replicas than the cluster can lose. A failed pod is rescheduled and reattached to the same volume.

What we operate

The capabilities below apply to all three engines, and are the substance of the managed service. They are properties of the platform underneath the database, which is why they do not vary by engine.

High availability and failover

Clusters run multiple nodes with replication, spread across hosts by anti-affinity so no single failure takes a quorum. When a primary fails the operator detects it, promotes a replica, fences the demoted node to prevent split-brain, and repoints the read-write Service endpoint. Applications reconnect to the same DNS name; nothing in your connection string changes.

Backup, restore and recovery

Scheduled base backups and CSI volume snapshots are written through Velero to S3-compatible object storage held off the cluster, so a cluster-level failure does not take the backups with it. For PostgreSQL, continuous WAL archiving adds recovery to any chosen timestamp. We test restores rather than only monitoring that backup jobs exited zero — an unverified backup is a hypothesis, not a recovery plan.

Storage

Local NVMe for latency-bound data directories, Ceph RBD block for replicated and snapshot-capable volumes, and S3 object storage for backups and WAL archive. Volumes are dynamically provisioned and expand online without a maintenance window. Storage is €0.044/GB-month and backup €0.008/GB-month, with no per-GB egress charge.

Security and isolation

Credentials are held in Kubernetes Secrets and never in application images or manifests. Access to the database is over TLS, and NetworkPolicy restricts which pods can reach the database port at all — the default is that nothing can, until you say otherwise. RBAC governs who can act on the database resources, and the data sits on encrypted storage in the Netherlands under Dutch jurisdiction, with a standard DPA and no US Cloud Act exposure.

Monitoring and alerting

Per-engine exporters feed the cluster Prometheus, with Grafana dashboards covering connections, replication lag, cache hit ratio, lock waits, checkpoint activity and disk usage. Alerts are set on the conditions that precede an outage — replication lag trending up, a volume projected to fill, autovacuum falling behind — and route to our on-call engineers rather than to you.

Scaling

CPU and memory resize from the console, storage expands online, and read replicas can be added for query fan-out. Connection pooling keeps a large fleet of application pods from exhausting backend connections, which matters most for PostgreSQL. We apply the change and sequence the rollout; you do not plan it.

How it connects to the rest of the platform

A database here is a workload on the same cluster as everything else, so it inherits the platform rather than integrating with it across a boundary. It uses block and object storage through CSI for volumes, snapshots and backups; runs on the same compute as your containers, virtual machines and GPU nodes, so an AI workload queries its metadata store over the cluster network rather than the public internet; and uses cluster networking for internal Services and DNS, NetworkPolicy segmentation, load balancers and ingress where an external endpoint is needed, and Submariner for cross-cluster or site-to-site connectivity. All of it sits on managed Kubernetes.

Reference architectures

Two shapes that cover most deployments.

A · Transactional web application

Web application

Stateless pods behind an Ingress

PostgreSQL

Primary plus a streaming replica

Persistent volume

Ceph RBD block, expandable online

Backup to object storage

Base backup plus WAL archive for PITR

B · Read-heavy API with a cache

API service

Reads the cache before the database

Redis

Cache, sessions and rate limits, with TTL

PostgreSQL

System of record, queried on cache miss

Monitoring

Hit ratio, replication lag, alerting

In shape B the cache holds no data that cannot be rebuilt from PostgreSQL. That is what makes it safe to lose a Redis node: the read path slows down until it warms again, but nothing is lost.

FAQ

The questions an engineer actually asks.

What exactly do you manage, and what stays mine?

We manage the database as a service: provisioning, configuration and tuning, minor-version patching and major-version upgrades, replication and failover, scheduled backups and verified restores, monitoring and alerting, and the nodes, storage and networking underneath. You own the data, the schema and the queries, and you connect with superuser access to your own database over TLS — psql, your ORM and your migration tooling all work normally. You do not get cluster-admin on the Kubernetes cluster or shell access to the database pods; that is the part we take responsibility for. Anything that needs a configuration change we cannot expose safely, our engineers make for you.

Is running a database on Kubernetes actually a good idea?

It is now the normal way to do it, provided an operator is driving it rather than a bare StatefulSet. The operator supplies the database-specific knowledge — failover, base backups, ordered upgrades — that Kubernetes itself has no opinion about. What matters more than the orchestrator is the storage underneath: a block StorageClass with predictable latency, the data directory kept off shared network filesystems, and a restore path that has actually been tested. Those are the decisions we have already made, and the reason this is offered as a managed service rather than a deployment guide.

How do I migrate an existing database in?

We run the migration with you, using the standard tools. For MariaDB, mariadb-dump for a full load or replication from your existing primary for a low-downtime cutover. For PostgreSQL, pg_dump/pg_restore, or logical replication when downtime has to stay short. For Redis, replication from the existing instance or an RDB load. We size the target, rehearse the cutover, agree a rollback point and run it in a window you choose.

What does it cost?

There is no database licence fee. You pay for the compute, storage and backup the instance uses, pay-as-you-go, with 10% off on annual commitment — storage €0.044/GB-month, backup €0.008/GB-month, cross-region replication €0.0465/GB-month, and no per-GB egress charge. The management fee for the service itself depends on the engine, the topology and the support hours you need, and is quoted per instance; ask us for a figure. During the Alpha phase you get 10 vCPU and 32 GB of memory free to run on.

What happens when something breaks at 03:00?

Failover is automatic and does not wait for a human: the operator detects the failed primary, promotes a replica, fences the old one and repoints the read-write endpoint. Alerts route to our on-call engineers, who deal with the underlying cause — a failed node, a full volume, a runaway query plan. You are notified rather than paged. Support hours, response targets and escalation are set per contract, so agree them with sales rather than assuming them from this page.

Which versions and licences do you run?

We track upstream stable releases and keep instances on a supported major version, upgrading on a schedule agreed with you rather than unannounced. Licensing differs across the three and is worth knowing: MariaDB Server is GPLv2, PostgreSQL uses the permissive PostgreSQL Licence, and Redis is dual-licensed under AGPLv3 or RSALv2 since Redis 8. Where the Redis licence is a problem for you, Valkey — the BSD-licensed fork maintained under the Linux Foundation — is protocol-compatible and can be substituted.

Put your data on infrastructure that answers to Europe.

MariaDB, PostgreSQL or Redis — provisioned in one step, operated by our engineers, and yours to use.

100% renewable energy · EU data residency · No US Cloud Act exposure · No egress tax