Command-Line Tool
Everything the panel and the REST API can do, you can also do from the shell with the panelica command. It is the third face of Panelica's triple architecture (panel UI, REST API, CLI) and the fastest way to script provisioning, run one-off admin tasks over SSH, or recover a server. Under the hood it is a thin, authenticated client for the panel's own API, so it enforces exactly the same permissions and validation as the UI. This page documents every command group with real examples and the output you actually get back.
/opt/panelica/bin/panelicapanelica <command> <subcommand> [flags]ID USERNAME EMAIL ROLE STATUS
------------------------------------------------------------
943feea0-19ab-43b7-b53e-6659befb9041 jane [email protected] RESELLER active
$ panelica domains create -n shop.example.com -u 943feea0-... --php=8.3
✓ Domain 'shop.example.com' created
Signing in
The binary lives at /opt/panelica/bin/panelica (add it to your PATH, or call it by full path). It uses the standard cobra framework, so every command has --help, and panelica --version prints the build. Authentication works three ways:
- Automatic (root): run as the Linux root user on the panel server and the CLI logs itself in as the panel's root account automatically (it reads the panel config), so
panelica users listjust works over SSH with no login step. This is the normal path for a server admin. - Explicit login: as any other user, sign in once; the token is cached in
~/.panelica/config.json(mode 0600):panelica login -u admin -p 'your-password' panelica whoami # confirm who you are panelica logout - External API (HMAC): point the CLI at the signed External API with an API key/secret (useful from a remote machine). This switches the base path to
/v1and signs every request:panelica configure --api-key pk_live_xxx --api-secret sk_live_xxx --use-external
Global flags, output and the response envelope
Every command accepts:
-o, --output table|json, table (default) for humans,jsonfor scripts.--api-url https://host:3001, target a different backend (defaults to localhost).-v, --verboseand--config <path>(defaults to~/.panelica/config.json).
The API wraps successful responses in an envelope, {"status":"success","success":true,"data": ... }, and -o json prints that verbatim. So a created object's ID is under .data.id, not .id, which matters when you pipe into jq:
panelica users list -o json | jq -r '.data[].username'
UID=$(panelica users create -u acme -e [email protected] -p 'S3cret!' --plan=<id> -o json | jq -r '.data.id')
Mutating commands (create/delete/suspend…) print a ✓ … line on success and ✗ … on failure, with a non-zero exit code so scripts with set -e stop correctly. Some read commands always print JSON regardless of -o (noted where relevant).
users get and domains get (read-only lookups), and the subdomains, ssl and backup commands (which accept a domain name). Everything else, including users update/delete/suspend, db create --domain and dns records create --zone, expects the actual UUID. Use the matching get/list to find it first.Accounts, plans and quotas
panelica users list
panelica users create -u jane -e [email protected] -p 'S3cret!' --role=RESELLER --plan=<plan_id>
panelica users get jane # resolves username OR id (read-only)
panelica users update <user_id> --full-name "Jane Doe" --role=USER
panelica users suspend <user_id>
panelica users unsuspend <user_id>
panelica users change-password <user_id> -p 'NewPass!'
panelica users delete <user_id>
panelica plans list
panelica plans create --name "Basic" --disk=5120 --bandwidth=51200 --domains=5
panelica plans get <plan_id>
panelica plans delete <plan_id>
panelica quota get <user_id>
panelica quota set <user_id> --disk=10G --inodes=200000
users list (table): columns ID, USERNAME, EMAIL, ROLE, STATUS. users create flags: -u/--username, -e/--email, -p/--password (all needed), plus --full-name, --role (default USER), --plan (a plan UUID). Example JSON from a create:
{
"status": "success", "success": true,
"message": "User created successfully",
"data": {
"id": "943feea0-19ab-43b7-b53e-6659befb9041",
"username": "jane", "email": "[email protected]",
"full_name": "Jane Doe", "role": "RESELLER", "status": "active",
"two_factor_enabled": false, "system_username": "jane",
"home_directory": "/home/jane", "created_at": "2026-07-24T10:00:00Z"
}
}
plans list columns: ID, NAME, DISK, BANDWIDTH, DOMAINS (disk/bandwidth shown as MB). quota set sends --disk as a string (e.g. 10G) and --inodes as an integer; it is a PUT.
Domains, subdomains and redirects
panelica domains list
panelica domains create -n example.com -u <user_id> --php=8.3 --template=laravel
panelica domains get example.com # resolves name OR id (read-only)
panelica domains suspend <domain_id>
panelica domains unsuspend <domain_id>
panelica domains delete <domain_id>
panelica subdomains list --domain=example.com # accepts name or id
panelica subdomains create --name=blog --domain=example.com --path=/blog
panelica subdomains delete <subdomain_id> --domain=<domain_id>
panelica domain-config get <domain_id>
panelica domain-config update <domain_id> --document-root=/public --ssl-redirect=true --www-redirect=www
panelica redirect list <domain_id>
panelica redirect create --domain=<domain_id> --source=/old --target=https://example.com/new --type=301
panelica redirect delete <redirect_id>
domains list (table): ID, DOMAIN, USER, PHP, STATUS. domains create flags: -n/--name, -u/--user (a user UUID), --php (default 8.3), --template (default/laravel/wordpress). A created domain:
{
"status": "success", "success": true,
"data": {
"id": "7479113a-c6ce-4e1d-9aff-6d7fde4b6226",
"domain_name": "example.com", "user_id": "943feea0-...",
"web_server": "nginx_apache", "php_version": "8.3", "php_handler": "fpm",
"document_root": "public_html", "ip_address": "203.0.113.10",
"ssl_enabled": false, "provisioning_status": "completed", "status": "active"
}
}
subdomains is one of the few groups that resolves a domain name to its ID. domain-config update's --ssl-redirect is only sent when you pass the flag explicitly; --www-redirect takes none/www/non-www.
Databases and database users
panelica db list --domain=<domain_id>
panelica db create --name=wp_db --domain=<domain_id> --username=wp_user --password='S3cret!'
panelica db delete <database_id>
panelica db users list --domain=<domain_id>
panelica db users create --domain=<domain_id> --username=wp_user --password='S3cret!' --role=readWrite
panelica db-user list <domain_id>
panelica db-user create --domain=<domain_id> --username=app --password='S3cret!'
panelica db-user delete <db_user_id>
panelica mysql-user list
panelica mysql-user create --username=app --password='S3cret!' --host='%'
panelica mysql-user delete <username> --host='%'
panelica remote-mysql list
panelica remote-mysql add 203.0.113.50 # host is a positional argument
panelica remote-mysql remove <host_id>
db list (table): ID, NAME, TYPE, USER, SIZE. db create --domain takes the domain UUID (it does not resolve a name). Note remote-mysql add takes the host as a positional argument, not a flag.
ID NAME TYPE USER SIZE
------------------------------------------------------------
d5f8a1b2-...-c3d4 wordpress_db mysql wp_user 12.44 MB
DNS and SSL
panelica dns zones list
panelica dns records list --zone=example.com # zone name resolves to id here
panelica dns records create --zone=<zone_id> --type=A --name=www --value=1.2.3.4 --ttl=3600
panelica dns records create --zone=<zone_id> --type=MX --name=@ --value=mail.example.com --priority=10
panelica dns records delete <record_id> --zone=<zone_id>
panelica ssl list
panelica ssl info example.com # resolves name or id
panelica ssl install --domain=example.com --type=letsencrypt
panelica ssl install --domain=example.com --type=custom --cert=./fullchain.pem --key=./privkey.pem
panelica ssl renew example.com
dns records list (table): ID, TYPE, NAME, VALUE, TTL. One subtlety: dns records list --zone resolves a name to an ID, but dns records create --zone expects the zone UUID directly.
ssl list (table): DOMAIN, SSL, PROVIDER, EXPIRES, AUTO-RENEW (read from the domain record). ssl install has two paths: --type=letsencrypt issues/re-issues via Let's Encrypt (no cert files); --type=custom uploads the PEM files you give in --cert and --key. Example:
DOMAIN SSL PROVIDER EXPIRES AUTO-RENEW
------------------------------------------------------------
example.com Yes letsencrypt 2026-10-01 Yes
panelica email accounts list --domain=<domain_id>
panelica email accounts create [email protected] --password='S3cret!' --quota=2048
panelica email accounts delete <email_id>
panelica email forwarders list
panelica email forwarders create [email protected] [email protected]
panelica email autoresponders list
# domain-scoped variants (separate command groups)
panelica email-forwarder list <domain_id>
panelica email-forwarder create --domain=<domain_id> --source=info [email protected],[email protected]
panelica email-autoresponder create --email=<email_account_id> --subject="Away" --body="Back Monday" \
--start-date=2026-08-01 --end-date=2026-08-10
panelica mailing-list create --domain=<domain_id> --name=news [email protected]
panelica mailing-list add-member <list_id> [email protected]
panelica mail-queue list
panelica mail-queue flush
panelica smtp-relay set --host=smtp.provider.com --port=587 --username=u --password=p
panelica smtp-relay disable
email accounts list (table): ID, EMAIL, QUOTA, STATUS. email accounts create derives the domain from the address, so there is no --domain flag here; --quota is in MB (default 250). Note there are two forwarder commands: email forwarders create (--from/--to, a single destination) and the domain-scoped email-forwarder create (--source/--destinations, a comma-separated list).
FTP and SSH access
panelica ftp list
panelica ftp create -u deploy -p 'S3cret!' -d <domain_id> --path=/public_html
panelica ftp suspend <ftp_id>
panelica ftp delete <ftp_id>
panelica ssh-users list
panelica ssh-users create --username=dev --password='S3cret!' --user=<owner_id>
panelica ssh-users delete <ssh_user_id>
panelica ssh config
panelica ssh keys
panelica ssh add-key --name=laptop --key="ssh-ed25519 AAAA..."
panelica ssh remove-key <key_id>
panelica ssh-connection list # live SSH sessions
panelica ssh-connection kill <connection_id>
ftp list (table): ID, FTP USER, OWNER, PATH, QUOTA, STATUS (quota in MB). Password hashes are never returned. ssh-connection list shows the live sessions you can terminate.
Backups
panelica backup list
panelica backup domains # domains you can back up
panelica backup create --name=nightly --domain=all --type=full --encryption=panel-key
panelica backup create --name=one --domain=example.com --encryption=password --password='Key!'
panelica backup details <filename>
panelica backup download <filename> # prints a download URL, not the file
panelica backup restore <filename> --password=<key>
panelica backup delete <filename>
panelica backup-schedule list
panelica backup-schedule create --name=daily --cron="0 3 * * *" --retention=7 --type=full
panelica backup-activity list --limit=50
backup list (table): FILENAME, TYPE, SIZE, CREATED. backup create flags: --name (required), --domain (a domain name, comma-separated names, or the literal all) or --domain-ids (UUIDs), --type (full/incremental), --encryption (none/panel-key/password), --password (required when encryption is password), --note. --domain=all is expanded to every domain you own. backup download prints a tokenized URL to fetch with curl/wget rather than streaming the file itself.
PHP
panelica php list # installed versions
panelica php config <domain_id>
panelica php update <domain_id> --version=8.3 --memory-limit=256M --max-execution-time=120
panelica php restart # or: panelica php restart 8.3
panelica php system-status # the CLI/system default PHP
panelica php system-set 8.3
panelica php system-remove
php update sends only the flags you set (--version, --memory-limit, --max-execution-time). php system-status renders a small report plus a COMMAND, TARGET, STATUS table of the wrapper symlinks:
Status: Configured
Active Version: PHP 8.3
Symlink: /usr/local/bin/php -> /opt/panelica/services/php/8.3/bin/php
COMMAND TARGET STATUS
------------------------------------------------------------
php83 /opt/panelica/services/php/8.3/bin/php OK
Security
panelica security firewall list
panelica security ip block 203.0.113.9 --reason="brute force" --duration=24h
panelica security ip unblock <block_id>
panelica security ip list
panelica security modsecurity status
panelica antivirus status <scan_id>
panelica antivirus list # scan history
panelica file-audit list --limit=50
panelica logs audit # panel audit trail
security ip list (table): ID, IP ADDRESS, REASON, BLOCKED AT. security ip block takes the IP as a positional argument with --reason and an optional --duration (e.g. 24h, 7d); omitting duration blocks permanently.
Server, services and monitoring
panelica server status # CPU/RAM/disk snapshot
panelica server info # full hardware inventory (JSON)
panelica server services list
panelica server services restart panelica-mysql
panelica services list
panelica monitoring status
panelica monitoring metrics
panelica processes list
panelica processes kill <pid>
panelica logs access --domain=<domain_id> --lines=100
panelica logs error --domain=<domain_id> --lines=100
server status prints a formatted snapshot (and full JSON with -o json):
Server Status:
--------------
Uptime: up 5 days, 3 hours
CPU: 8 cores, 12.4% usage
Memory: 4096 MB / 16384 MB (25.0%)
Disk: 84 GB / 400 GB (21.0%)
Load Average: 0.34, 0.28, 0.19
server services list (table): NAME, TYPE, STATUS, PORT, VERSION. To control a service, use server services restart <name> (which drives the panel's service-control path); monitoring metrics returns the same live CPU/memory/disk/load figures as the dashboard as JSON.
Sessions, 2FA and your own account
panelica session list
panelica session kill <session_id>
panelica session kill-all # end every other session
panelica 2fa status
panelica 2fa enable # returns secret + QR URL
panelica 2fa verify 123456
panelica 2fa disable --password='your-password'
panelica password change --current='old' --new='new'
panelica password generate --length=24 # local, no API call
password generate runs entirely locally (crypto-strong, clamped to 8–128 chars) and prints just the password, handy for scripting new credentials.
Updates and packages
panelica package check # look for updates
panelica package list # same, alias
panelica package apply panelica-server
panelica package rollback panelica-server --slot=23
panelica package reinstall nginx
panelica package history panelica-server
package history (table): SLOT, VERSION, PREVIOUS, INSTALLED AT. package rollback without --slot does a smart rollback (the newest slot whose version differs from the current one); pass --slot=N to target a specific backup slot. package apply on panelica-server triggers a self-update; see System Updates for how the panel reconnects.
SLOT VERSION PREVIOUS INSTALLED AT
------------------------------------------------------------
23 1.0.372 1.0.371 2026-07-20T14:03:11Z
22 1.0.371 1.0.370 2026-07-18T09:11:02Z
Utilities
panelica validate domain example.com # always JSON
panelica validate email [email protected]
panelica validate password 'S3cret!'
panelica api generate-openapi > openapi.json # in-process, emits OpenAPI 3.0
panelica config-locks list
panelica panel-app list
panelica panel-app restart <app_name>
panelica panel-settings get
panelica panel-settings set <key> <value>
panelica phpmyadmin sso <database_id> # returns a one-time login URL
panelica system-cron list
panelica system-cron run <job_id>
panelica bandwidth user <user_id>
panelica bandwidth domain <domain_id>
panelica files list /home/jane/public_html
panelica files read /home/jane/public_html/index.php
panelica ip-addresses list # alias: panelica ip list
panelica ip-addresses add --ip=203.0.113.60 --interface=eth0
validate prints a JSON verdict ({"valid":true,"score":4,...}). api generate-openapi builds the spec in-process (no server call) and writes OpenAPI 3.0 JSON to stdout, a quick way to feed the API into other tooling.
Operator recovery commands
A few commands work without logging in, because they exist to rescue a server; they read the panel's database directly, so in practice only Linux root can run them:
panelica root 'NewRootPassword!' # reset the panel root password (min 6 chars)
panelica panel # print a one-time browser login URL (root)
panelica login --url --user=jane # one-time login URL for a specific user
panelica migration export-metadata --quick # export account/domain metadata (JSON)
panelica panel is handy over SSH, it prints a link that logs you into the web panel once (valid five minutes) with no password. migration export-metadata dumps account and domain data as JSON for the migration tooling; its --quick mode lists domains, the default deep mode includes databases, email and (decrypted) Cloudflare credentials.
panelica login or an API key so it stays audited and role-scoped like everything else. And keep passwords off the command line where you can (they show up in shell history and ps), prefer prompting or a tightly-permissioned config file.A worked example: provision a customer end-to-end
#!/usr/bin/env bash
set -e
# 1. create the account on a plan (the ID is under .data.id)
UID=$(panelica users create -u acme -e [email protected] -p 'S3cret!' \
--role=USER --plan=<plan_id> -o json | jq -r '.data.id')
# 2. add the domain with PHP 8.3, capturing its ID
DID=$(panelica domains create -n acme.com -u "$UID" --php=8.3 -o json | jq -r '.data.id')
# 3. database + user (db create takes the domain ID)
panelica db create --name=acme_db --domain="$DID" --username=acme_db --password='Db!pass'
# 4. a mailbox
panelica email accounts create [email protected] --password='Mail!pass' --quota=2048
# 5. issue a certificate (ssl install accepts the domain name)
panelica ssl install --domain=acme.com --type=letsencrypt
echo "acme.com provisioned"
Access
The CLI enforces the panel's roles and permissions through the API, so what a given login can do from the shell matches exactly what it can do in the UI. Running as Linux root gives full panel-root scope; anything else is scoped to that account.