Skip to content

Blog ·

Virtualmin billing automation: what a Virtualmin billing panel has to do

From order to live Virtualmin account, and what an unpaid invoice actually does: suspend, resume and delete, with the panel commands behind each step.

By Provibr

There is no Virtualmin billing panel in the box, so whatever turns a Virtualmin account into a paying customer is a layer you add. Two questions decide whether that layer can carry paying customers at all: does an order become a working account without you touching it, and does an unpaid invoice actually change anything on the server.

This page answers both for Virtualmin, in the concrete: which Virtualmin command sits behind each step, what gets stored where, and where the automation deliberately stops. Everything was measured against a real Virtualmin GPL 8.1.2 install.

From order to account

An order needs four things: a domain, an email address, a plan and a password.

The plan is the interesting one. A Provibr web plan pins exactly one Virtualmin plan on exactly one integration and gives it a name of your own. That indirection exists because a Virtualmin plan is the panel's own description of an account: disk quota, monthly traffic, mailbox and domain counts, which features are on. Its contents can change on your box without us being told. So we refer to it by name and read back what actually happened, rather than caching what we think it means.

Behind the scenes the order runs create-domain on the panel with the domain, the plan and the password. Then:

Provibr commandVirtualmin program
Create accountcreate-domain
Suspenddisable-domain
Resumeenable-domain
Change planmodify-domain --plan
Read statuslist-domains --domain
Deletedelete-domain

Two details of the create step are worth knowing.

The unix user comes from the domain. my-client.nl becomes my-client. Hyphens are the normal form here, and the ceiling is Linux's own 32 characters, unlike DirectAdmin and cPanel, which impose stricter naming of their own. You do not pick the username; the panel derives it.

The password is never recorded. Provibr stores the fact that you supplied one, not the value. The password travels separately from the audit trail that records the order, so a hosting account password does not exist anywhere in the platform's history. If you lose it, you reset it; you do not look it up.

What gets stored where

A hosting account becomes an ordinary server row of type webserver. Its identifier is the internal number Virtualmin gives the domain, not the name.

That choice is deliberate and it matters for billing. Virtualmin can rename a domain, and a customer or an admin will eventually do it. A name-based key comes loose from its row the first time that happens: your invoice keeps charging for something the platform can no longer find. A numeric id survives the rename.

The account boundary is the unix user, not the domain. Sub-servers and aliases under one account would otherwise each land in the capacity count as separate hosting accounts, and you would be paying licence capacity for aliases.

⚠️ There is a translation step here that is easy to get wrong if you build this yourself. Virtualmin's mutation programs (disable-domain, enable-domain, modify-domain) take only --domain as a selector. We measured that on 8.1.2: there is no --user selector. But the platform addresses accounts by username, because that is the account's identity. The translation belongs on the agent, which does one scoped lookup on the panel to resolve the current domain, rather than on the platform, which would have to keep a copy of your domain names and would silently target the wrong domain the first time somebody renames one outside Provibr.

(Related trap: modify-domain --user means rename, not select. It is never sent.)

The capacity model, in one paragraph

You buy capacity per resource type: virtual machines, dedicated machines, hosting accounts and game servers. It is a limit and a price at the same time. Every account counts, whether it is running or not. Only a suspended account does not, and neither does a deleted one. Stopping something therefore does not free a place: remove it, or raise your capacity. Raising takes effect immediately and the extra for the rest of the period is prorated onto your next invoice; lowering also takes effect immediately, and the unused part comes back as credit.

Imported accounts count exactly like ordered ones. Adopting a panel full of existing customers is a capacity decision, not a free action.

What an unpaid invoice does

Your organisation carries one licence status: active, grace or suspended. Two separate things can push it (an outstanding payment and an overage), and when both apply the stricter one wins, so the banner does not alternate between two stories.

The trial path is the clearest example of the shape. A new organisation runs on a two-week trial with no payment details asked for and everything working. When it ends without a payment method you are told, and nothing changes yet. A week later the panel starts warning. Two weeks after the period ended, servers are suspended. Every step is announced before it happens. That is the rule the whole schedule is built around, not a courtesy bolted onto it.

For a running subscription the mechanism is the same: the status moves to grace before it moves to suspended, the banner names the cause, and the fix is linked from it. A suspension for payment and a suspension for capacity look and read differently, because they need different things from you.

What suspension does on the panel

When the platform suspends a hosting account, it sends disable-domain. Virtualmin locks the administration user's password and replaces the website with an error page. The data stays.

