Configuration Reference
Configuration File
Location: /etc/whost/agent.conf (YAML format)
File permissions: 600, owner root (readable only by root). The agent resets the mode to 600 at every start.
The installer writes a working configuration with sane defaults — most operators never need to edit it directly: most keys below are changed from the panel, which writes this file and applies the change to the service concerned. The sections below describe what each block controls so you know where to look when something does need adjustment.
How the file is written. The installer writes the first version with the blocks it needs. From then on the agent rewrites the whole file from the settings it holds in memory: at its first start, which adds every block below with its default values, and on every change saved from the panel. Comments, key order and formatting added by hand are not kept, and a key the agent does not know (a misspelled one included) is ignored without a warning and dropped at the next write. Broken YAML, or a value of the wrong type (text where a number or true/false is expected), stops the agent from starting; the reason is appended to /var/log/whost/agent.log.
Secrets. Passwords and secrets are stored in this file in plain text (the administrator password as a bcrypt hash). The agent moves them into an in-memory vault while it runs; on disk the file's 600 root mode is their only protection, so keep every copy and backup of the file equally restricted.
Settings kept elsewhere. Update preferences (/etc/whost/update_settings.json, see the last section), the notification channels and relay of Settings › Notifications (/etc/whost/notification_prefs.json), remote backup destinations (/etc/whost/remote_backup.json), API keys (/var/lib/whost/api_keys.json), plugin state (/etc/whost/plugins.json) and the server time zone (a system setting) are not in this file.
Reading the tables. Default is the value the agent uses when the key is missing; where the installer writes a different value, it is named. Changed from names the panel page. file — only by editing this file (see Applying Changes at the end). agent — written by the installer or the agent: do not edit it, a hand-written value breaks the service that uses it. — — the key has no effect in this release.
Section Details
server
Identity of the server, and the agent's own listener.
| Key | Default | Changed from | What it does |
|---|---|---|---|
hostname |
installer: the host name (--hostname) |
Settings › General › Hostname | The panel's host name (FQDN). A save from the panel also runs hostnamectl, rewrites /etc/hosts, sets mail.hostname, reloads Postfix and Dovecot and rewrites the panel's nginx vhost. Notification e-mails link to https://<hostname>, and it is the fallback origin of the password-reset link (see frontend.public_url). |
server_ip |
installer: the server's primary IPv4 address | Settings › General › Server IP Address | The primary public IPv4 address: an account gets it unless another address is assigned, the account's DNS zone points at it, and the panel vhost answers on it. A save from the panel rewrites the DNS A records that carried the old address. |
force_ssl |
true |
Settings › General › Force HTTPS Redirection | Redirects the panel's HTTP requests to HTTPS; a request that a proxy or CDN forwards with X-Forwarded-Proto: https is served without the redirect. The save rewrites the panel vhost and reloads nginx. |
host, port |
0.0.0.0, 2000 (installer: 127.0.0.1, 2000) |
file | They do not move the listener: the agent listens on 127.0.0.1:2000 as its service unit sets it (--host, --port; the unit also reserves ports below 2001 to root — net.ipv4.ip_unprivileged_port_start = 2001, kept in /etc/sysctl.d/60-whost-agent-port.conf — so no process of an account can listen on the agent's port, not even while the agent restarts; processes that do not run as root cannot listen on ports 1024–2000), and the panel's nginx proxy and the update pipeline use that address. port is still read by the firewall rule that keeps tenant processes away from the agent and by the port list Under-Attack mode never drops — keep it 2000. |
ssl_cert, ssl_key |
/etc/whost/ssl/cert.pem, /etc/whost/ssl/key.pem |
— | Not read; see the certificate note below. |
workers |
1 |
— | Always 1: any other value is logged and replaced with 1. |
Panel certificate. The pair /etc/whost/ssl/cert.pem and /etc/whost/ssl/key.pem is created self-signed for the host name at install. The same pair serves the panel's nginx vhost on port 443, Postfix and Dovecot TLS, the OpenLiteSpeed listener when LiteSpeed serves the sites, and the agent's loopback listener. To serve a trusted certificate, replace the two files (the certificate with its chain in cert.pem), check with nginx -t, then reload nginx, Postfix and Dovecot.
auth
HMAC-SHA256 authentication of API requests (machine-to-machine; the panels use session cookies).
| Key | Default | Changed from | What it does |
|---|---|---|---|
api_key, api_secret |
generated by the installer | agent | The API key pair the installer generated (also saved in /etc/whost/api_key and /etc/whost/api_secret). At its first start the agent copies it into the API key list as Default API Key (Settings › API Access), where it is managed like any other key; revoke or delete it there to retire it. A deleted installer key stays retired: it does not answer through this copy and is not copied back at the next start. A pair a later installer run writes answers until it is managed the same way. |
allowed_ips |
[] |
file | Copied onto that key's IP allowlist when the agent adds the key to the list; afterwards the allowlist is edited in Settings › API Access, and this list no longer applies to the key. |
api_key_encryption_secret |
generated at first start | agent | Encrypts the stored API key secrets and the credentials of remote backup destinations. Never change or remove it: the stored secrets could no longer be decrypted. |
webserver
Which web server stack serves the sites, and its global connection tuning. type takes one of:
- nginx: Nginx handles all HTTP traffic directly
- apache: Apache handles all HTTP traffic directly
- nginx_apache: Nginx as reverse proxy (port 80/443) + Apache backend (
apache_port, 8080) - openlitespeed, litespeed: OpenLiteSpeed or LiteSpeed Enterprise, set when the matching plugin is activated on the Plugins page; PHP then runs through LSAPI
The installer writes nginx_apache or nginx; it refuses the other values (see Installation).
| Key | Default | Changed from | What it does |
|---|---|---|---|
type |
nginx_apache (installer: --webserver) |
Settings › General › Default Web Server (nginx, nginx_apache; apache is offered and accepted only on a server installed with Apache alone) |
The stack the agent writes every site configuration, certificate renewal hook and WAF setup for. The General save changes only this value — it does not install, start or reconfigure a web server — so change it only after the server's stack has been changed to match. With OpenLiteSpeed or LiteSpeed the field is read-only (the plugin owns it). At start the agent logs WEBSERVER_BINDING_DRIFT when the process listening on port 443 does not match. |
max_clients, keepalive_timeout |
256, 65 |
Settings › General › Max Clients (1–10000), Keep-Alive Timeout (0–300 s) | Written to the web server when saved from the panel: Nginx worker_connections and keepalive_timeout in nginx.conf (only where those lines exist; nginx -t, then reload), Apache KeepAliveTimeout only (apachectl configtest, then reload), OpenLiteSpeed/LiteSpeed maxConnections and keepAliveTimeout (the server restarts). A failed test restores the previous file. The page shows the configured values, not the running server's. |
apache_port |
8080 |
file | The Apache port nginx proxies to in nginx_apache site configurations. Apache's own Listen port is set by the installer and does not follow this key. |
php_handler |
fpm |
agent | fpm (PHP-FPM pools) or lsapi; set to lsapi whenever type is OpenLiteSpeed or LiteSpeed. |
ols_admin_port, ols_admin_user, ols_admin_password |
7080, admin, empty |
agent | The OpenLiteSpeed/LiteSpeed WebAdmin console. The LiteSpeed Enterprise plugin generates the password when it installs the server (a value already here is reused on a reinstall); read it here when you need the console. |
lscache_enabled |
true |
file | Adds LSCache to OpenLiteSpeed/LiteSpeed site configurations. |
litespeed_serial |
— | agent | The LiteSpeed Enterprise serial the plugin was activated with. |
nginx_port |
80 |
— | Not read. |
php
Multiple PHP versions can be installed simultaneously, and each hosting account can use a different one.
| Key | Default | Changed from | What it does |
|---|---|---|---|
default_version |
8.4 (installer: --php-default) |
Settings › General › Default PHP Version; PHP › Default PHP Settings | PHP version of new accounts; must be one of installed_versions. |
installed_versions |
7.4, 8.0–8.5 (installer: the versions it installed, --php-versions) |
PHP page, version switches | Versions offered to accounts. A version can be switched on only when its packages are installed; the default version and a version a domain still uses cannot be switched off. |
default_upload_max_filesize, default_post_max_size |
64M |
PHP › Default PHP Settings | A size with an optional K/M/G; -1 is refused. |
default_memory_limit |
256M |
PHP › Default PHP Settings | A size, or -1 for unlimited. |
default_max_execution_time |
300 |
PHP › Default PHP Settings | Seconds, 0–86400. |
default_max_input_time |
300 |
PHP › Default PHP Settings | Seconds, -1–86400. |
default_max_input_vars |
1000 |
PHP › Default PHP Settings | 1–1000000. |
default_display_errors |
false |
PHP › Default PHP Settings | true or false. |
The seven default_* values are the ini values an account's pool is rendered with until the account saves its own; an account's saved values live in its metadata and survive a version, worker or plan change. Changing a default does not rewrite existing pools: an account without values of its own gets the new default the next time its pool is rendered.
python
Python application hosting (the Python Hosting plugin). The plugin's on/off state is client_features.python_apps, not a key of this block.
| Key | Default | Changed from | What it does |
|---|---|---|---|
installed_versions |
3.10, 3.11, 3.12, 3.13 |
file | Python versions offered to apps; only those whose interpreter is installed on the server are listed. |
default_timeout, default_max_requests |
120, 1000 |
file | Gunicorn timeout and max_requests of an app that has not set its own. |
allowed_manage_commands |
migrate, collectstatic, createsuperuser, showmigrations, check, shell |
file | The Django manage.py commands the panel may run for an app; any other command is refused. |
socket_dir, venv_base_dir |
/run/whost, venvs |
agent | Where app sockets and (under the account's home) virtual environments are created. |
enabled, default_version, default_app_server, default_workers, default_worker_class, max_workers_per_app |
— | — | Not read. |
node
Node.js application hosting (the Node.js Hosting plugin). The plugin's on/off state is client_features.node_apps, not a key of this block.
| Key | Default | Changed from | What it does |
|---|---|---|---|
installed_versions |
18, 20, 22 |
file | Node.js versions offered to apps; only those whose runtime is installed on the server are listed. |
allowed_npm_scripts |
start, build, test, lint, migrate, seed |
file | The npm run scripts the panel may run for an app; any other script is refused. |
socket_dir, wrappers_dir |
/run/whost, /etc/whost/wrappers |
agent | Where app sockets and the per-app start wrappers are created. |
enabled, default_version, default_app_type, default_package_manager, default_instances, max_instances_per_app, default_max_memory_mb, runtime_base_dir |
— | — | Not read. |
mysql
The agent's MariaDB connection and the MariaDB tuning it applies for you.
| Key | Default | Changed from | What it does |
|---|---|---|---|
host, port |
localhost, 3306 |
agent | The agent's MariaDB connection. |
root_password |
set by the installer | agent | MariaDB root password (also in /etc/whost/mysql_root_password). |
max_connections |
151 |
Settings › General (1–10000) | max_connections. |
buffer_pool_size |
256 (MB) |
Settings › General (16–65536) | innodb_buffer_pool_size. |
connect_timeout, wait_timeout, interactive_timeout |
300 (seconds) |
Settings › General (1–86400) | The MariaDB variables of the same name. |
sort_buffer_size, read_buffer_size, max_allowed_packet |
4M, 4M, 64M |
Settings › General | A non-zero K/M/G size (4M, 256K, 1G). |
A save from Settings › General that changes a database value rewrites the drop-in /etc/mysql/mariadb.conf.d/99-whost-tuning.cnf (/etc/my.cnf.d/ on RHEL) from the agent's template with all eight values and applies each one live with SET GLOBAL, so no restart follows. The drop-in also carries local_infile = 0 and secure_file_priv = /var/lib/mysql-files, which MariaDB reads at its next restart. The installer writes its own version of this file, sized to the server's RAM (InnoDB buffer pool 37.5% of RAM, at least 256 MB; max_connections = 300; InnoDB log and flush settings); the first database save from the panel replaces it with the eight values the page shows. Editing these keys in this file does not change MariaDB.
dns
PowerDNS integration via its HTTP API.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true (installer: false with --no-dns) |
file | Whether PowerDNS is installed. While false, accounts are created without a DNS zone, and an account's address change or a nameserver change is not pushed to zones. |
nameservers |
ns1.example.com, ns2.example.com (installer: --nameservers, default ns1.<hostname>, ns2.<hostname>) |
Settings › General › Nameservers (up to four, filled in order) | NS records of every zone the agent creates; the first entry is the SOA primary. A save from the panel also rewrites the NS records and SOA primary of every existing zone; a hand edit reaches only zones created after the restart. Set them to your real nameserver host names, as the delegation at the registrar names them. |
api_url, api_key |
http://127.0.0.1:8081, set by the installer |
agent | The PowerDNS HTTP API and its key (also in /etc/whost/pdns_api_key). |
default_ttl |
3600 |
file | TTL of the records the agent creates. |
soa_content |
ns1.example.com hostmaster.example.com 0 10800 3600 604800 3600 |
file | Only its last four numbers — refresh, retry, expire and negative-cache TTL — are used; the SOA primary is the first nameserver and the contact is hostmaster.<zone>. |
type |
powerdns |
— | Not read; PowerDNS is the only DNS server. |
Postfix (SMTP) + Dovecot (IMAP/POP3) + OpenDKIM. The mail system uses a MariaDB database for virtual mailbox management.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true (installer: false with --no-mail) |
file | Whether the mail stack is installed. While false the agent does not install or refresh the WHost Mail Policy service (whost-policyd) that applies the hourly sending limits. |
hostname |
mail.example.com (installer: --mail-hostname, default mail.<hostname>) |
file | The mail host name recorded at install; a host-name change in Settings › General replaces it with the new server host name. |
default_hourly_limit |
500 |
Settings › General › Default Hourly Email Limit (0–100000) | Messages an account may send per hour when its plan sets no limit of its own; 0 = unlimited. A save from the panel re-renders and restarts whost-policyd; a hand edit reaches it at the next agent start. |
max_email_size |
25 (MB) |
Settings › General › Maximum Email Size (1–100 MB) | Written to Postfix message_size_limit when saved from the panel (Postfix reloads); a hand edit does not change Postfix. |
dkim_selector, dkim_key_dir |
default, /etc/opendkim/keys |
agent | Selector and key directory of the DKIM keys the agent creates (<selector>._domainkey.<domain> records). |
maildir_base, vmail_uid, vmail_gid |
/var/vmail, 5000, 5000 |
agent | Mailbox storage and its owner, as installed. |
mysql_host, mysql_port, mysql_user, mysql_password, mysql_database |
localhost, 3306, vmail, set by the installer, vmail |
agent | The virtual-mailbox database (password also in /etc/whost/mail_db_password). |
dovecot_master_user, dovecot_master_password |
whost_master, set with webmail |
agent | The Dovecot master login used for webmail single sign-on. |
ssl
Let's Encrypt integration via certbot. Orders validate over HTTP from the domain's own document root, are named after the domain and install a renewal hook that refreshes the files the vhost points at and reloads the web server; renewals run on certbot's own schedule (its systemd timer, or the daily cron line the installer adds where there is no timer). Deleting a certificate removes the lineage.
| Key | Default | Changed from | What it does |
|---|---|---|---|
email |
empty (installer: --ssl-email) |
Settings › General › Let's Encrypt Email | Account e-mail of the orders, required: an order is refused with 400 SSL_EMAIL_REQUIRED while it is empty. |
staging |
false |
file | true sends orders to the Let's Encrypt staging environment, whose certificates browsers do not trust — for testing without hitting rate limits. Restart the agent after changing it. |
auto_renew |
true |
Settings › General › Auto-SSL for New Domains | Stored only in this release: the account and addon-domain forms carry their own Auto-SSL choice. |
provider, webroot_path, renew_days_before |
certbot, /var/www/letsencrypt, 30 |
— | Not read. |
ftp
Pure-FTPd with MariaDB authentication. Each hosting account can have multiple FTP sub-accounts.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true (installer: false with --no-ftp) |
file | Whether Pure-FTPd is installed. |
passive_ports |
30000:30100 |
Settings › General › Passive Port Range | START:END, 1024–65535, START lower than END. Written to Pure-FTPd's PassivePortRange when saved from the panel. The installer opens 30000:30100 in the firewall; open a new range on the Firewall page as a START:END port rule. |
max_clients |
50 |
Settings › General › Max Clients (1–1000) | MaxClientsNumber. |
max_per_ip |
10 |
Settings › General › Max Connections per IP (1–100) | MaxClientsPerIP. |
passive_mode, bandwidth_limit |
true, 0 (KB/s) |
API only (the form does not show them) | Stored only: the save keeps and echoes them, but no Pure-FTPd directive is written for them. |
mysql_host, mysql_port, mysql_user, mysql_password, mysql_database |
localhost, 3306, pureftpd, set by the installer, pureftpd |
agent | The Pure-FTPd authentication database (password also in /etc/whost/ftp_db_password). |
A save that changes passive_ports, max_clients or max_per_ip writes the three directives (/etc/pure-ftpd/conf/ on Debian/Ubuntu, /etc/pure-ftpd/pure-ftpd.conf on RHEL) and restarts Pure-FTPd; a hand edit does not change Pure-FTPd.
backup
Local backup storage and its retention.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true |
Backups › Settings › Backup System | While false, creating, restoring and scheduling backups is refused (403 BACKUP_DISABLED) and scheduled runs are skipped. |
local_path |
/var/whost/backups |
file | Where local archives are kept, one directory per account. |
retention_days |
7 (installer: 30) |
Backups › Settings › Retention Days (1–365) | Once a day, on the scheduler's first run at or after 04:00 UTC, every account backup older than this is deleted; 0 in this file turns pruning off. |
schedule.enabled |
true |
Backups › Settings › Schedule System | Starts the built-in scheduler, which runs the backup schedules and the retention pruning. |
schedule.max_per_account |
3 |
Backups › Settings › Max Schedules per Account (1–20) | A schedule beyond this is refused (403 BACKUP_SCHEDULE_LIMIT). |
schedule.check_interval |
300 (seconds) |
file | Time between scheduler runs. |
remote |
enabled: false, type: s3 |
— | Parsed but not used; see below. |
The scheduler starts with the agent only while both enabled and schedule.enabled are true, and schedule.check_interval is read at that moment: a change of schedule.enabled — also when saved from the panel — takes effect at the next agent start, and so does switching enabled back on when it was off at start. While the scheduler is not running, neither scheduled backups nor retention pruning run.
Remote destinations are not configured in this file: they are managed from the panel (Backups › Remote Storage) and stored encrypted in /etc/whost/remote_backup.json (per-account ones under /etc/whost/accounts/<user>/remote_backup.json).
logging
| Key | Default | Changed from | What it does |
|---|---|---|---|
level |
info |
file | debug, info, warning, error or critical (an unknown name means info). In production, info is recommended; use debug only for troubleshooting. |
file |
/var/log/whost/agent.log |
file | Keep the default: the service unit appends the process output to this path, the fail2ban whost-agent jail watches it and the post-update check writes to it. The file is kept at mode 640. |
max_size, keep |
100M, 10 |
file | The agent rotates the file itself when it reaches max_size (a number with an optional K, M or G; any other suffix stops the agent from starting) and keeps keep old copies. No logrotate rule covers it. |
license
license.state_file selects the persistent license state file; its default is
/etc/whost/license.state. The companion HMAC file appends .hmac to that path.
State loading, saving, and independent endpoint, service, startup and runtime
checks use the selected file. An old state file at the default location does
not substitute for a missing file at the configured location.
Keep both files owned by root with mode 0600. Startup normalizes their modes
to 0600. Restart the agent after changing the setting; avoid repeated restarts,
which consume license verification limits. The installation's boot token stays
at /etc/whost/.boot_token; prior activation evidence in either the selected or
default state file prevents silent regeneration of a missing or malformed token.
License grace and cached-token validity use the license server's verified UTC time advanced by elapsed kernel boot time. Moving the system clock backward does not add grace time. The state HMAC also protects the clock anchor, allowing an agent restart within the same operating-system boot to preserve elapsed time. After an operating-system reboot, or when upgrading a cache without this anchor, the agent requires online license verification before cached grace can be used. Keep NTP enabled: TLS certificate validation and other system services still depend on the system clock.
Cached activation and grace require the original RSA-signed license assertion and its expiry. A valid local HMAC alone does not authorize cached access. Missing signature fields, a missing signed expiry, modified assertions, and expired tokens discard the cache and require online verification. Activation and periodic verification both persist the signed assertion. Do not edit or remove token fields when restoring a state file.
The other keys of this block are maintained by the agent — do not edit them. key and the license secrets are written when the license is activated on the License page; a hand-edited key that differs from the activated one is replaced with the activated key at the next start.
rate_limit
API rate limiting per client address (the visitor's address as the agent resolves it behind the panel's nginx), using slowapi. A request over its limit is answered 429 RATE_LIMIT with a Retry-After header. Values follow the format {count}/{period} (e.g., 120/minute, 5/minute).
rate_limit:
default: "60/minute"
read: "120/minute"
write: "30/minute"
delete: "20/minute"
heavy: "5/minute"
auth: "5/minute"
All six values are read from this file at agent start and applied.
defaultApplied by the limiter middleware to the few routes that carry no class of their own (deploy event streams, the update status stream, the SSO endpoints, the frontend debug beacon). /health, the panel's root files (/config.json, favicon, logo), / and the page fallback are exemptreadLimit for GET (read) endpoints, including the public GET /api/v1/auth/ip-check allowlist probe used by the login pagewriteLimit for POST, PUT, PATCH (write) endpointsdeleteLimit for DELETE endpointsheavyLimit for resource-intensive operations: backups and restores, file upload, compress and extract, Let's Encrypt orders, plugin installs, license activation, password and second-factor changes, password-reset requestsauthLimit for authentication endpoints (admin and client login, 2FA)fail2ban
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true |
— | Not read: the panel manages fail2ban whenever fail2ban-client answers. |
bantime, findtime, maxretry |
3600, 600, 5 |
— | Not read: the installer writes jail.local with its own values, and per-jail changes on the Fail2Ban page go to /etc/fail2ban/jail.d/<jail>.local. |
ignoreip |
127.0.0.1/8, ::1 |
file | Shown as the whitelist only when no jail answers; the live whitelist is the jails' ignoreip set, edited on the Fail2Ban page and persisted in jail.local. |
modsecurity
WHost enforces ModSecurity at the nginx tier (nginx and nginx_apache); with another web server the settings are saved but not applied to traffic. /etc/modsecurity/modsecurity.conf is rendered from this block when the WAF page changes a setting or a rule — after a web-server configuration test — so a hand edit reaches it only at the next such change.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled, default_account_waf |
true, true |
file | An account that has not chosen its own WAF state gets modsecurity on in its site configuration only while both are true (picked up when the configuration is next rendered). enabled: false also makes the WAF page report the WAF as inactive. |
engine_mode |
detection_only |
WAF page | on (block), detection_only (log only) or off; rendered as SecRuleEngine. A new installation starts in detection_only; an existing one keeps the mode it has. |
crs_paranoia_level |
1 |
WAF page | OWASP CRS paranoia level, 1–4. |
audit_log |
true |
WAF page | SecAuditEngine RelevantOnly (5xx and 4xx responses except 404) or Off. |
audit_log_path |
/var/log/modsecurity/audit.log |
file | The serial audit log the WAF page reads; the installer's logrotate rule (daily, 30 kept, copytruncate) names the default path. |
owasp_crs |
true |
file | Loads the OWASP Core Rule Set from <rules_path>/crs. |
rules_path, custom_rules_path |
/etc/modsecurity, /etc/modsecurity/custom |
file | The rule set's base directory, and the custom rules directory (included only while it holds a .conf). |
per_account_overrides |
true |
file | Per-account WAF state and rule exclusions under /etc/modsecurity/accounts/; while false the account-scope WAF writes answer 409 FEATURE_DISABLED. |
security
| Key | Default | Changed from | What it does |
|---|---|---|---|
admin_allowed_ips |
empty | Settings › Security › Admin Panel IP Whitelist | IP/CIDR entries, one per line (a YAML block string); empty allows every address. nginx answers 404 for the /admin pages from any other address, and the agent refuses admin sign-in and admin sessions from it (403 IP_NOT_ALLOWED). A save from the panel rewrites /etc/nginx/conf.d/whost-admin-geo.conf and /etc/nginx/snippets/whost-admin-ip-whitelist.conf (after nginx -t), and a changed non-empty list signs out every admin and client session; a list that leaves out your own address is refused unless you confirm it. A hand edit changes only the agent's check. |
two_factor_enforcement, two_factor_enforcement_method |
false, both |
Settings › Security › Two-Factor Authentication Enforcement | The switch and the method it demands (totp, email, or both = either). While it is on, the login and session endpoints answer requires_2fa_setup: true (with required_2fa_method) for an identity that has no second factor yet, and the panels keep that identity on its enrolment page (admin Settings › Profile, client Settings › Security) until it enrols. |
ssh_port, ssh_root_login, ssh_password_auth, ssh_key_auth, ssh_allowed_ips |
22, true, true, true, empty |
Settings › Security › SSH Access Restrictions | Written to /etc/ssh/sshd_config.d/00-whost.conf when saved from the panel (after sshd -t; a port change restarts sshd and updates the firewall and fail2ban, other changes reload it). At least one of password and key authentication stays on, and a port another service holds is refused (PORT_IN_USE). ssh_allowed_ips (one IP/CIDR per line) denies SSH logins from every other address. ssh_port is also the SSH port the firewall and Under-Attack mode protect. A hand edit does not change sshd. |
auto_configure_dkim, auto_configure_spf, auto_configure_dmarc, dmarc_policy |
true, true, true, none |
Settings › Security › DKIM / SPF / DMARC | Which mail records the agent adds when it creates an account's mail DNS: the DKIM key record, SPF (v=spf1 a mx ip4:<server_ip> ~all) and DMARC (v=DMARC1; p=<policy>; rua=mailto:postmaster@<domain>, policy none, quarantine or reject). Existing zones are not rewritten. |
trusted_origins |
empty | file | Extra origins allowed to send cookie-authenticated writes (POST, PUT, PATCH, DELETE) whose Origin differs from the host they are sent to, whitespace separated as host[:port]. The panel calling its own API under whatever name it was opened always passes, and HMAC-signed calls are not checked; any other cross-origin write that carries a panel session cookie is refused with 403 CROSS_ORIGIN_DENIED. |
spam_reject_threshold, spam_add_header_threshold, spam_greylist_threshold |
15, 6, 4 |
— | Not read: the spam scores are set on the Spam Filter page. |
admin
The built-in administrator, the sign-in protection of both panels and the session lifetimes.
| Key | Default | Changed from | What it does |
|---|---|---|---|
enabled |
true |
file | While false, cookie sign-in is off for both panels: the admin sign-in answers 404, admin and client session cookies are no longer accepted, and only HMAC-signed API calls are served. |
username |
admin |
file | The administrator signs in with this name or with email. |
email |
empty | Settings › Profile | Recovery address and second sign-in name of the administrator; admin notification e-mails and e-mail sign-in codes are sent to it. |
password |
set by the installer | Settings › Profile (password change) | A bcrypt hash; a plain-text value is never accepted at sign-in. A change signs out every other admin and client session. |
language |
auto |
Settings › Profile | The administrator's panel language (auto keeps the language chosen at sign-in). |
session_timeout |
3600 (seconds; installer: 3600) |
Settings › Security › Session Timeout (5–1440 minutes) | Lifetime of an admin or client session; a session in use is renewed once half of it has passed. |
remember_me_timeout |
2592000 (30 days) |
file | Lifetime of a "remember me" session, in both panels. |
session_max_lifetime |
604800 (7 days) |
file | Hard limit counted from sign-in, whatever the activity, in both panels. |
allow_concurrent_sessions |
false |
file | Off: a sign-in ends the earlier sessions of the same identity (the administrator, or that client account) and a sign-out ends them all. On: earlier sessions keep working until they time out, a sign-out ends only the browser it is made in, and session_max_lifetime counts from the identity's latest sign-in or sign-out; a password change still signs everyone out. |
session_secret |
set by the installer | agent | Signs admin and client session cookies. The agent replaces it when the admin password changes and when the admin IP whitelist is changed to a non-empty list; any change signs everyone out. |
two_factor.enabled, two_factor.method |
false, totp |
Settings › Profile | The administrator's own second factor (totp or email). |
two_factor.email |
empty | Settings › Profile | Where the administrator's e-mail sign-in codes go; it follows email unless set to another address here. |
two_factor.trusted_ip_days |
0 |
file | How long an address that passed the administrator's second factor skips it: 0 = with no expiry while sign-ins come from that address, -1 = the code is always asked, N = N days. |
two_factor.smtp_host, two_factor.smtp_port, two_factor.smtp_user, two_factor.smtp_password, two_factor.smtp_tls |
localhost, 587, empty, empty, true |
file | The relay of the sign-in code e-mails of both panels and of the client password-reset e-mail — not the relay of Settings › Notifications. With the defaults the local Postfix is used on port 25. In this release these messages are sent without SMTP authentication even when smtp_user and smtp_password are set, so smtp_host must name a relay that accepts this server without a login. |
recaptcha.enabled, recaptcha.provider, recaptcha.min_score |
false, recaptcha, 0.5 |
Settings › Security › CAPTCHA Protection | Sign-in captcha of both panels; provider recaptcha, hcaptcha or turnstile; min_score (0.0–1.0) is the reCAPTCHA v3 threshold. Switching it on requires the selected provider's site key and secret key. The public GET /api/v1/auth/captcha-config publishes only the enabled flag, the provider and its site key. |
recaptcha.<provider>_site_key, recaptcha.<provider>_secret_key |
empty | Settings › Security › CAPTCHA Protection | The key pair of each provider; the page shows secret keys masked. |
notification_smtp_password |
empty | Settings › Notifications | Password of the notification relay. The rest of that SMTP block (host, port, user name, encryption and sender) is stored in /etc/whost/notification_prefs.json; only the password is kept here. Notification e-mails use that block, then fall back to the two_factor.smtp_* keys, then to the local relay. |
two_factor.totp_secret, backup_codes, pending_*, totp_last_step, avatar_url, last_login |
— | agent | Maintained by the Profile page and the sign-in flow (the TOTP secret, hashed backup codes, an enrolment in progress, the avatar). |
frontend
Static panel bundle served by nginx.
| Key | Default | Changed from | What it does |
|---|---|---|---|
public_url |
empty | file | The address the panel is reached at from outside (https://panel.example.com): the origin of the password-reset link the panel mails. When it is empty the link uses https://<server.hostname>, and only without a host name the bare https://<server_ip> — a name matches the certificate the panel serves, an address does not. Notification e-mails link to https://<server.hostname> whatever this key says. |
default_language |
auto |
Settings › General › Language Settings | auto or one of the panel's locale codes: the language of a browser that has not chosen one. Written to the panel's config.json when saved and at every agent start. |
debug_log |
false |
file | Opens the browser debug beacon route (POST /api/v1/debug-log); its file /var/log/whost/frontend-debug.log is rotated daily by the installer's /etc/logrotate.d/whost (seven copies kept). |
enabled, path |
true, /opt/whost/frontend/out |
agent | The agent's copy of the panel files; nginx serves /var/www/whost/panel, which the installer copies from it. |
client_features
Eleven switches (file_manager, dns, backups, cron, ssl, php, email, ftp, databases, python_apps, node_apps), all true by default, edited from Settings › General. A switch that is off is enforced at the API for every client route of that surface (409 FEATURE_DISABLED, keyed on the request path — /client/emails and /client/spam for email, /client/files for file_manager, the per-domain /client/domains/{domain}/php/* routes for php); the client profile carries the switches as features, and the client panel hides the sidebar entry and the dashboard tiles of a surface that is off. A switch that is off does not stop the backup scheduler: backups off refuses the client's backup routes only, and the account's existing schedules keep running.
python_apps and node_apps are also the on/off state of the Python Hosting and Node.js Hosting plugins: activating or deactivating the plugin on the Plugins page sets them (deactivation is refused while apps are deployed), and while one is off every Python or Node.js app route — the administrator's included — answers 503 PLUGIN_DISABLED.
whitelabel
enabled, company_name, four logo addresses (logo_light, logo_dark, favicon, favicon_dark), the sign-in page video and its cover for each panel (login_video_admin, login_poster_admin, login_video_client, login_poster_client; empty = the default video), six HSL colours (primary_color, secondary_color, nav_color and their _dark twins, "H S% L%" with H 0–360 and S, L 0–100) and hide_login_branding (default true). Edited on the Whitelabel page (account menu at the top right); every change writes a settings_updated audit row and rewrites config.json in the panel trees. While the whitelabel license addon is not active, the page's saves, logo and login video uploads and removals and reset are refused with 403 LICENSE_ADDON_REQUIRED; the values already in this block stay, and the public branding read answers active: false until the addon is active. A licence check that grants or withdraws the addon rewrites config.json at once, so the panel follows without a restart. A hand edit is published at the next agent start while the addon is active.
A logo field holds the address of a file the agent stores under /var/lib/whost/branding/ — /branding/<field>.<digest>.<png|jpg|webp|svg>, served by the panel's nginx (location ^~ /branding/ in /etc/nginx/snippets/whost-panel-locations.conf) with a one-year immutable cache and a sandboxing content policy; the name changes with the content, and a replaced or removed logo's file is deleted. Only such an address (or an inline data:image/ URL, see below) is published; any other hand-written value stays private. Uploads (2 MB per file) are shaped before they are stored: a raster logo to fit 512 × 160 px (width × height), a favicon to a square PNG of at most 256 px with transparent padding; an image above 25 megapixels is refused (400 IMAGE_DIMENSIONS_TOO_LARGE), a file that does not decode as its type too (400 INVALID_IMAGE); SVG is sanitised and kept. A sign-in video field holds /branding/login_video_<admin|client>.<digest>.mp4 — an MP4 whose video track is H.264, at most 5 MB, stored as uploaded (the panel compresses a larger video in the browser before it uploads it) — and its cover /branding/login_poster_<admin|client>.<digest>.<jpg|png|webp>, scaled to fit 1920 × 1080 (1080 × 1920 upright); each publishes only a file written for that field. Logos an earlier release stored inline as data: URLs move to files at the first agent start that finds the panel nginx serving /branding/: the original values are copied first to /var/lib/whost/branding-legacy-<timestamp>.yaml (mode 0600), the logos are shaped as uploads are, and the block is saved once; a value that does not decode stays as it was. Include /var/lib/whost/branding/ in host backups.
update preferences (not an agent.conf block)
agent.conf carries no update: block; an update: block left by an older install is not read by any code and is dropped the next time the agent writes the file. The update preferences live in /etc/whost/update_settings.json (root, 0600): channel (stable | beta), auto_update, auto_update_type (all | security | patch | minor), backup_before_update, notify_available, notify_installed, os_packages_check_enabled, os_packages_notify_security, allow_vendor_critical_override. auto_update_type decides what installs on its own: security only releases the vendor flags as critical (and only while allow_vendor_critical_override is on), patch a patch step, minor a minor or patch step, all every release; a vendor-flagged critical release also installs under the other types while allow_vendor_critical_override is on. The installer writes the file once on a new server with the channel of the release it installs (beta for a -beta.N release, stable otherwise) and leaves an existing file alone; afterwards the file is written by the Updates page (Settings tab, PUT /system/update/settings) and read by the install pipeline and the scheduler; an absent file means the defaults (auto-update of vendor-flagged critical releases on), a file that cannot be parsed is logged and treated as auto_update: false until it is repaired. The check cadence is fixed: five minutes after every agent start, then every 24 hours (up to 30 minutes of jitter).
Environment Variables
The agent reads its settings from this YAML file only; no key of this file can be set through an environment variable. The service unit (/etc/systemd/system/whost-agent.service) sets the environment the agent runs with — among it WHOST_CONFIG_PATH=/etc/whost/agent.conf, the path of this file — together with hardening switches. Keep the unit as installed: the installer and the agent's start-up permission check work on /etc/whost/agent.conf.
Applying Changes
Settings saved from the panel take effect at once: the agent writes this file itself and applies the change to the service concerned, as the tables describe. To change a key the panel does not offer (file in the tables):
- Keep a copy, for example
cp -p /etc/whost/agent.conf /root/agent.conf.bak— it holds the same secrets, so keep it at mode600. - Edit the file, then restart the agent before saving anything in the panel; until the restart, the next panel save writes the agent's in-memory settings back over the edit.
- Check that the agent came back:
curl -sk https://127.0.0.1:2000/healthanswers{"status":"ok",...}once the start has finished (a few seconds). If it does not, read the end of/var/log/whost/agent.log.
systemctl restart whost-agent
curl -sk https://127.0.0.1:2000/health
A hand edit changes what the agent reads; it does not rewrite the service configurations a panel save writes (web server tuning, MariaDB, Pure-FTPd, the Postfix message size, sshd, the nginx admin whitelist, DNS zones, the panel vhost). Change those keys from the panel. Every agent start verifies the license with the license server; avoid repeated restarts.
Developed by WISECP LLC. Contact: [email protected]
Our support team is here around the clock for anything you can't find above.