Blog ·
Selling web hosting with Virtualmin
Sell hosting on Virtualmin: which Webmin user and password to use, why the TLS fingerprint is mandatory, how plans map, and when quota fields stay empty.
By Provibr
Virtualmin is one of the oldest control panels still in active use (it has been around since 2004), and it has an installed base that GitHub does not show you. On 21 August 2026 the repository had 412 stars, 129 forks and 46 contributors, which is small by GitHub standards and says almost nothing about how many small hosters actually run it.
What it does say is that there is very little written about selling on it. This page is that: how Provibr connects to a Virtualmin box, which decisions the panel forces on you, and where the sharp edges are. Everything below was measured against a real Virtualmin GPL 8.1.2 install on Ubuntu 24.04.
Webmin is the panel, Virtualmin does the hosting
You know the product as Webmin. That is the name on the login page, in the documentation and in every search. But plain Webmin manages a Linux machine; it has no notion of a hosting account at all. What Provibr actually drives is Virtualmin: the module that knows about domains, plans and quotas.
This matters more than it sounds. On a box without the module, Virtualmin's remote API endpoint simply is not there: it answers exactly like a module name you invented. We measured that in step zero of the research, before installing anything. So there is no falling back on plain Webmin, and Provibr does not pretend there is: the connection is refused with a reason rather than half-created.
What you need to connect
Four fields, and two of them are stricter than for any other control panel we support.
| Field | Value |
|---|---|
| Host | The panel address. Must be https. Port 10000 is filled in for you. |
| Webmin user | root works, but a user that only has the Virtualmin module is better. |
| Password | The password of that user. |
| TLS fingerprint | Required. |
Why https and the fingerprint are both mandatory. Our other control panel integrations accept a plain http address and tell you what that costs. This one does not, and the difference is the nature of the secret.
Webmin's remote API speaks HTTP Basic. The secret here is a password, not a token. That is a property of Webmin, not a choice of ours. A DirectAdmin login key can be limited to a handful of commands; a Webmin password cannot be limited at all. Sending that unencrypted, or to a certificate nobody verified, is not a trade-off we make on your behalf. And Webmin's installer leaves a self-signed certificate behind, so the fingerprint field is not theoretical: it is the normal case.
Practical advice: create a dedicated Webmin user and give it the Virtualmin module. It can do everything Virtualmin can do, which is a lot, but not everything Webmin can do.
Two things that will bite an integration
These are the two measured behaviours most likely to cost you an afternoon if you build this yourself.
Every error arrives with HTTP 200. Virtualmin has two failure shapes (error at the wrapper level and failure at the program level) and delivers both with a 200 status code. The status code tells you nothing. Anything that is not exactly status: "success" is a failure, and the wrapper error has to be checked before any text comparison, otherwise "Remote program xyz does not exist" reads like a missing account.
(This is a family trait of control panel APIs, not a Virtualmin quirk. DirectAdmin does the same thing differently: HTTP 200 with error=1 in the body. An integration that trusts the status code succeeds silently while nothing happened.)
A UI path answers with 200 and a login page. If you point at a Webmin module path instead of the remote API, you get HTTP 200 with an HTML login page, for a wrong password, a correct password and no password alike, all three indistinguishable by status code. The remote API itself does it properly with a 401. That difference is what makes the error usable: a 200 with HTML can only mean you are not aimed at the API, and Provibr says so by name instead of showing you a JSON parse error on a panel that works fine.
Some installations have the remote API route switched off. You turn it back on under Webmin → Webmin Configuration → Advanced Options.
Settings: there are none
A Virtualmin server decides for itself where an account lives and which address it gets. There is nothing left for you to configure, so the settings tab carries nothing but the option to resend credentials.
That includes the IP address. Virtualmin puts an account on the host's shared address by default; we measured ip_address coming back as 192.0.2.10 (Shared). Provibr's IP management therefore stays off for this provider. An IPAM that suggested it was assigning the address would be lying.
Plans
A Virtualmin plan is the panel's own description of an account: disk quota, monthly traffic, how many domains and mailboxes, which features are on. It is not a Provibr plan and it is not an operating system. It is a name we refer to, and its contents can change on your server without us noticing.
The integration page reads the list of plans with their contents, so you can see what you are choosing rather than typing a name from memory. A Provibr web plan then pins one Virtualmin plan on one integration and gives it a name of your own. Ordering through a plan beats picking a loose one, and the loose picker stays available for the cases a plan does not cover.
⚠️ One measured trap worth knowing: modify-domain with a plan name that does not exist answers status: "success" and leaves the plan exactly where it was. There is no error shape to classify: the refusal is silence. Provibr compares what it asked for against what it reads back afterwards, so a plan change that did not happen is reported as one that did not happen.
What an account looks like in Provibr
A hosting account is an ordinary server row of type webserver. Creating one needs a domain, an email address, a plan and a password.
- The identifier is the internal number Virtualmin gives the domain, not the name. Virtualmin can rename a domain; a name-based key would come loose from its row the first time somebody does that.
- The unix user is derived from the domain, so
my-client.nlbecomesmy-client. Hyphens are the normal form here, not the exception, and the limit is Linux's own 32 characters. That is unlike DirectAdmin and cPanel, which have their own stricter naming rules. - The account boundary is the unix user, not the domain. Sub-servers and aliases would otherwise each land in the licence count as separate hosting accounts.
- The password is not recorded anywhere. Only the fact that you supplied one is; the value travels separately and is never written to an audit row.
- Afterwards you can suspend, resume, change the plan and delete. There is no console, because a hosting account does not have one.
Suspended means a field exists
This is the sharpest detail of the whole integration, and it is the one a hand-rolled parser gets wrong.
On a suspended account, list-domains grows a field called disabled. On an active account that field is simply absent. There is no disabled: "No".
A parser that looks for a value never finds one, and reads every account as active, including the suspended ones. In a billing context that is not a cosmetic bug: it means an account you suspended for non-payment reports itself as live, and your capacity counting is wrong in the direction that costs you money.
Quota and usage: sometimes there is nothing to show
Disk quota and traffic ride along with the regular poll, so an account gets a usage graph without anything extra being switched on, provided the server can measure them.
Virtualmin only reports quota and usage when filesystem quotas are switched on, and a fresh installation has them off. We measured it: quotaon -p / reported off on a stock install, because the installer writes the old quota format into /etc/fstab while a modern ext4 kernel wants journaled quota. Until somebody turns them on, no quota field appears in the API output at all.
Provibr shows nothing rather than a zero. No measurement and no usage are not the same thing, and a graph pinned at zero looks like the second when it is the first.
A related note if you ever read the raw output yourself: prefer the machine-readable fields (created_unix_time, databases_byte_size) over their human twins (created_on, databases_size). The human ones carry units and a date format that depend on the panel's locale, and parsing them is exactly the kind of silent failure that shows up first at a customer with a different setting.
Two deliberate omissions
The connection test does not display a version line. Virtualmin has no API program that returns its own version: virtualmin info knows twenty-one lookup keys and none of them is a version, and the Server: MiniServ header carries no number. An empty line would claim we tried and failed to read it; leaving it out says the truth. The test shows your plan list instead, which is what you actually want to confirm.
And nothing secret from the panel is ever stored. list-domains --multiline carries hashed_password, password_for_mysql and password_storage literally in its output. Provibr filters with an allowlist before reading, not by redacting afterwards, because a deny-list misses the next secret that gets a different name. We verified it with a counter-test in the same run: the raw panel answer demonstrably carries those three fields, and the import list contains none of them.
Adopting accounts that already exist
The import tab lists the accounts on the panel and lets you take the ones you want into Provibr. Accounts already known are shown but cannot be selected again, so you see the whole picture rather than a filtered one.
Imported accounts count towards your capacity exactly like accounts you ordered. Import more than you have room for and an overage opens, with the usual grace period.
Where this leaves a small host
Virtualmin is a good panel with almost no commercial tooling around it. The usual advice is to move to something the billing systems already speak, which asks you to migrate live customers off a working box to solve a paperwork problem.
Provibr connects to control panels, hypervisors and game panels side by side. Keep the Virtualmin box you have, put ordering and capacity counting on top of it, and add a VPS or a game panel next to it later without changing anything about how the hosting side works.