I'm building a small homelab platform on Kubernetes with Gitea, Harbor, and CI runners. Gitea needs persistent repository and application data, while Harbor can use persistent volumes or an S3-compatible service such as MinIO or another object-storage implementation.
This is mainly a learning project, so performance is not a major concern. However, I'd like to understand how the architecture compares with real production setups. If PostgreSQL or object storage runs on the same worker nodes as application workloads, a node failure could affect both compute and storage. StatefulSets, PVCs, Longhorn, Rook, database operators, replication, and backups can help, but I'm unsure how much protection they provide in practice.
Is running PostgreSQL, MySQL, or S3-compatible object storage inside Kubernetes considered a reasonable design, or should these services generally run on dedicated machines or managed platforms? At what scale do separate infrastructure and storage failure domains become worthwhile? Is mixing stateful and stateless workloads on the same workers a problem, and are there meaningful performance or reliability differences between integrated and external storage? I'm especially interested in common production practices and the trade-offs involving high availability, node failure, scaling, backups, disaster recovery, cost, and operational complexity.
4 Answers
There is no universal size threshold. The decision is usually driven by SLOs, failure domains, staffing, compliance, cost, and how much operational responsibility you want to accept. A managed database may be simpler when the provider handles replication, upgrades, backups, and failover. On the other hand, at larger on-premises deployments, managed services can become expensive or unavailable, and running database operators or distributed storage internally may provide better cost and control.
If you run PostgreSQL in Kubernetes, use a mature operator rather than treating a basic StatefulSet as a complete database solution. The operator should coordinate replication, failover, upgrades, backups, and recovery. The same principle applies to object storage: use a maintained project and understand its replication and durability model rather than assuming that a PVC automatically provides highly available storage.
The practical answer often depends on what is considered tier-zero infrastructure. Git hosting, container registries, DNS, and storage may be required to recover or even bootstrap the rest of the platform. If your cluster depends on an in-cluster registry to pull the images needed to start that registry, for example, you need an independent bootstrap path.
Some operators therefore keep foundational services on separate VMs or infrastructure and run application databases in Kubernetes with an operator. Others use a separate storage platform such as Ceph and expose it to both Kubernetes and non-Kubernetes workloads. Neither approach is automatically superior. Keeping everything in one cluster gives you consistent automation, observability, GitOps, and recovery tooling; moving components out can provide isolation and an easier bootstrap path, but it also creates another platform that must be patched, monitored, backed up, and documented.
For a small self-hosted setup, choose the arrangement you can reliably operate. Avoid adopting distributed storage solely because it sounds more production-like; it can be considerably more complex than a single database with good backups.
This distinction between ordinary workloads and bootstrap or tier-zero services is helpful. The question is not just where the pod runs, but whether the platform can still be recovered when the cluster, registry, or storage system is unavailable.
Kubernetes does not magically make storage highly available. A StatefulSet mainly manages pod identity and rescheduling; PVCs provide storage claims. The actual protection against node failure comes from the storage system and the database or object-storage software itself.
For example, replicated volumes can tolerate some node or disk failures, but they consume extra capacity and still need careful recovery testing. A database cluster needs database-level replication and a way to elect or promote a new primary. Object storage needs its own distributed layout. You also need to consider control-plane availability, because a cluster that cannot schedule or manage workloads may not recover cleanly even if the data is replicated.
For genuine high availability, plan around multiple failure domains, usually at least three suitable nodes, independent backups, and a tested disaster-recovery process. Separate machines are not automatically safer if they share the same power, network, or backup failure domain.
This is not inherently an anti-pattern. For a homelab, running the database and object storage in Kubernetes is a good way to learn the operational side. The important issue is not whether the workload is in Kubernetes, but whether you have the right replication, backups, monitoring, and recovery procedures.
External or managed services become attractive when you need a stronger failure-domain boundary, different scaling characteristics, a stricter SLA, or you no longer want your team operating the storage layer. At small scale, adding separate machines can simply create more infrastructure to maintain without improving the outcome. Stateful and stateless workloads can share workers as long as resource requests, scheduling rules, disruption policies, and storage placement are configured sensibly.
For the homelab I’ll probably run PostgreSQL in Kubernetes and learn from it. What I’m mainly trying to understand is production practice: is PostgreSQL commonly operated inside Kubernetes, or is it still more typical to use separate infrastructure or a managed database?

That makes sense. I was wondering whether starting with an external service is always better if migration may eventually be necessary, but it sounds like the right choice depends more on the required SLA and who is responsible for operating it than on a simple scale limit.