ozpanel sovereign hosting
Get ozpanel

Operator & API reference

Tenants, plans and files

Tenants, plans and every limit, resource control, files, SFTP, cron, terminal, shared storage, backups.

Tenants

A tenant is a real Linux user, ozt<uid>, same uid on every node. uids from 100000, never reused.

AssetWhere
Home/home/ozt<uid> on shared storage
PHPOwn FPM pool and master, as the tenant
Cron/etc/cron.d/ozpanel-ozt<uid>, in the tenant slice
Databasesozt<uid>_<name>
Processesozpanel-tenants-t<uid>.slice
Files, SFTPAlways as the tenant, never root
POST /tenants {"name":"Example Ltd","plan_id":2,"contact_email":"admin@example.com","domains":["example.com"]}
FieldNotes
name, plan_idRequired; no plan = refused
contact_email, contact_nameContact; default first login
domainsInitial domains, reported per domain
login_email, login_password, no_loginFirst domain_admin login; password shown once (Argon2id)
region_policy, cluster_modeany; local · all · region · nodes

Lifecycle

CallEffect
PUT /tenants/{id}, /plan, /limits, /features, /clusteringPartial update, plan, overrides
POST /tenants/{id}/suspend · /unsuspendSites 503, mail refused, logins and DB logins locked, cron stopped. Backups continue. No data touched.
GET /tenants/{id}/teardown · DELETE /tenants/{id}Everything, every node, incl. snapshots. Irreversible.
POST /tenants/{id}/impersonateOperator acts as the tenant; banner, attributed, no chaining. POST /auth/stop-impersonating.

Billing hooks

Idempotent, keyed by domain: POST /provision/activate {domain,plan?,contact_email?,tenant?} · /suspend · /unsuspend · /cancel {delete} · GET /provision/status/{domain}. Domain provisioning: POST /domains/{d}/provision (DNS → web → mail). Templates: /templates.

Operator · Tenants. — ozpanel screenshot
Operator · Tenants.

Plans and limits

POST /plans {"name":"Starter","limits":{"domains":1,"mailboxes":5,"disk_mb":5120},
  "features":{"shell_access":true},"template_id":1,"visible":true}

Effective value = plan, overridden per tenant; source shown. Missing or 0 = unlimited. Plan edits apply at once. Plan in use cannot be deleted.

FeatureOn
shell_accessChrooted SFTP (never a shell)
container_appsContainer apps allowed
external_forwardingMail forwards outside the platform allowed
LimitUnitEnforced
domains, subdomains, mailboxes, mail_aliases, databases, db_users, cron_jobs, terminals, containerscountRefused (409)
disk_mbMBHard JuiceFS quota, rounded up to GiB; uploads refused
mail_quota_mbMBMailbox allocation refused
php_memory_mb, php_upload_mb, php_max_execution, php_workersMB, s, countPHP pool
processes, memory_mb, cpu_percentcount, MB, %Tenant slice (cgroup v2)
io_mb_hourMB/hMeasured
email_per_hour, email_per_daymessagesMail gateway, cluster-wide
bandwidth_gb_monthGBMeasured
container_memory_mbMBPer container
api_req_min/minNot enforced

Resource control

processes → TasksMax (fork refused) · memory_mb → MemoryHigh (throttle, default) or MemoryMax (hard) · cpu_percent → CPUQuota. Mode off · throttle · hard: PUT /resources/tenants/{id}. Coverage: GET /resources. Size plans for failover: all apps may land on one node.

Usage

ozpanel usage-collect every 15 min. Unmeasured ≠ 0. Over limit: reported, never auto-suspended. GET /stats/overview, /tenants/{id}/usage?days=30, /tenants/{id}/processes, /quota/verify.

Operator · Plans. — ozpanel screenshot
Operator · Plans.
Operator · Usage. — ozpanel screenshot
Operator · Usage.

Consoles and logins

RoleConsoleScope
super_admin/admin/Platform
domain_admin/account/One tenant
domain_userwebmail.<domain> / /ozpanel/One mailbox

Tenant screens: Overview · Websites · Domain · DNS · Apps · Mail · Mail domain · Mail routing · Mailbox · Files · Databases & tasks · Backups · Notifications · Plan · Security. No reseller role. Out of scope answers "not found".

