Three engines
Engine Topology Failover decided by Holds
MariaDB + Galera Synchronous multi-primary; garbd at even node counts Galera weighted quorum Panel state, tenant MySQL
PostgreSQL + repmgr + PgBouncer One primary, hot standby per node, PgBouncer per node Signed declaration (repmgr failover=manual) Tenant PostgreSQL, CrowdSec, JuiceFS metadata
Redis + Sentinel One master, replicas, Sentinel per node Signed declaration Rspamd, sessions, cache, Caddy certs, counters
Operator · Databases.
MariaDB and Galera
Ports 3306 (peers + tenant container bridge), 4567, 4568 IST, 4444 SST, 4569 garbd. Peers only.
TLS Cluster CA certs. GET/POST /galera/ssl.
Ready gate ozpanel-galera-ready.service: Primary and Synced before consumers start.
Accounts Per-node service accounts (ozpanel_<tag>). POST /mariadb/panel-credential/rotate.
Crash ozpanel galera-recover: rejoin, join-only.
Lone survivor Bootstraps alone only if every guard passes; else stays down and says why.
Start safety gcache.recover=no; MemoryMax = half of RAM.
All down POST /galera/recover-position everywhere → POST /galera/bootstrap on the highest → POST /galera/join others. See Cluster .
PostgreSQL, repmgr, PgBouncer
Connect Always local PgBouncer 127.0.0.1:6432, pool_mode=transaction. Never 5432.
Replication wal_keep_size=1GB, wal_log_hints=on. No slots, no WAL archive, no PITR.
Synchronous Default synchronous_commit=local; JuiceFS role uses on. FIRST 1 over the succession. Lone survivor releases it, alerts.
Promotion Lag ≤ max_lag_bytes (1 MiB). Recorded sync standby only.
Fences Self-fence · boot gate · rejoin marker · pooler repointed every pass.
PgBouncer auth auth_query via a single-function role. /pgbouncer/auth-query/enable, …/rotate.
Readouts GET /pgbouncer/failover, /postgresql/verify, /repmgrd/sync, /cluster/sync-replication.
Moves Use POST /cluster/handover-to. /repmgrd/rejoin rebuilds a standby.
Redis and Sentinel
One decider replica-priority 0 everywhere; the declared primary commands the move.
Two nodes Second Sentinel on 26380 on the replica; min-replicas-to-write 1.
Accounts One ACL account per service; FLUSHALL, CONFIG, ACL, SHUTDOWN, REPLICAOF, DEBUG removed. /redis/acl, …/{service}/rotate.
Memory maxmemory 1gb, noeviction at install.
Check GET /redis/verify, /redis/topology.
Tenant databases
Tenant console (Databases & tasks) or operator. Name ozt<uid>_<name>, name part ^[a-z][a-z0-9_]{0,31}$.
POST /tenants/{id}/databases {"name":"shop","engine":"mysql"} # mysql | postgres; owner user, password once
POST /tenants/{id}/db-users {"name":"report","engine":"mysql"}
POST /tenants/{id}/databases/{name}/grants {"user":"ozt100001_report","privilege":"readonly"}
privilegeMariaDB PostgreSQL
readonlySELECT CONNECT, USAGE, SELECT (incl. future)
readwriteSELECT, INSERT, UPDATE, DELETE Same + sequences
all (default)Full owner list on that DB All on tables, sequences
Passwords 20 chars, shown once. Reset: …/databases/{name}/password, …/db-users/{user}/password.
Delete Database + bundled user. PostgreSQL WITH (FORCE). User owning a DB: 409.
Suspend Logins locked, sessions closed, nothing dropped.
Limits Plan databases, db_users. Size measured, not capped.
Isolation Data private. PostgreSQL catalogue lists names; GET /postgresql/tenant-visibility.
Import mariadb to 127.0.0.1:3306 or psql to :6432. Upload endpoint: not available.
Backup One gzip dump per database in each snapshot, checksummed. See Backups .
Client MariaDB PostgreSQL Redis
Site, cron 127.0.0.1:3306 127.0.0.1:6432 127.0.0.1:6379, ACL
Container ozpanel-db:3306— —
Internet Never
Tenant · Databases & tasks.
Engine settings
Operator. GET/PUT /{module}/settings; validated, applied, rolled back on failure. Cluster-wide fields applied on every node, including later joiners. Config triad: /{module}/config/live, /stored, /diff.
Module Cluster-wide (default) Per node
mariadbmax_connections 151 · max_allowed_packet 16M · wait/interactive_timeout 28800 · thread_cache_size 151 · innodb_flush_log_at_trx_commit 2 · slow_query_log OFF · long_query_time 10 · general_log OFF innodb_buffer_pool_size · innodb_io_capacity · table_open_cache · tmp_table_size · max_heap_table_size
postgresqlmax_connections 200 · superuser_reserved 3 · max_worker_processes 8 (rolling restart) · idle_in_transaction 0 · max/min_wal_size 1GB/80MB · wal_keep_size 1GB · checkpoint 0.9 · wal_compression off · statistics 100 · log_min_duration -1 · log_lock_waits off · autovacuum on shared_buffers · effective_cache_size · work_mem · maintenance_work_mem · random_page_cost · effective_io_concurrency · parallel workers
pgbouncerpool_mode transaction · default_pool_size 25 · max_client_conn 500 · server_idle 600 · server_lifetime 3600 · query_wait 120 · client_login 60 · others 0
repmgrdfailover manual · connection_check_type · reconnect/monitor timing · monitoring_history off priority
redisnoeviction · samples 5 · save 3600 1 300 100 60 10000 · appendonly yes · everysec · maxclients 10000 · tcp-keepalive 300 · loglevel notice · slowlog 10000/128 maxmemory · tcp-backlog
redis-sentineldown-after 10000 · failover-timeout 60000 · parallel-syncs 1
garbdgalera_nodes · galera_group · galera_options · log_file
POST /postgresql/param sets other parameters (replication, listener, paths refused). POST /redis/param, /pgbouncer/param, /postfix/param likewise.
CLI
Command Run by
ozpanel galera-ready-gate, galera-position, galera-recoversystemd
ozpanel boot-gate [--pgdata] [--wait-for-peer]PostgreSQL pre-start
ozpanel pgbouncer-follow-primaryrepmgr event hook
ozpanel migrate, ozpanel recoverOperator