Command quick reference
Everything an admin runs from a root shell on the server, on one
page. All panelctl commands need root (sudo),
take --json before the command for machine-readable output,
and exit 1 with a one-line message on error. Generated passwords are
printed once.
Install and upgrade
tar xzf minipanel-<version>.tar.gz && cd minipanel-<version>
sudo ./install/install.sh --hostname panel.example.com --email admin@example.com # first install
sudo ./install/install.sh --yes # upgrade / re-apply, keeps settings
sudo ./install/install.sh --hostname new.example.com --public-ipv4 203.0.113.10 # change hostname / public address
| Installer flag | Meaning |
|---|---|
--hostname FQDN |
server hostname (panel, mail and SFTP name) |
--email ADDRESS |
Let's Encrypt contact |
--php 8.3[,8.4] |
PHP versions to install |
--public-ipv4 IP /
--public-ipv6 IP|none |
address clients' DNS should point at (behind NAT: the forwarded one) |
--skip-dns-check |
continue if the hostname does not resolve here yet |
--yes |
no prompts |
--force |
continue despite existing services / busy ports / sshd Allow-Deny rules |
--skip-resource-check |
allow < 2 GB RAM / 20 GB disk (test boxes) |
panelctl version # installed version
panelctl ping # daemon alive, uptime, schema version
panelctl doctor # services, configs, disk, DB, DNS, certs, apply state, backups, updates
panelctl update check # ask the signed release feed ([update] url in config.toml)
panelctl update apply # download, verify, install the newer release (add --force to reinstall)
panelctl update status # progress and installer log of the last update
Accounts
panelctl account create NAME --domain DOMAIN --email ADDRESS [--php 8.4] [--ssh off|sftp|shell] [--password-stdin]
panelctl account list
panelctl account show NAME
panelctl account suspend NAME
panelctl account unsuspend NAME
panelctl account passwd NAME [--password-stdin] # new panel/SFTP/SSH password
panelctl account modify NAME --email E --php 8.3 # contact email, default PHP for new domains
panelctl account delete NAME --confirm NAME # archives first (kept 30 days), then removes everything
panelctl account delete NAME --confirm NAME --no-backup # no archive: irreversible
panelctl account restore minipanel-deleted-NAME-<time>.tar.gz --confirm # bring a terminated account back
panelctl ip list # IP pool, usage, spare addresses on the server
panelctl ip add 203.0.113.10 --label shop # an address already in netplan
panelctl ip add 10.0.0.12 --public 198.51.100.12 # behind NAT: local + public (DNS) address
panelctl ip assign 203.0.113.10 a.com b.com c.com # move domains to an address
panelctl ip assign shared a.com # back to the shared address
panelctl ip remove 203.0.113.10 --force # its domains go back to the shared address
panelctl account limits NAME --cpu 100 --memory 1G --procs 100 # resource limits (one core, 1 GB, 100 processes)
panelctl account limits NAME --cooldown on # pause automatically when it stays at a limit
panelctl account limits NAME --restore # resume a paused account now
panelctl limits status # enforced controllers, paused accounts
panelctl limits usage # live CPU/memory/process/IO per account
printf '%s' 'the password' | panelctl account create NAME --domain D --email E --password-stdin
Account names: 3–16 characters, a-z0-9, starting with a
letter; they become Unix users (uid 2000+). Clients log in at
https://<hostname>:8443/ with the account name or
primary domain.
Disk quotas
panelctl quota status
panelctl quota enable # once; edits /etc/fstab (ext4: live, XFS: after a reboot)
panelctl account quota NAME 10G # 500M, 1T, unlimited
sudo repquota -u / # raw usage/limits per user
Domains, certificates, PHP
panelctl domain list [--account NAME]
panelctl ssl status # every certificate: state, names, expiry, next attempt
panelctl ssl retry DOMAIN # request now, ignoring backoff (after DNS is fixed)
panelctl ssl deployed CERT-NAME # what certbot's renewal hook calls; reloads services
panelctl ssl tick # what the hourly timer runs
panelctl php list
panelctl php install 8.4 # from the PPA; clients can then pick it
panelctl php set-default 8.4 # for new accounts
DNS
panelctl dns status
panelctl dns setup powerdns --ns ns1.example.net --ns ns2.example.net # install PowerDNS here
echo "$TOKEN" | panelctl dns setup cloudflare --token-stdin [--account-id ID]
echo "$TOKEN" | panelctl dns setup cpanel --url https://dns1.example.net:2087 --token-stdin
panelctl dns zones # every zone: status, last error
panelctl dns zone example.com # its records
panelctl dns sync [example.com] # publish now (the timer does it every 5 min)
dig +short @127.0.0.1 example.com A # PowerDNS answering locally
Suspended and pause pages
panelctl page show suspended # what suspended accounts' visitors see
panelctl page set suspended --file my.html
panelctl page set limited --file my.html # default for sites paused by the limiter
panelctl page show example.com # that domain's own pause page (client-editable)
panelctl page reset example.com
Settings and notifications
panelctl config show
panelctl config set public_ipv4 203.0.113.10 # behind NAT: the forwarded address
panelctl config set public_ipv6 none
panelctl config set admin_email admin@example.com
panelctl config set notify_email ops@example.com # empty = admin_email
panelctl config set notify off # or on
panelctl config set fail2ban_ignore 10.0.1.0/24 203.0.113.7 # never banned; '' clears
panelctl config set update_url https://updates.example.com/minipanel/
panelctl notify --test # mail the current health state now
Each set validates, saves config.toml and
restarts minipaneld. The hostname is changed with
install.sh --hostname NEW --yes (it is wired into Postfix,
Dovecot and certificates).
Configuration and repair
panelctl rebuild # re-render every managed file from the state DB, reload services
panelctl rebuild --account NAME # one account (after fixing an apply error)
panelctl usage refresh # re-measure disk usage (daily timer does this)
sudo nano /etc/minipanel/config.toml && sudo systemctl restart minipaneld # settings: hostname, IPs, limits, backup retention
Managed files (never edit by hand — rebuild overwrites
them): /etc/apache2/minipanel/,
/etc/php/*/fpm/pool.d/minipanel-*.conf,
/etc/postfix/minipanel/,
/etc/dovecot/minipanel-*,
/etc/ssh/sshd_config.d/50-minipanel.conf,
/var/spool/cron/crontabs/<account>.
Backups
panelctl backup run # full archive to /var/backups/minipanel (nightly 03:00)
panelctl backup run --account NAME # one account
panelctl backup list
panelctl backup restore FILE --account NAME --confirm # one account's home + databases, into the existing account
panelctl backup restore FILE --confirm # whole server (fresh install + archive); restarts minipaneld, rebuilds
rsync -a /var/backups/minipanel/ backup-host:minipanel/ # offsite copy: your job
Admin web UI (port 2087)
panelctl admin create NAME [--password-stdin] # login for https://<hostname>:2087/; first login sets up 2FA
panelctl admin list
panelctl admin passwd NAME [--password-stdin] # new password, clears a lockout
panelctl admin totp-reset NAME # lost authenticator: next login sets it up again
panelctl admin delete NAME
sudo ufw delete allow 2087/tcp && sudo ufw allow from 203.0.113.0/24 to any port 2087 proto tcp # restrict to admin addresses
Audit and logs
panelctl audit # newest 100 entries
panelctl audit --account NAME --since 7d --limit 500
panelctl --json audit | jq '.[] | select(.result != "ok")'
sudo journalctl -u minipaneld -u minipanel-web -u minipanel-admin --since today
sudo journalctl -u minipanel-web | grep 'login failed' # what fail2ban sees
sudo tail -f /var/log/apache2/minipanel/<account>/<domain>-error.log
sudo tail -f /var/log/mail.log /var/log/rspamd/rspamd.log
Services
systemctl status minipaneld minipanel-web minipanel-admin
systemctl list-timers 'minipanel-*' # ssl (hourly), usage (daily), backup (03:00)
sudo systemctl restart minipaneld # after editing config.toml
sudo systemctl reload apache2 php8.3-fpm postfix dovecot
sudo apachectl configtest; sudo postfix check; sudo doveconf -n >/dev/null; sudo sshd -t; sudo php-fpm8.3 -t
| Unit | Runs as | Listens |
|---|---|---|
minipaneld |
root | /run/minipanel/helper.sock |
minipanel-web |
minipanel |
127.0.0.1:8081 → Apache 8443 |
minipanel-admin |
minipanel-admin |
127.0.0.1:8082 → Apache 2087 |
apache2, php8.x-fpm, mariadb,
postfix, dovecot, rspamd,
redis-server, fail2ban, cron,
ssh |
system | 80/443, sockets, 25/465/587/993/995, 22 |
Firewall and fail2ban
sudo ufw status numbered
sudo fail2ban-client status # jails: sshd, postfix-sasl, dovecot, minipanel-panel
sudo fail2ban-client status minipanel-panel
sudo fail2ban-client banned
sudo fail2ban-client unban 203.0.113.7
sudo fail2ban-client set sshd addignoreip 10.0.1.0/24 # until restart; make permanent in jail.local
Open ports: 22, 25, 80, 443, 465, 587, 993, 995, 8443 (client panel), 2087 (admin UI). Behind NAT, forward the same list.
Mail diagnostics
sudo doveadm auth test user@example.com 'password' # mailbox credentials
sudo doveadm quota get -u user@example.com
sudo postmap -q example.com hash:/etc/postfix/minipanel/virtual_domains
sudo postmap -q user@example.com hash:/etc/postfix/minipanel/virtual_mailboxes
sudo postqueue -p; sudo postqueue -f # queue, flush
sudo rspamadm configtest
openssl s_client -connect panel.example.com:993 -quiet </dev/null # IMAPS banner + certificate chain
ls -l /var/lib/rspamd/dkim/ # per-domain DKIM keys
Webmail and phpMyAdmin
https://<hostname>/webmail/ and https://<hostname>/phpmyadmin/
sudo tail -f /var/lib/minipanel/apps/roundcube/logs/errors.log # webmail failures (fail2ban: roundcube-auth)
sudo journalctl -t phpMyAdmin # phpMyAdmin denials (fail2ban: phpmyadmin-syslog)
sudo tail -f /var/lib/minipanel/apps/logs/php-error.log
# switch off: [apps] webmail = false / phpmyadmin = false in config.toml, then:
sudo ./install/install.sh --yes
Databases
sudo mariadb # root over the socket
sudo mariadb -e "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA"
sudo mariadb -e "SELECT user, host FROM mysql.user WHERE user LIKE 'acme_%'"
sudo mariadb-dump --single-transaction acme_shop > acme_shop.sql
Client databases and users are named
<account>_<suffix>; passwords are generated in
the panel and never stored in clear.
Files and accounts on disk
ls -la /home/<account>/ # public_html/, <domain>/, mail/, tmp/, .ssh/
getfacl /home/<account>/public_html # www-data traverse/read ACLs
sudo -u <account> ls /home/<account> # what the account itself sees
id <account>; passwd -S <account> # uid/groups, lock state (L = suspended)
ls /etc/apache2/minipanel/ # one vhost file per domain
cat /var/spool/cron/crontabs/<account> # rendered crontab
Where things are
| Path | What |
|---|---|
/etc/minipanel/config.toml |
server settings (root-only) |
/var/lib/minipanel/state.db |
the state database |
/var/log/minipanel/audit.log |
audit log (JSON lines) |
/var/backups/minipanel/ |
backup archives |
/var/lib/minipanel/web/,
/var/lib/minipanel/admin/ |
web app session DBs and upload spools |
/var/log/apache2/minipanel/<account>/ |
per-domain web logs |
/etc/letsencrypt/live/ |
certificates |
/usr/local/lib/minipanel/ |
binaries and VERSION |
Emergencies
| Situation | Do |
|---|---|
| Client locked out of the panel | panelctl account passwd NAME (also clears the lockout);
2FA lost → the client uses a recovery code, or you reset with
panelctl account passwd and they disable/re-enable 2FA |
| Admin locked out / lost authenticator | panelctl admin passwd NAME,
panelctl admin totp-reset NAME |
| Site 500 after a cPanel migration | .htaccess has php_value lines — remove
them, use .user.ini |
Account shows apply_state failed |
panelctl account show NAME for the reason, fix,
panelctl rebuild --account NAME |
Certificate stuck pending_dns |
every A/AAAA of the domain must point here;
panelctl ssl retry DOMAIN |
| Panel says "Panel unavailable" | systemctl status minipaneld,
journalctl -u minipaneld |
| Restore a deleted file for a client | panelctl backup restore FILE --account NAME --confirm
(replaces the whole home) or hand-extract
home/<account>/path from the archive with
tar |
| Server lost | fresh install, copy the newest archive to
/var/backups/minipanel/,
panelctl backup restore FILE --confirm |