CorePanel CLI
The corepanel command-line interface is the administrative tool for operating a CorePanel
server from the shell. It talks directly to corepanel-core over a local Unix socket, so it
is ideal for initial provisioning, automation, and troubleshooting — no browser required.
Overview
Section titled “Overview”corepanel [command] [subcommand] [flags]| Command | Description |
|---|---|
corepanel system init | Interactive wizard to initialize a fresh server |
corepanel status | Show the status of all CorePanel services |
corepanel quota | Enable disk quotas, inspect state and usage, and set per-account limits |
corepanel account list | List hosting accounts and their account ids |
corepanel account create | Create a new hosting account and its primary domain |
corepanel account update | Change an account’s limits, owner details or hosting package |
corepanel account import | Import a cPanel account (or preview with --dry-run) |
corepanel account backup | Create a full local backup of a hosting account |
corepanel account restore | Restore a hosting account from a cpbackup archive |
corepanel account backup-schedule | Manage recurring backup schedules and retention |
corepanel domain | List, add and remove an account’s secondary (alias/addon) domains |
corepanel subdomain | List, add and remove an account’s subdomains |
corepanel app | Run and publish an account’s persistent applications |
corepanel cron | Manage an account’s cron jobs |
corepanel wp | Install, update, harden and roll back WordPress sites |
corepanel list admins | List administrator accounts |
corepanel mail sync | Resynchronize the Postfix lookup database |
corepanel mail list | List the mailboxes of a domain |
corepanel mail delete | Delete a mailbox and its Maildir |
corepanel mail quota | Set a mailbox storage quota |
corepanel mail suspend / activate | Suspend or reactivate a mailbox |
corepanel mail forwarder | Manage mail forwarders (aliases) |
corepanel mail catchall | Manage a domain catch-all |
corepanel mail redirect | Redirect all mail for a domain to another domain |
corepanel mail vacation | Manage a mailbox vacation autoresponder |
corepanel mail filter | Manage mailbox Sieve filter rules |
corepanel mail spam | Manage spam thresholds and allow/deny lists |
corepanel edition websites | Show how many websites the edition allows and how many are in use |
corepanel license | Activate, inspect, refresh or release this server’s license |
corepanel version | Print the CLI build version |
Run corepanel [command] --help at any time to see the available subcommands and flags.
corepanel system init
Section titled “corepanel system init”Interactive wizard that initializes a fresh CorePanel server. It detects the server identity, sets the panel domain (the FQDN used to access CorePanel), validates DNS, sets the administrator identity, and generates (or reuses) the administrator password.
corepanel system initThe wizard walks through six steps: detecting the public IP, prompting for the panel domain (validated as an FQDN), checking DNS resolution against the server IP (advisory, non-blocking), prompting for the admin email, preparing the admin password, and applying the configuration. The panel domain is a DNS name pointing at the server — not the machine’s OS hostname, which is only offered as the default.
Nameservers and company details are not asked here. The panel’s initial configuration wizard collects them on first login and keeps opening until they are set — see Initial Server Setup.
| Flag | Description |
|---|---|
--domain <fqdn> | Panel domain — the FQDN used to access CorePanel (e.g. panel.example.com) |
--admin-name <name> | Administrator display name (default Administrator) |
--admin-email <email> | Administrator email for notifications and Let’s Encrypt |
--admin-password <password> | Administrator password (generated if omitted) |
--no-interactive | Skip all prompts; use flags and defaults for scripted provisioning |
--hostname <fqdn> is retained as a deprecated alias for --domain.
Non-interactive provisioning
Section titled “Non-interactive provisioning”For automated installs, pass the values as flags and disable prompts:
corepanel system init \ --domain panel.example.com \ --admin-email admin@example.com \ --no-interactiveWhen --admin-password is omitted, a strong 16-character password is generated and stored at
/root/.corepanel_password (mode 0600). If that file already contains a password, it is
reused instead of generating a new one.
corepanel status
Section titled “corepanel status”Displays the status of all CorePanel services — corepanel-api, corepanel-auth,
corepanel-core, and corepanel-sys — in a table. For each service it shows the systemd
active state, the detailed sub-state (running, exited, failed, …), and the availability of its
sockets.
corepanel status+----------------+--------+---------+---------------------------------+| Service | Active | Status | Sockets |+----------------+--------+---------+---------------------------------+| corepanel-api | active | running | ● 127.0.0.1:16087 || corepanel-auth | active | running | ● corepanel-ftp-auth.sock || | | | ● corepanel-mail-auth.sock || | | | ● ftp-getcert.sock || corepanel-core | active | running | ● corepanel-core.sock || corepanel-sys | active | running | ● corepanel-sys.sock |+----------------+--------+---------+---------------------------------+Socket indicators: ● means the socket (or TCP port) is available, ○ means it is
unavailable. Use this command as a first check when the panel or a subsystem is not responding.
corepanel quota
Section titled “corepanel quota”Manages disk quotas: the server’s quota state, what each account is using, and the per-account limits.
Usage is measured by the kernel’s per-uid quota accounting, so these commands always report the state of that accounting alongside the figures. With accounting off there is nothing to measure, and a usage of zero would read as an empty account rather than as “not measured”. Limits are stored either way and take effect when quotas are enabled.
corepanel quota enable
Section titled “corepanel quota enable”Turns disk quotas on for this server: enables the kernel’s per-account accounting and enforcement, applies every account’s stored limit, and measures what each one is using.
Until this is run, CorePanel stores the quota an account was sold but nothing measures or enforces it. Quota accounting is a property of how the filesystem was mounted, which is why enabling it is a deliberate act and never something a package upgrade does on its own.
What the command can finish depends on the filesystem:
| Filesystem | What happens | Reboot |
|---|---|---|
| ext2/3/4 | the option is added to /etc/fstab and the filesystem is remounted | no |
XFS, separate /home | unmounted and mounted again, when nothing holds it open | only if busy |
XFS on / | configured on the kernel command line with grubby | yes |
The output distinguishes configured from active. On a root XFS — the usual server layout — nothing is measured or limited until the machine reboots, and the command says so instead of reporting success.
corepanel quota enable [--json]Disk quotas are CONFIGURED but NOT yet active — this server must be rebooted. disk quotas are configured on the kernel command line and start at the next reboot Configured in: the kernel command line (grubby) grubby --update-kernel=ALL --args=rootflags=uquota
Usage figures stay unavailable and no limit is enforced until this server reboots.corepanel quota status
Section titled “corepanel quota status”Shows the quota state of the filesystem backing the account home directories.
The three layers are reported separately because they fail separately:
- Accounting — is the kernel counting bytes per account at all.
- Enforcement — is it refusing writes past a limit.
- Reboot required — quotas are configured but the filesystem has not been mounted with them yet. On a root filesystem this is the normal state between enabling quotas and restarting the server.
corepanel quota status [--json]Disk quotas: ACTIVE (accounting + enforcement) Filesystem : /dev/sda4 on / (xfs) Separate /home : no Accounting : on Enforcement : on App data counted: yesApp data counted reports whether /var/opt/userapps shares the quota filesystem. When
/home is a separate partition it does not, and application data is therefore excluded
from every usage figure — the tidier-looking layout is the one that measures less.
corepanel quota list
Section titled “corepanel quota list”Lists per-account disk usage, largest first.
Disk and database sizes are shown in separate columns and are deliberately not added
together: only the disk figure is accounted by the kernel and enforced against, because the
MySQL data directory belongs to the mysql user and no per-uid quota can see it.
corepanel quota list [--search <domain>] [--json]When quotas are inactive the command prints a warning before the table: the figures are then whatever was last measured, not current usage.
corepanel quota inspect
Section titled “corepanel quota inspect”Shows where an account’s disk usage is, not just how much it is.
corepanel quota inspect <account> [--deep] [--json]A disk quota counts every file the account’s Linux user owns on the filesystem — not the contents of its home directory. Usually those are the same thing. When they are not, the account sees a figure it cannot explain and cannot reduce, because the files are somewhere it cannot even list.
Some of that is by design, and the report says so:
| Location | Why the account owns files there |
|---|---|
/opt/userapps/<user> | A published application’s program and releases — deliberately out of reach of the account’s own FTP, so it cannot replace the binary systemd is executing |
/var/opt/userapps/<user> | That application’s data, which survives every deploy |
/var/spool/cron/<user> | The account’s crontab |
/tmp, /var/tmp | Not by design: files left there still count against the quota |
The rest is the reason this command exists: files that ended up owned by an account by accident. The account cannot delete them, so a quota applied on top of them puts it permanently over its limit with nothing it can do.
Anything the scan cannot place is reported as unexplained. --deep sweeps the entire
filesystem to locate it and groups the results by directory. That takes minutes on a busy
server, which is why it is not the default.
Disk usage of example.com (user example, uid 1007)
Counted by the kernel : 1.2 GiB Inside the home : 840.0 MiB (/home/example) Outside the home : 384.0 MiB
+----------------------------+-----------+-------+-----------+---------------------------+| LOCATION | SIZE | ENTRIES | BY DESIGN | WHAT IT IS |+----------------------------+-----------+-------+-----------+---------------------------+| /home/example | 840.0 MiB | 9214 | yes | home directory || /opt/userapps/example | 320.0 MiB | 412 | yes | published application ... || /var/opt/userapps/example | 64.0 MiB | 88 | yes | application data ... |+----------------------------+-----------+-------+-----------+---------------------------+corepanel quota set
Section titled “corepanel quota set”Sets an account’s disk quota in MB. A quota of 0 removes the limit.
corepanel quota set <account> <MB># 20 GB for account 12, and no limit at all for dom-7corepanel quota set 12 20480corepanel quota set dom-7 0The kernel gets a hard limit slightly above the quota — 5 %, at least 50 MB, never more than double it. That margin is the grace window, and it is not generosity: at exactly 100 % the failures are indirect and ugly. PHP cannot write its session files, Dovecot cannot deliver and mail bounces, cron jobs fail half-way. The account goes over its stated quota, and is warned, before writes are refused.
If the account is already above the new quota, the command says so — its grace period starts immediately. On a server where quotas are not active the limit is still stored, and applies as soon as they are enabled.
corepanel quota apply
Section titled “corepanel quota apply”Pushes every account’s stored quota to the kernel and reports which accounts are already over theirs.
corepanel quota apply [--dry-run]This is what a server needs when its accounts predate disk quotas: the limits are in CorePanel’s database, but the kernel has never been told about them. It is also the repair path for a limit whose push failed when the account was created.
Run --dry-run first. Before enforcement is switched on, the number that matters is how many
accounts are already over their limit — those are exactly the ones whose writes will start
failing.
corepanel quota refresh
Section titled “corepanel quota refresh”Forces an immediate refresh of the stored usage instead of waiting for the hourly job.
corepanel quota refreshWhen quotas are inactive nothing is measured and the stored figures are left untouched. They are never zeroed — “cannot measure” must not be recorded as “uses nothing”.
corepanel account list
Section titled “corepanel account list”Lists hosting accounts and, crucially, their numeric account id — the value the
domain, subdomain, and cron commands take as their <account-id> argument. The id is
printed once when the account is created; this command is how you recover it afterwards, or
resolve a known domain to its id.
corepanel account list| Flag | Description |
|---|---|
--search <domain> | Filter accounts by primary domain (case-insensitive substring) |
--json | Output the account list as JSON for scripting |
The default table lists ID, Domain, User, Seller, Email (used/limit), DBs (used/limit),
Quota MB (used/limit), and Package; a limit of ∞ means unlimited. The command pages
through core transparently, so every matching account is returned in one call.
Examples
Section titled “Examples”# List every hosting accountcorepanel account list
# Resolve a domain to its account idcorepanel account list --search example.com
# Scripting: extract the account id for a domaincorepanel account list --search example.com --json | jq '.[0].accountId'corepanel account create
Section titled “corepanel account create”Creates a new hosting account together with its primary domain. The domain is passed as a positional argument; core provisions the Linux user, web vhost, DNS, and mail records.
corepanel account create example.comPassword handling
Section titled “Password handling”The account password can be supplied in three ways, listed from most to least secure:
--password-stdin— read the password from standard input. Best for automation, since the secret never appears in the shell history or the process list.- Interactive prompt — when no password flag is given and the session is attached to a terminal, the CLI prompts for the password with hidden input.
--password <value>— pass it directly. Convenient but visible in the shell history and inps.
The password must meet core’s strength requirements: at least 12 characters including lower-case, upper-case, a digit and a special character.
| Flag | Description |
|---|---|
--password <value> | Account password (prefer --password-stdin or the prompt) |
--password-stdin | Read the account password from standard input |
--owner-name <name> | Owner full name |
--owner-email <email> | Owner email address |
--description <text> | Free-form account description |
--quota <mb> | Disk quota in MB (0 = unlimited) |
--max-emails <n> | Maximum number of mailboxes (0 = unlimited) |
--max-databases <n> | Maximum number of MySQL databases (0 = unlimited) |
--max-ftp <n> | Maximum number of FTP accounts (0 = unlimited) |
--max-apps <n> | Applications the account may run. 0 = none, not unlimited, and there is no unlimited value — an account created without this flag cannot run applications |
--plan <name> | Hosting plan name to associate with the account |
--php <version> | PHP version to provision (e.g. 8.3). Defaults to the highest version installed on the server. |
# Interactive password prompt, default (highest) PHP versioncorepanel account create example.com
# Scripted: read the password from stdin, pin PHP 8.3 and a 5 GB quotaprintf '%s' "$ACCOUNT_PASSWORD" | \ corepanel account create example.com --password-stdin --php 8.3 --quota 5120On success the command prints the account ID, Linux username, domain, and home directory.
corepanel account update
Section titled “corepanel account update”Changes an existing account’s limits, owner details or hosting package
(core.UpdateAccount). The account id is passed as a positional argument — the numeric id
from corepanel account list; the dom-<id> form is accepted too.
corepanel account update <account-id> [flags]Only the flags you pass are changed. Every other limit is left exactly as it is. Since
0 means unlimited, omitting a flag is not the same as passing it with 0: a limit you
do not mention is untouched, while --max-emails 0 grants unlimited mailboxes.
| Flag | Description |
|---|---|
--owner-name <name> | Owner full name |
--owner-email <email> | Owner email address |
--description <text> | Free-form account description |
--quota <mb> | Disk quota in MB (0 = unlimited) |
--max-bandwidth <mb> | Monthly bandwidth allowance in MB (0 = unlimited) |
--max-emails <n> | Maximum number of mailboxes (0 = unlimited) |
--max-databases <n> | Maximum number of MySQL databases (0 = unlimited) |
--max-ftp <n> | Maximum number of FTP accounts (0 = unlimited) |
--max-aliases <n> | Maximum number of domain aliases (0 = unlimited) |
--max-subdomains <n> | Maximum number of subdomains (0 = unlimited) |
--max-addon <n> | Maximum number of addon domains (0 = unlimited) |
--max-apps <n> | Applications the account may run. 0 = none, not unlimited — the one limit that reads the other way round. Lowering it below the applications already running is refused |
--package <id> | Apply a hosting package; its limits become the account’s. Any limit flag given alongside it still wins. 0 detaches the account without changing a limit. |
--force | Allow a disk quota below current usage, leaving the account over quota |
# Upgrade: more mailboxes and more diskcorepanel account update 7 --max-emails 100 --quota 20480
# Move the account onto a package, then override one of its limitscorepanel account update 7 --package 3 --max-subdomains 50
# Plan downgrade below what the account currently storescorepanel account update 7 --quota 1024 --forceAn account created from a package stays linked to it after a manual edit and is reported as customized — see Editing an Account.
corepanel account import
Section titled “corepanel account import”Imports a cPanel account into CorePanel from a cpmove-<user>.tar.gz / backup archive —
or from an already-extracted account directory. With --dry-run the archive is parsed
server-side and the plan is printed (core.PreviewImport, read-only); without it, the
import runs (core.ImportAccount).
# Preview (read-only)corepanel account import <archive> --dry-run
# Run the importcorepanel account import <archive> [--package <id>] [--preserve-uid]| Flag | Description |
|---|---|
--dry-run | Parse and validate the archive, print the plan, and touch nothing |
--seller <id> | Target seller id (default: primary seller) |
--package <id> | Target hosting package id; its limits drive the account |
--preserve-uid | Preserve the source UID/GID via useradd -u/-g |
--on-conflict abort|skip | Policy when the linux user or primary domain already exists (default abort) |
--report <path> | Write the JSON import report to this path |
The <archive> path is resolved on the machine running corepanel-core (the archive must
already be staged on the server) and is sent as an absolute path.
Dry-run reports:
- the detected source panel and version;
- the account identity (Linux user, UID/GID, plan, contact);
- resource counts — domains, mailboxes, forwarders, databases, DB users, DNS records, FTP accounts, cron jobs, certificates;
- credential compatibility — which passwords carry across verbatim versus which need a
reset (cPanel
$6$/$5$/$1$, bcrypt and$y$yescrypt hashes are preserved); - conflicts against existing CorePanel accounts and domains;
- every parse warning (anything skipped or unsupported — nothing is dropped silently).
Real import creates the system account preserving the source username, UID/GID
(with --preserve-uid) and password hash where the scheme is compatible, then provisions
the CorePanel account, the primary domain (vhost + managed DNS zone), the FTP accounts and
the primary domain’s mailboxes with their credentials carried over verbatim. A credential
whose scheme cannot be preserved gets a random password and is flagged for a reset (never
surfaced in plaintext). It also restores the account’s heavy data: the home directory
(site files and Maildir messages), the MySQL databases (schema + data) and their users
(source password hashes preserved), the crontab (imported as panel-managed cron jobs), and
the primary domain’s TLS certificate (installed verbatim when not self-signed).
Addon/subdomain provisioning, source DNS-record import and mail forwarders remain later
phases and are listed as deferred in the report. The job outcome is persisted and can be
polled via core.GetImportJob or listed via core.ListImportJobs.
Examples:
# Preview a staged cpmove archive without importingcorepanel account import /var/lib/corepanel/import/cpmove-example.tar.gz --dry-run
# Preview an already-extracted account directorycorepanel account import /var/lib/corepanel/import/job-42/cpmove-example --dry-run
# Run the import into package 3, preserving the original UID/GIDcorepanel account import /var/lib/corepanel/import/cpmove-example.tar.gz \ --package 3 --preserve-uid --report /root/import-example.jsoncorepanel account backup
Section titled “corepanel account backup”Creates a full local backup of a hosting account. Every MySQL database is dumped with
mysqldump --single-transaction, each database user’s credential is preserved, and the
home directory (files + Maildir) is archived into a self-describing
cpbackup-<user>-<timestamp>.tar archive under /var/lib/corepanel/backups/<user>/.
Local scheduled backups are available in every edition; encrypted and remote (S3 / GCS / MinIO / SFTP) backups are a Pro/Business feature.
corepanel account backup <user> [--wait]The backup runs asynchronously server-side and prints a job id. Pass --wait to block
until it finishes and print the resulting archive path and status.
# Start a backup and return immediately with the job idcorepanel account backup pxdemo
# Start a backup and block until it completescorepanel account backup pxdemo --wait
# Show the status and report of a backup jobcorepanel account backup-status 7corepanel account restore
Section titled “corepanel account restore”Restores a hosting account from a cpbackup-<user>-<timestamp>.tar archive produced by
corepanel account backup. Recreates the system account (preserving UID/GID and the
password hash where compatible), the CorePanel account row and primary domain, every
secondary domain (aliases and addons) and subdomain — each with its vhost and managed
DNS zone — the home directory (files + Maildir), MySQL databases + users with preserved
credentials, FTP accounts, and every mailbox login (primary and addon domains) with its
password hash carried over verbatim.
corepanel account restore <archive> [--on-conflict abort|skip] [--dry-run] [--wait]Use --dry-run to preview what would be restored without touching the system, and
--wait to block until the restore finishes and print the per-resource report.
# Preview a restorecorepanel account restore /var/lib/corepanel/backups/cph2/cpbackup-cph2-20260721T090349Z.tar --dry-run --wait
# Restore, blocking until donecorepanel account restore /var/lib/corepanel/backups/cph2/cpbackup-cph2-20260721T090349Z.tar --waitcorepanel account backup-schedule
Section titled “corepanel account backup-schedule”Manages recurring backup schedules and their retention. A schedule targets one account
(--user) or every account (--all), fires on a cron expression or a
daily/weekly/monthly preset, and prunes only its own old archives. CorePanel runs an
in-process scheduler that polls every minute, so no OS crontab entry is created. Retention
never deletes a manual backup and always keeps at least the most recent scheduled archive
per account. run starts the run in the background and returns immediately; track progress
with the schedule’s last status or corepanel account backup-status.
corepanel account backup-schedule listcorepanel account backup-schedule create (--user <user> | --all) [--cron "m h dom mon dow" | --frequency daily|weekly|monthly] [--keep N] [--days N] [--name <label>] [--disabled]corepanel account backup-schedule enable <id>corepanel account backup-schedule disable <id>corepanel account backup-schedule run <id>corepanel account backup-schedule delete <id>create flags:
--user <linux-user>— Back up a single account (mutually exclusive with--all).--all— Back up every account on the server.--cron "m h dom mon dow"— Explicit 5-field cron expression (wins over--frequency).--frequency <daily|weekly|monthly>— Preset (daily 02:00, weekly Sun 02:00, monthly 1st 02:00).--keep N— Keep the N most recent scheduled archives per account (0= unlimited).--days N— Delete scheduled archives older than N days (0= disabled).--name <label>— Optional human label.--disabled— Create the schedule disabled.
# Nightly backup of one account, keep the last 7 archivescorepanel account backup-schedule create --user pxdemo --frequency daily --keep 7 --name "pxdemo nightly"
# Weekly server-wide backup with a custom cron, delete archives older than 30 dayscorepanel account backup-schedule create --all --cron "0 3 * * 0" --days 30
# List schedules, run one now, then disable itcorepanel account backup-schedule listcorepanel account backup-schedule run 3corepanel account backup-schedule disable 3corepanel list admins
Section titled “corepanel list admins”Lists administrator accounts and prints their key fields — ID, username, email, seller, and super-admin flag — in a table.
corepanel list admins| Flag | Description |
|---|---|
--seller <id> | Filter by a specific seller (default: 1) |
--all | Include administrators from every seller (overrides --seller) |
# All administrators across every sellercorepanel list admins --all
# Administrators for seller 42corepanel list admins --seller 42corepanel mail sync
Section titled “corepanel mail sync”Triggers a complete resynchronization of all mail domains, mailboxes, and aliases from
corepanel-core into corepanel-auth’s Postfix lookup database. Use it to recover from data
inconsistencies, initialize the Postfix database after installation, or force a refresh after
bulk imports or manual database changes.
corepanel mail syncAdd -v / --verbose to see a breakdown of how many domains, mailboxes, and aliases were
synchronized:
corepanel mail sync --verboseSynchronizing Postfix lookup database...Synced: - Domains: 12 - Mailboxes: 148 - Aliases: 37corepanel mail (mailbox management)
Section titled “corepanel mail (mailbox management)”Manage mailboxes for a domain. Each command persists the change in corepanel-core and
synchronizes it to the Postfix/Dovecot lookup database.
# List the mailboxes of a domain (address, status, quota)corepanel mail list example.com
# Delete a mailbox: removes the Maildir and revokes the credentialscorepanel mail delete intern@example.com
# Set a mailbox storage quota in MB (0 = unlimited)corepanel mail quota sales@example.com 1024
# Suspend a mailbox (blocks authentication and delivery), then reactivate itcorepanel mail suspend sales@example.comcorepanel mail activate sales@example.comcorepanel mail forwarder
Section titled “corepanel mail forwarder”Manage mail forwarders (aliases). A forwarder maps a source address to one or more destinations; a forwarder that shadows an existing mailbox or forms a mail loop with another alias is rejected.
# List forwarders for a domaincorepanel mail forwarder list example.com
# Create or replace a forwarder to one or more destinationscorepanel mail forwarder set sales@example.com a@example.com b@partner.tld
# Delete a forwardercorepanel mail forwarder delete sales@example.comcorepanel mail catchall
Section titled “corepanel mail catchall”Manage the domain catch-all: any address that does not match a mailbox or forwarder is routed to the catch-all destinations.
# Show the current catch-allcorepanel mail catchall get example.com
# Route all otherwise-unmatched mail to a destinationcorepanel mail catchall set example.com bucket@example.com
# Remove the catch-allcorepanel mail catchall delete example.comcorepanel mail redirect
Section titled “corepanel mail redirect”Redirect all mail for a domain to the same local part at another domain (Postfix
@source → @target). Useful when a domain is renamed or consolidated: sales@old.com
becomes sales@new.com. A domain has a single @domain route, so setting a redirect
replaces any catch-all configured on the domain.
# Show the current whole-domain redirectcorepanel mail redirect get old-example.com
# Redirect every address to the same local part at another domaincorepanel mail redirect set old-example.com example.com
# Remove the redirectcorepanel mail redirect delete old-example.comcorepanel mail vacation
Section titled “corepanel mail vacation”Manage a mailbox vacation autoresponder (an out-of-office reply). CorePanel renders the
reply as a Sieve script that Dovecot runs at delivery, so replies are sent server-side even
when no mail client is connected. Replies are rate-limited per sender by --days.
# Show the current autorespondercorepanel mail vacation get sales@example.com
# Enable an autoresponder (optionally bounded to a date window)corepanel mail vacation set sales@example.com \ --subject "Out of office" --message "Back on Monday." --days 2 \ --start 2026-07-20 --end 2026-07-27
# Save it without turning it on yetcorepanel mail vacation set sales@example.com --subject "OOO" --message "…" --inactive
# Remove the autorespondercorepanel mail vacation clear sales@example.comcorepanel mail filter
Section titled “corepanel mail filter”Manage mailbox Sieve filter rules. Each rule matches on message headers and applies actions; CorePanel compiles the rules to a Sieve script executed at delivery.
- fields:
from,to,cc,subject - operators:
contains,is,matches(the last supports*/?wildcards) - actions:
fileinto(folder),redirect(address),discard,keep,stop
# Print the current rules as JSONcorepanel mail filter get sales@example.com
# Replace the rules from a JSON arraycorepanel mail filter set sales@example.com --file rules.json
# Clear all rulescorepanel mail filter clear sales@example.comExample rules.json:
[ { "name": "Newsletters to a folder", "matchAll": true, "conditions": [{ "field": "subject", "op": "contains", "value": "newsletter" }], "actions": [{ "type": "fileinto", "arg": "Lists" }, { "type": "stop" }] }]corepanel mail spam
Section titled “corepanel mail spam”Manage spam thresholds and allow/deny sender lists at a scope, enforced by Rspamd.
- scope:
global(no target),domain <domain>, ormailbox <address> - thresholds:
reject,add_header,greylist(Rspamd action scores);0inherits the Rspamd default, and the effective values must satisfygreylist ≤ add_header ≤ reject - lists:
allow(never mark matching mail as spam) anddeny(always reject it); a pattern is a sender email address or a bare domain
More specific scopes and explicit allow/deny lists take precedence over broader ones.
# Show a mailbox's thresholds and listscorepanel mail spam show mailbox sales@example.com
# Set per-domain thresholds (0 = inherit default; all-zero clears the policy)corepanel mail spam threshold domain example.com --reject 12 --add-header 5
# Allow / deny senderscorepanel mail spam allow mailbox sales@example.com friend@partner.tldcorepanel mail spam deny global spammer.example
# Remove a list entry by its id (from `show`)corepanel mail spam remove domain example.com 7Spam training is automatic: moving a message to the Junk folder teaches the filter it is spam, and moving it back out teaches it as legitimate.
corepanel edition websites
Section titled “corepanel edition websites”Reports how many websites this server may host and how many are already in use.
corepanel edition websitescorepanel edition websites --jsonEdition: personalWebsites: 12 / 20Available: 8A website is a primary or an addon domain — the two kinds that own a document root and serve a site of their own. Domain aliases (parked domains) and subdomains are not counted: they republish a document root that is already counted, and stay unlimited in every edition. The figure is server-wide, across every account.
The allowance is enforced by the backend on every creation path — account create,
domain add --type addon, a cPanel import and a
backup restore — so hitting the limit refuses the operation before
anything is provisioned. The free Personal edition hosts up to 20 websites; Pro and
Business are unlimited (reported as "limit": 0, "unlimited": true).
The exit status is 1 once the allowance is exhausted, so a provisioning script can check for room before it starts:
# Bail out early when the server is fullcorepanel edition websites >/dev/null || { echo "no room left"; exit 1; }
# How many are left?corepanel edition websites --json | jq '.remaining'See the server-wide website limit for the full picture.
corepanel license
Section titled “corepanel license”Manages this server’s license — the credential that unlocks the Pro and Business
features. edition reports what the server is allowed to do; license is where that
allowance comes from.
corepanel license status [--json]corepanel license activate [key] [--key-stdin] [--json]corepanel license refresh [--json]corepanel license deactivate [--yes]Two rules explain the whole command group:
- The license binds to the public IPv4 the license server observes on the connection,
never to one this machine reports. There is nothing to configure and nothing to spoof;
a change of address is handled by
refresh, which rebinds and retries by itself. - Licensing never stops sites from being served. If the license server cannot be reached, the last verified license keeps working for 14 days (a warning starts at 7); only a verdict — expired or revoked — degrades the server immediately. Degrading means falling back to Personal features; nothing is taken offline.
corepanel license status
Section titled “corepanel license status”Reports the licensing state from the license cached locally, so it works offline and is safe to call from a monitoring script.
Edition: proKey: CPL-EQ0T6-…-2ES83Status: activeBound to: 203.0.113.24Valid to: 2027-08-01 00:00 UTCVerified: 2026-08-01 04:12 UTCThe report distinguishes the edition in effect from the edition the license pays
for; they differ exactly when something needs attention, and then the licensed edition
is printed as not in effect.
The exit status is 1 once the licensed edition is not in effect and 0 otherwise. A server with no license at all is not a degradation — Personal is a supported edition and the status stays 0.
Alert (alert in JSON) | Meaning |
|---|---|
| (empty) | Healthy, or unlicensed Personal |
validation_failing | Check-ins have been failing for over 7 days; the edition still holds until the grace deadline |
grace_expired | Check-ins never recovered; the server fell back to Personal |
expired | The license ran out (payment stopped) |
revoked | The license was revoked (refund or chargeback) |
unverifiable | The stored license no longer verifies against the trusted signing key |
The key is always shown masked (CPL-EQ0T6-…-2ES83): the panel never returns it in full.
corepanel license activate
Section titled “corepanel license activate”Binds a license key to this server and unlocks its edition. The key looks like
CPL-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX; it is case-insensitive and the dashes are optional.
A key already bound to another server is rebound to this one, spending one of the
license’s rebinds (3 per 30 days). Releasing the old server first with
corepanel license deactivate does not spend one.
The key is a bearer credential: passing it as an argument leaves it in the shell history
and in the process list, so --key-stdin is the right form on shared machines. With no
argument and no --key-stdin, an interactive run prompts for it and a scripted one
fails rather than hanging.
corepanel license activate --key-stdin < license.keycorepanel license refresh
Section titled “corepanel license refresh”Forces a check-in instead of waiting for the daily job. Use it after paying an invoice, after changing the server’s IP address, or to confirm connectivity to the license server. A failed check-in does not degrade the server — the grace window has to run out first.
corepanel license deactivate
Section titled “corepanel license deactivate”Releases the license so it can be activated on another server: the license server drops the binding and this server forgets the key. The server returns to Personal features immediately.
Because the key is forgotten locally, the command asks for confirmation — keep the key at
hand before running it. A non-interactive run must pass --yes; without it, it refuses
rather than proceeding silently.
# Monitoring: alert when the paid edition is not in effectcorepanel license status >/dev/null || echo "licensing needs attention"
# Moving to a new server: release here first so the rebind is freecorepanel license deactivate --yes
# Scripting: how long until the grace window closes?corepanel license status --json | jq -r '.graceExpiresAt // "n/a"'corepanel version
Section titled “corepanel version”Prints the CLI build version embedded at compile time.
corepanel versionCron jobs
Section titled “Cron jobs”Manage per-account cron jobs. CorePanel stores the jobs and renders the account’s
crontab (/var/spool/cron/<user>), which cronie executes as the account user — so
scheduled jobs keep running independently of the panel and behave exactly like cPanel.
A bare php in a command resolves to the account’s own PHP version, so standard
WHMCS/WordPress cron lines work unchanged. Accounts are identified by their numeric
account id — list accounts with corepanel account list to
find it.
corepanel cron list <account-id>corepanel cron add <account-id> --schedule <spec> --command <cmd> [flags]corepanel cron update <account-id> <job-id> --schedule <spec> --command <cmd> [flags]corepanel cron remove <account-id> <job-id>corepanel cron enable|disable <account-id> <job-id>corepanel cron run <account-id> <job-id>The --schedule value is a five-field cron spec ("*/5 * * * *") or one of the
macros @hourly, @daily, @midnight, @weekly, @monthly, @yearly,
@annually. It is validated and the next run computed before the job is stored.
@reboot and @every <duration> are not supported — use a five-field spec
(*/30 * * * * for “every 30 minutes”).
Flags for add / update:
| Flag | Description |
|---|---|
--schedule <spec> | Five-field cron spec or @macro (required) |
--command <cmd> | Command to run (required) |
--comment <text> | Human-readable label |
--mailto <email> | Email job output to this address (empty disables mail) |
--timezone <tz> | IANA timezone for the schedule (e.g. America/Santiago) |
--disabled | Create/leave the job disabled |
--single-instance | Skip a run if the previous one is still active (flock) |
--timeout <sec> | Kill the job after N seconds |
--cpu <sec> | CPU-time cap |
--mem-kb <kb> | PHP memory limit in KB (applied to PHP jobs via memory_limit) |
--nproc <n> | Max processes |
--nice <0-19> | Niceness (higher = lower priority) |
# WordPress and WHMCS cron, cPanel-compatible:corepanel cron add 12 --schedule "*/5 * * * *" --command "php /home/user/public_html/wp-cron.php"corepanel cron add 12 --schedule "@daily" --command "php ~/whmcs/crons/cron.php" --mailto ops@example.com --single-instance
corepanel cron list 12corepanel cron run 12 3 # run once, now, and show the outputcorepanel cron disable 12 3Jobs run through a wrapper that applies the resource limits, single-instance lock, and a timeout, and captures output. Disabling a job keeps it in the list but comments it out of the crontab.
The command environment
Section titled “The command environment”There is no allowlist: the command is any shell line the account could type itself. It
runs as the account user (never root), so it can only reach what that user can reach —
homes are 0700 and files 0600.
Every job is executed as /bin/sh -c '<your command>' with:
SHELL=/bin/shPATH=/opt/corepanel/bin:/usr/local/bin:/usr/bin:/binWhat that means in practice:
- It is
sh, notbash. No[[ ]], arrays or process substitution. If you need bash features, wrap them:bash -c '...'. phpworks on its own and resolves to that account’s PHP version, even though the host has no global PHP. The shim also points PHP’s temporary files, uploads and sessions at the account’s private~/tmp, and applies the job’s--mem-kbas PHP’s ownmemory_limit. Do not hardcode/usr/bin/php— it does not exist on a CorePanel host, and an absolute Remi path such as/opt/remi/php84/root/usr/bin/phppins the job to one PHP version and loses the temp-dir isolation and memory limit.wp(WP-CLI) is on the PATH and runs with the account’s PHP.- Pipes, redirections,
&&and variables are all fine — it is a normal shell line. - Base tools available to jobs include
curl,wget,git,tar,unzip,rsync,jq,findand the MariaDB client (mysqldump). %needs no escaping. CorePanel escapes it for cron, sodate +%Yreaches the shell verbatim.~expands to the account home, since cron setsHOME— but only at the start of a word. Inside a flag (--path=~/public_html) it stays literal; write"$HOME/..."there instead.
More valid commands
Section titled “More valid commands”# Application cron entry pointsphp ~/public_html/cron.phpphp ~/whmcs/crons/cron.phpphp ~/app/artisan schedule:run # Laravel scheduler (run every minute)
# WordPress via WP-CLI (after disabling wp-cron.php in wp-config.php)wp --path="$HOME/public_html" cron event run --due-nowwp --path="$HOME/public_html" plugin update --allwp --path="$HOME/public_html" db export ~/backups/wp-$(date +%F).sql
# Trigger something over HTTP instead of on diskcurl -fsS https://example.com/tasks/run > /dev/nullcurl -fsS -m 30 https://example.com/health || echo "health check failed"
# Database dump with rotation (credentials in ~/.my.cnf, mode 0600)mysqldump --defaults-extra-file="$HOME/.my.cnf" mydb | gzip > ~/backups/db-$(date +%F).sql.gzfind ~/backups -name 'db-*.sql.gz' -mtime +14 -delete
# Housekeepingfind ~/tmp -type f -mtime +7 -deletefind ~/logs -name '*.log' -size +100M -delete
# Your own scripts (must be executable, or invoke the interpreter explicitly)~/bin/nightly-report.sh >> ~/logs/report.log 2>&1sh ~/bin/sync-assets.shbash -c 'for d in ~/sites/*/; do echo "checking $d"; done'
# Archive a directorytar -czf ~/backups/uploads-$(date +%F).tar.gz -C ~/public_html uploadsPassing a password on the command line (mysqldump -p'secret') works but exposes it in
the process list to other processes on the host; a ~/.my.cnf with mode 0600 is the
safer form shown above.
What does not work
Section titled “What does not work”| Command | Why |
|---|---|
/usr/bin/php script.php | No global PHP on the host — exits 127. Use php. |
sudo ..., anything as root | Jobs run as the account user; cronie drops privileges first. |
| Multi-line commands | Newlines and control characters are rejected — a job is one line. |
--schedule "@reboot" | Not a supported macro (see above). |
| Reading another account’s files | Homes are 0700; jobs are confined to their own account. |
An account may define up to 100 jobs.
Domains
Section titled “Domains”Manage an account’s domains beyond the primary. Two types are supported:
- alias (parked): serves the account’s primary docroot under its own DNS zone (pointing at the server IP), and its mail is routed to the primary — it has no independent mailboxes. Aliases count against the account’s parked-domain limit.
- addon: a fully independent domain inside the same account — its own docroot at
~/domains/<domain>, its own DNS zone, and its own mail identity (DKIM + awebmail.<domain>vhost). Addon domains can have their own mailboxes and count against the account’s dedicated addon-domain limit.
Both limits use 0 = unlimited. Accounts are identified by their numeric account id
(list them with corepanel account list); domain names are
globally unique.
corepanel domain list <account-id>corepanel domain add <account-id> <domain> [--type alias|addon] [--web-mode <mode>] [--canonical <host>]corepanel domain delete <domain>corepanel domain canonical show <domain>corepanel domain canonical set <domain> <none|www|nonwww|primary>corepanel domain optimizations show <domain>corepanel domain optimizations set <domain> [--webp on|off|default] [--minify-js on|off|default] [--minify-css on|off|default] [--early-hints on|off|default]listprints every domain the account owns — primary and secondary — with its type, alias target, document root, web mode and canonical host (core.ListAccountDomains). The document root is printed because it is not derivable from the name: an addon owns~/domains/<domain>, an alias shares the primary’s~/public_html, and an account imported from another panel keeps the layout it arrived with.addvalidates the FQDN, global uniqueness, ownership and the matching per-type limit, provisions the vhost + DNS zone (and, for an addon, its own docroot, DKIM andwebmail.<domain>) viasys.AddDomain, and moves the usage counter (core.AddDomain).deleteremoves a secondary domain (core.DeleteDomain). For an addon this also tears down its mailboxes, DNS zone, DKIM and docroot. The account’s primary domain cannot be removed this way — it is only removed by deleting the account.canonical showprints which hostname of the domain serves the site and what the others do (core.GetDomainCanonicalHost).canonical setpicks the real hostname (core.SetDomainCanonicalHost) and re-emits the site config. Every domain answers under both<domain>andwww.<domain>, and an alias adds two more hostnames serving a copy of the primary’s site — identical content on several hostnames is duplicate content, which splits link equity and leaves search engines guessing which URL is authoritative. The hostnames that are not canonical answer a 301 that preserves the path and query string, so deep links and indexed URLs keep working.optimizations showprints a domain’s web optimization overrides (core.GetDomainOptimizations).optimizations setsets them (core.SetDomainOptimizations) and re-emits the site config. WebP-on-the-fly and Early Hints (HTTP 103) are baseline in every edition and on by default — WebP transparently serves WebP to browsers that accept it (safe fallback to the original image); Early Hints learns a page’s preload sub-resources and replays them as a 103 on later requests (HTTP/2 and HTTP/3 only, inert for static/SPA sites). CSS/JS minification is Speed Optimizer (Pro/Business) and off by default — it can break sites (source maps, already-minified assets, execution order), so it is opt-in. Use these commands to override each per domain.
Flags for add:
| Flag | Description |
|---|---|
--type <type> | Domain type: alias (parked, default) or addon (independent) |
--web-mode <mode> | Web serving mode plain|cms|spa (empty ⇒ plain) |
--canonical <host> | Canonical hostname none|www|nonwww|primary (empty ⇒ www) |
Values for canonical set (and --canonical):
| Value | Effect |
|---|---|
none | Every hostname serves the site directly, no redirect. Domains created before this feature keep this after an upgrade — nothing changes for a live site until you choose a canonical host. |
www | www.<domain> serves; <domain> answers 301 to it. Default for new domains. |
nonwww | <domain> serves; www.<domain> answers 301 to it. |
primary | Alias (parked) domains only. Every hostname of the alias answers 301 to the primary’s canonical hostname, in a single hop, instead of serving a copy of its site. Changing the primary’s canonical host re-points every alias set to primary automatically. |
Flags for optimizations set:
| Flag | Description |
|---|---|
--webp <state> | WebP-on-the-fly: on (force), off (disable), or default (follow the server default, on). Omitting the flag resets to default. |
--minify-js <state> | CSS/JS minify — JavaScript (Speed Optimizer, Pro/Business): on (force), off (disable), or default (follow the server default, off). Omitting the flag resets to default. |
--minify-css <state> | CSS/JS minify — CSS (Speed Optimizer, Pro/Business): on (force), off (disable), or default (follow the server default, off). Omitting the flag resets to default. |
--early-hints <state> | Early Hints (HTTP 103): on (force), off (opt this domain out), or default (follow the server default, on). Omitting the flag resets to default. |
corepanel domain list 12corepanel domain add 12 parked.example.comcorepanel domain add 12 shop.example.net --type addon --web-mode cmscorepanel domain add 12 parked.example.com --canonical primarycorepanel domain delete parked.example.comcorepanel domain canonical show example.comcorepanel domain canonical set example.com wwwcorepanel domain canonical set example.com nonwwwcorepanel domain canonical set parked.example.com primarycorepanel domain optimizations show shop.example.netcorepanel domain optimizations set shop.example.net --webp offcorepanel domain optimizations set shop.example.net --webp defaultcorepanel domain optimizations set shop.example.net --minify-js on --minify-css oncorepanel domain optimizations set shop.example.net --early-hints offReverse proxy routes
Section titled “Reverse proxy routes”Publish a local service under one of the server’s domains. A route forwards the requests
matching a path to a host:port upstream; every unmatched path keeps being served from the
docroot. Routes are evaluated first match wins, and CorePanel keeps them ordered
most-specific-first so a catch-all never hides a more specific route. The full guide —
what the feature is for, when to use it and how the panel presents it — is in
Reverse Proxy Routes.
These commands are administrator-only, and that is a security boundary rather than a UI
choice: the caller picks the upstream, so pointed at an internal service the route would
publish it on the public internet under the domain’s name. Targets on the local loopback are
filtered accordingly — the web server’s own ports (80/443) are refused outright because they
would loop, and well-known service ports (corepanel-api, MySQL, PostgreSQL, Redis, Memcached,
SSH, SMTP) require --allow-reserved-port.
corepanel domain proxy list <domain> [--check]corepanel domain proxy add <domain> <path> <upstream> [--strip-prefix] [--tls] [--allow-reserved-port]corepanel domain proxy remove <domain> <path>listprints the domain’s whole route table in evaluation order (core.GetDomainRoutes). The MANAGED column marks which entries these commands own:yesfor a proxy route you added,autofor the docroot fallback CorePanel maintains,nofor a hand-written route it leaves untouched. With--checkevery proxy target is dialed (core.CheckProxyUpstreams) and a STATUS column reportsup (Nms)orDOWN: <reason>— the fastest way to tell “my service died” from “my site is broken”, which from the outside are the same 502.addadds or replaces a route (core.SetDomainProxyRoute).<path>is the matcher — a prefix (/api/*), an exact path (/health) or a catch-all (/*) — and it is the route’s identity: re-runningaddwith the same path edits that route in place instead of adding a second, unreachable one.<upstream>ishost:port, never a URL — or the unix socket of an application CorePanel published, written asunix:/run/corepanel/apps/<account>/<app>.sock. Only that exact layout is accepted, so no other socket on the server can be named; CorePanel writes those routes itself. The upstream is probed right after the route is applied, and a warning is printed if nothing is listening (the route is applied either way).removedrops the route matching<path>(core.DeleteDomainProxyRoute). Removing the last one returns the site to serving its docroot directly; removing a path that has no route is not an error.
Flags for add:
| Flag | Description |
|---|---|
--strip-prefix | Remove the matcher prefix from the proxied request, so /api/* reaches the upstream as /*. Ignored for a catch-all matcher. |
--tls | Dial the upstream over HTTPS instead of plain HTTP. |
--allow-reserved-port | Confirm a target on a well-known local service port. |
corepanel domain proxy list app.example.comcorepanel domain proxy list app.example.com --checkcorepanel domain proxy add app.example.com '/api/*' 127.0.0.1:8080 --strip-prefixcorepanel domain proxy add app.example.com '/*' 127.0.0.1:8080corepanel domain proxy remove app.example.com '/api/*'Subdomains
Section titled “Subdomains”Manage an account’s subdomains (e.g. blog.example.com). A subdomain gets its own document
root (~/domains/<fqdn>, beside the primary’s public_html rather than inside it) and web
vhost, plus a single A record inside its parent domain’s existing DNS zone — it does not create a new zone and has no mail of
its own. TLS is still issued for the subdomain FQDN. The parent must be an existing
primary or addon domain owned by the account. Subdomains count against the account’s
subdomain limit (0 = unlimited). Accounts are identified by their numeric account id
— find it with corepanel account list.
corepanel subdomain list <account-id>corepanel subdomain add <account-id> <label> <parent-domain> [--web-mode <mode>]corepanel subdomain delete <fqdn>listprints every subdomain the account owns, with its parent, web mode, and document root (core.ListSubdomains).addvalidates the label, parent ownership, global uniqueness (across both domains and subdomains) and the subdomain limit, provisions the docroot + vhost + parent-zone A record viasys.AddSubdomain, and moves the usage counter (core.AddSubdomain).<label>is the left-most part (e.g.blog); the labelswwwandwebmailare reserved.deleteremoves a subdomain by its fully-qualified name (core.DeleteSubdomain): the vhost, the parent-zone A record, the certificate and the document root.
Flags for add:
| Flag | Description |
|---|---|
--web-mode <mode> | Web serving mode plain|cms|spa (empty ⇒ plain) |
corepanel subdomain list 12corepanel subdomain add 12 blog example.comcorepanel subdomain add 12 shop example.com --web-mode cmscorepanel subdomain delete blog.example.comWordPress Manager
Section titled “WordPress Manager”Manage WordPress installations end to end: install, adopt, update, harden, verify and
roll back sites. Every operation runs WP-CLI as the account user (never as root)
with the account’s own PHP version. Accounts are identified by their numeric account
id; instances by the instance id shown in corepanel wp list.
corepanel wp list <account-id>corepanel wp scan <account-id>corepanel wp install <account-id> --domain <fqdn> --title <t> --admin-user <u> --admin-email <e> [flags]corepanel wp attach <account-id> <docroot-path>corepanel wp detach <account-id> <instance-id>corepanel wp login <account-id> <instance-id> [--user <login>]corepanel wp components <account-id> <instance-id>corepanel wp update <account-id> <instance-id> [--core] [--all-plugins] [--all-themes] [--plugins a,b] [--themes x,y]corepanel wp plugin|theme <action> <account-id> <instance-id> <slug>corepanel wp policy <account-id> <instance-id> <none|minor|all>corepanel wp hardening <account-id> <instance-id> [--set key=on|off ...]corepanel wp integrity <account-id> <instance-id> [--reinstall]corepanel wp maintenance|debug|indexing <account-id> <instance-id> [on|off]corepanel wp set-url <account-id> <instance-id> <url>corepanel wp restore-point create|list|rollback|deleteHighlights:
- 1-click install —
wp installprovisions the database and DB user, downloads core as the account, writes a hardenedwp-config.php(fresh salts, system-cron takeover, direct filesystem access), creates the native*/5cron job that replaces WordPress’s visitor-paid pseudo-cron, and sets the domain’s web mode tocms. When--admin-passwordis omitted a strong password is generated and shown once. - Adopt existing sites —
wp scanfinds installations anywhere under the account home (including sites migrated with the cPanel importer) and manages them without touching their behavior.wp detachleaves a.corepanel-wp-ignoremarker so the site stays unmanaged. - One-time login links —
wp loginprints a single-use admin URL that expires in 60 seconds. The token is minted inside WordPress and only its hash is stored; no passwords are involved. - Safe updates —
wp update --coreautomatically snapshots the site (files + database) first;wp restore-point rollbackbrings it back. - Reversible hardening — file-editor lockdown, XML-RPC blocking, version hiding,
user-enumeration blocking, and application-password disabling, each individually
switchable (
on= protection enabled). - Integrity —
wp integrityverifies core files against wordpress.org checksums;--reinstallreplaces core files in place (keepingwp-contentandwp-config.php), the standard malware-cleanup step. - Maintenance mode — a server-level 503 page served by the web server itself, so it keeps working even when WordPress is broken mid-upgrade.
Action verbs for wp plugin / wp theme: install (--version, --activate),
activate, deactivate (plugins only), delete, autoupdate-on, autoupdate-off.
Hardening keys for --set: file-editor, xmlrpc, version-hiding,
user-enumeration, app-passwords.
# Install and open the dashboardcorepanel wp install 12 --domain example.com --title "My Blog" --admin-user admin --admin-email me@example.comcorepanel wp login 12 3
# Keep it updated, safelycorepanel wp components 12 3corepanel wp update 12 3 --core --all-pluginscorepanel wp restore-point list 12 3
# Harden and verifycorepanel wp hardening 12 3 --set xmlrpc=on --set user-enumeration=oncorepanel wp integrity 12 3
# Upgrade windowcorepanel wp maintenance 12 3 oncorepanel wp update 12 3 --all-themescorepanel wp maintenance 12 3 offApplications
Section titled “Applications”Run a long-lived program for a hosting account and publish it under one of the account’s
domains — a Go API behind /api, a service on its own subdomain, anything that listens
for HTTP. Accounts are identified by their numeric account id; an application by that
id plus its slug.
Each application gets a systemd unit of its own (corepanel-app-<user>.<app>.service)
running as the account user in a private network namespace, with memory, CPU and task
limits applied as cgroup properties. It never listens on a TCP port the rest of the
server can reach: CorePanel binds a unix socket for it
(/run/corepanel/apps/<user>/<app>.sock) and the web server proxies to that socket, so
two applications can never collide on a port.
The code is shipped as a .tar.gz the account uploads (over FTP, to
~/apps/<name>.tar.gz by default). CorePanel extracts it as a new release, starts it and
checks it answers before publishing it; a release that does not answer is rolled back
automatically and the previous one keeps serving. Three releases are kept.
corepanel app list [<account-id>]corepanel app show <account-id> <name>corepanel app create <account-id> <name> --domain <fqdn> --port <n> [flags] -- <program> [args...]corepanel app update <account-id> <name> [flags] [-- <program> [args...]]corepanel app deploy <account-id> <name> [--artifact <path>]corepanel app rollback <account-id> <name> [--to <release>]corepanel app start|stop|restart <account-id> <name>corepanel app releases <account-id> <name>corepanel app logs <account-id> <name> [-n 200]corepanel app env list|set|unset <account-id> <name> [KEY=VALUE|KEY] [--secret]corepanel app webhook show|rotate|revoke <account-id> <name>corepanel app delete <account-id> <name> [--purge-data]Applications belong to a hosting account, identified by its numeric account id — list
accounts with corepanel account list to find it. Every command
below takes it as <account-id>, followed by the application’s name.
Creating an application
Section titled “Creating an application”The start command goes after a -- separator, as a program relative to the release
root followed by its arguments:
corepanel app create <account-id> api --domain example.com --path /api --port 8080 \ --health /healthz --mem 512 --cpu 100 -- server --addr :8080create reserves the name and derives the unit and the socket from it, but it does
not publish the application yet — the route is added by the first deploy that
answers. Pointing the site at a socket nothing is listening on would 502 the path, and
for an application mounted at / that is the whole site.
| Flag | Description |
|---|---|
--domain <fqdn> | Domain or subdomain of the account to publish under (required). A domain alias is refused: it has no site file of its own |
--path </sub> | Path under the site (default /); a sub-path mount gives the application its own root |
--port <n> | Port the application listens on inside its network namespace, 1024-65535 (required) |
--health </healthz> | Path requested after a deploy to decide whether the release is serving; without it the check is a plain connection to the socket |
--link <dir> | Release-relative directory symlinked into the data volume, for applications that write inside their own tree; repeatable |
--mem <mb> | Memory ceiling in MB (default 256) |
--cpu <pct> | CPU quota as a percentage; 100 is one whole core (default 50) |
--tasks <n> | Maximum number of tasks (default 64) |
--network outbound|none | Outbound access, outbound by default. none leaves the application with loopback only — no internet, no DNS — and saves the network backend’s ~25 MB. Neither value exposes services on the server’s own 127.0.0.1, and neither publishes the application’s port |
--runtime go | Application runtime; only go is supported in this release |
update takes the same flags and applies them to the release already running. Omitted
fields are left as they are, the previous settings are restored if the application stops
serving under the new ones, and an application that was stopped stays stopped.
--clear-links removes every linked directory.
Deploying and rolling back
Section titled “Deploying and rolling back”# Deploy the conventional ~/apps/api.tar.gzcorepanel app deploy <account-id> api
# Or an artifact uploaded somewhere else in the account homecorepanel app deploy <account-id> api --artifact uploads/api-2026-07-27.tar.gz
# Go backcorepanel app releases <account-id> apicorepanel app rollback <account-id> api --to 20260727T101500ZThe artifact must live inside the account’s home — it is extracted as root, so a path
outside the home is refused. A release that does not answer is rolled back to the
previous one, and deploy exits non-zero printing the reason from the application’s
journal.
Looking at what the machine says
Section titled “Looking at what the machine says”corepanel app show <account-id> apicorepanel app logs <account-id> api -n 500show prints the specification alongside the live systemd status — active state, main
pid, last exit, restarts and whether the socket exists. What CorePanel recorded and what
systemd answers are different questions: an application the panel believes is running can
be crash-looping, which is exactly the case worth diagnosing.
logs is a snapshot of the journal, which holds both the application’s own output and
the gateway’s. To follow it live, use journalctl -fu corepanel-app-<user>.<app> on the
server.
Environment and secrets
Section titled “Environment and secrets”corepanel app env set <account-id> api LOG_LEVEL=debugcorepanel app env set <account-id> api API_TOKEN=s3cr3t --secretcorepanel app env list <account-id> apicorepanel app env unset <account-id> api LOG_LEVELThe variables live in a file systemd reads as root before dropping privileges, so the
application receives them while the account cannot read the file that holds them. A
--secret value is never displayed back — it can still be replaced, only not read.
Setting or removing a variable restarts a running application.
Deploying from CI/CD
Section titled “Deploying from CI/CD”A build pipeline can deploy without a person in the loop:
corepanel app webhook rotate <account-id> api # issues the token and prints both URLscorepanel app webhook show <account-id> api # prints them againcorepanel app webhook revoke <account-id> api # disables itThe token works two ways. The pipeline can POST the artifact itself, deploying it in the
same call and needing no FTP account:
curl -fsS -X POST https://panel.example.com/api/apps/upload \ -H 'Authorization: Bearer cpd_...' --data-binary @api.tar.gzOr it can upload to ~/apps/<name>.tar.gz as the account and POST to the deploy URL,
which is what a GitHub or GitLab webhook — a caller that can be given nothing but an
address — has to do.
Either way, what runs is the same deploy as corepanel app deploy, automatic rollback
included. Two properties decide how the pipeline is written: the call answers 202 as
soon as the deploy starts, not when it finishes, and a second call while one is running
is refused with 409.
Full guide, with GitHub Actions and GitLab CI examples, signatures and every status code: Deploying from CI/CD.
Removing an application
Section titled “Removing an application”corepanel app stop <account-id> apicorepanel app delete <account-id> apidelete unpublishes the application, stops it, and removes its unit, program tree and
socket. Its data directory is kept unless --purge-data is given: it is the one thing
a redeploy cannot recreate. Deleting a domain that still publishes an application is
refused with a message naming them; corepanel domain delete --force removes them in
cascade.
Configuration
Section titled “Configuration”The CLI connects to corepanel-core through a Unix socket. By default it uses:
/run/corepanel-core/corepanel-core.sockOverride the socket path with the COREPANEL_CORE_SOCKET environment variable — useful in
non-standard deployments or when testing against a different Core instance:
COREPANEL_CORE_SOCKET=/run/corepanel-core/corepanel-core.sock corepanel status