Logins: GET/POST /tenants/{id}/users {email,role,password?,mailbox?} · PUT …/{uid} {role,mailbox,status,unlock} · …/password · …/invite.

Tenant · Overview. — ozpanel screenshot
Tenant · Overview.

Files, SFTP, cron, terminal

Routes for this area: API reference → route tables.

Symlink escapes refused. Operator adds ?tenant=<id>.

  • SFTPWith shell_access: group ozsftp, chroot /home, internal-sftp, no forwarding. POST /tenants/{id}/sftp/password. sftp ozt100001@panel.example.com.
  • CronPOST /tenants/{id}/cron {"schedule":"*/10 * * * *","command":"…"}; active on update. Runs on the primary only, in the slice, as the tenant.
  • Terminalttyd behind panel auth. Operator: host, container, tenant user. Tenant: own containers. 15 min default, 1 h max, 3 per user, 10 per node, plan terminals.
Tenant · Files. — ozpanel screenshot
Tenant · Files.
Tenant · Databases &amp; tasks. — ozpanel screenshot
Tenant · Databases &amp; tasks.

Shared storage

JuiceFS on Garage. Every tenant home and /mnt/cluster on every node.

PartDetail
DataGarage (S3, on the cluster's nodes), three copies on three nodes
MetadataPostgreSQL (synchronous); Redis selectable
QuotaHard per-tenant directory quota from disk_mb
CapacityReal, replication-aware: GET /juicefs/capacity
Fenceozpanel juicefs-mount-gate: mount only on a proven single metadata writer; seal after 3 bad passes
IntegrityPOST /juicefs/fsck
Metadata backupHourly dump (48, then daily ×14), daily restore drill
KeysPOST /juicefs/s3-key/rotate; Garage S3 keys /garage/s3-keys

Operator console: Garage/JuiceFS enable, fsck, metadata engine switch (POST /storage/juicefs/engine), Garage data dirs (/garage/data-dirs). Per tenant: GET /tenants/{id}/storage. Mount over a non-empty home: refused.

GlusterFS is retired cluster-wide. Switchable back by an administrator only: GET /storage/glusterfs/retirement, POST|DELETE /storage/glusterfs/retire, POST /storage/switch.

Backups

Snapshot per tenant: plain files, verifiable without ozpanel (tar, zstd, gzip, sha256).

<target>/<system_user>/<timestamp>/
  manifest.json        written last; parts, sizes, sha256, missing
  home.tar.zst         whole home
  db-<name>.sql.gz     one per MySQL / PostgreSQL database
  config.json          domains, DNS, mailboxes (hashes), aliases, spam, vhosts, PHP, cron
Target kindDestinationOff-site
localPath; default /var/backups/ozpanelno
sftpuser@host:/path, port, password; host key pinned; panel key pairyes
POST /backups/targets   {"name":"offsite","kind":"sftp","destination":"backup@backup.example.com:/srv/backups","port":22}
POST /backups/schedules {"target_id":1,"cron":"17 3 * * *","keep":7,"what":"files,databases,config"}
POST /backups/run       {"target_id":1,"files":true,"databases":true,"config":true}
POST /backups/snapshots/{id}/verify
POST /backups/snapshots/{id}/restore {"files":true,"databases":true,"config":true,"dry_run":true}
  • WhoTenant self-service (targets, schedules, snapshots, restore) and operator (?tenant=).
  • SchedulesSystem cron, ozpanel backup-run --schedule N, on the primary. keep 7; newest complete never removed. 6 h per run.
  • Verify firstRestore refuses a mismatched or partial snapshot. Dry run lists every line.
  • RestoreFiles, dumps, settings in order; passwords verbatim; paths remapped to a re-created account. Not included: panel logins, certificates.
  • Platform/mnt/cluster archived daily ×14 per node; POST /backups/platform/restore.
  • FleetGET /backups/status: last success, failures, tenants with none.
  • Not availableBackup encryption · S3 targets · PostgreSQL PITR.
Tenant · Backups. — ozpanel screenshot
Tenant · Backups.
Operator · Backups. — ozpanel screenshot
Operator · Backups.

Previous: Databases Next: Security