Three properties of that flow are worth spelling out, because they are the ones that separate a real automation from a script that looks like one.

The row is marked suspended only after the agent confirms it. Sending a command is not the same as it having happened. If the platform marked the row on dispatch, a failed suspend would leave a running account counted as "does not count towards your limit", which is the exact accounting error the enforcement exists to prevent.

An offline agent means deferred, not done. If the agent cannot be reached, the enforcement is skipped, an event is written saying so, and the next round tries again. Nothing is quietly marked complete.

Reading the result is a fresh read, not the command's own answer. disable-domain returns a wall of progress text ("Locking administration user's password ..", "Replacing website with error page ..") and that text is a quotation, not a contract. The state comes from a fresh list-domains afterwards. This is a house rule across every panel we integrate: no mutation is trusted on its own answer.

That last rule is what makes the suspend detection correct at all, and here is the measured reason. 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 looking for a value finds nothing and reads every account as active, including the suspended ones. In a billing context that is not cosmetic: an account you suspended for non-payment would report itself as live, and your capacity count would be wrong in the direction that costs you money.

Going over your limit

Running more accounts than you bought is not blocked at the moment you do it. It is detected, you are told, and you are given time.

  1. The excess is detected. You get a notification and a banner naming the resource type and by how much.
  2. You have a full working day to fix it: remove accounts, or raise your capacity.
  3. If the grace period expires, the platform suspends the newest accounts above your limit. Newest first, so what you built earliest survives. A suspended account keeps its disk and its data.
  4. Once you are back within your limit the overage closes.

A second overage within a week gets an hour instead of a day. Without that, the grace period could be renewed indefinitely by dipping under the limit and going over again.

⚠️ Nothing is started for you when the overage resolves. Switching an account back on is a decision, and the platform does not make it while nobody is watching. You resume it yourself, and that sends enable-domain.

There is a small piece of arithmetic behind step 4 that is easy to get backwards. The overage counts as resolved when accounts that count plus accounts suspended by this overage is back within the limit, not just the first number. Without that addition the enforcement would declare itself resolved the instant it acted, because the count drops below the limit precisely by suspending.

What is never automatic

Deleting. No unpaid invoice, no overage and no expired trial deletes a hosting account. Suspension keeps the disk and the data; deletion is a separate, explicit action you take. The one destructive step in the lifecycle is the one the platform will not take on your behalf.

Partial settlement from credit. You can top up in advance, and the balance is a ledger. One line per change, never edited; corrections are opposite lines with a reason rather than rewrites. If the balance covers the whole month, the month is paid from credit and nothing is collected. If it does not, the full amount is collected and the balance is left alone. There is no half-and-half.

Rewriting history. An invoice line records the plan name as it was at the moment of invoicing. Renaming something later does not change what an old invoice says.

Where this leaves you

Virtualmin gives you a solid hosting panel and no commercial layer at all. The gap is not the provisioning, because create-domain is one command. It is everything around it: counting what a customer has, knowing an invoice went unpaid, doing something about it that is reversible, and being able to explain afterwards what happened and when.

Provibr connects to control panels, hypervisors and game panels side by side. The billing model above is the same across all of them, which is the practical argument for a small host: the Virtualmin box you already run keeps working exactly as it does, and the VPS or game panel you add next year lands in the same capacity count, the same invoice and the same suspend flow.

Frequently asked questions

Does Virtualmin have a billing panel of its own?

No. Virtualmin gives you a solid hosting panel and no commercial layer at all. The gap is not the provisioning, because create-domain is one command; it is counting what a customer has, knowing an invoice went unpaid, and doing something about it that is reversible.

What happens on the panel when an account is suspended?

The platform sends disable-domain. Virtualmin locks the administration user's password and replaces the website with an error page, and the data stays where it is.

Does an unpaid invoice delete a hosting account?

Never. No unpaid invoice, no overage and no expired trial deletes an account. The one destructive step in the lifecycle is the one the platform will not take on your behalf.

How is a suspended Virtualmin account detected reliably?

By reading the state back rather than trusting the command's own answer. On a suspended account list-domains grows a disabled field; on an active account that field is simply absent, so a parser looking for a value reads every account as active, including the suspended ones.

Does a suspended account still count towards my capacity?

No. Every account counts whether it is running or not, and only a suspended or a deleted one does not. Stopping something therefore does not free a place: remove it, or raise your capacity.

Start with one host and one agent

Create an account, install the agent with a single command and connect your first Proxmox host. The trial runs for 14 days and asks for no payment details.