---
title: How to List All Hosted Domains from the Command Line in Webmin and Virtualmin
url: https://ilogr.com/how-to-get-list-of-all-hosted-domains-using-command-line-in-webmin/
published: 2019-10-04
updated: 2026-11-02
author: iLogr Team
categories: Linux
reading_time: 7 min
description: Run virtualmin list-domains --name-only as root to list every hosted domain. Note that --alias keeps only aliases. Plus CSV export and Apache or nginx.
---

# How to List All Hosted Domains from the Command Line in Webmin and Virtualmin

**In short:** On a server running Virtualmin, run the list-domains command with –name-only as root and it prints every hosted domain, aliases included; the –alias option does the opposite and keeps only aliases. Without Virtualmin, read the names from the Apache or nginx configuration.

Webmin itself has no list of hosted domains. The list belongs to Virtualmin, the hosting layer built on Webmin [1], and its `virtualmin` command prints it. This guide gives the command, the filters that narrow it, a way to export the result to a spreadsheet, and what to do on a server that has Webmin but no Virtualmin. Tested on: the Apache and nginx commands were run on Apache 2.4.69 and nginx 1.30.6. The Virtualmin commands are checked against the Virtualmin 8.3.0 source and documentation and were not run on a live Virtualmin server.

**Before you run anything:** none of these commands changes Virtualmin or web-server settings, but the CSV export creates or overwrites a file called domains.csv, and the detailed Virtualmin output can contain passwords in plain text (see the warning in the export section). Run them at your own risk. iLogr cannot see your server and accepts no responsibility for what happens on it.

## How do you list all domains in Virtualmin from the command line?

Log in to the server over SSH as root and run:

```bash
virtualmin list-domains --name-only
```

The command must run as root [2][3]. You get one domain name per line, for every kind of virtual server: top-level domains, sub-servers, sub-domains and alias domains [3][4]. The order is by owner username and then by creation time, not alphabetical [4], so add `| sort` if you want an alphabetical list:

```bash
virtualmin list-domains --name-only | sort
```

Without `--name-only`, the command prints a table with a Domain, Username and Description column. That table cuts each column off after 30, 15 and 30 characters [4], so a long domain name comes out shortened. Use `--name-only` whenever the names themselves matter.

## Does the –alias option include alias domains?

No. It does the opposite, and an earlier version of this guide had it wrong. `--alias` is a filter that keeps only alias domains [4]. This command therefore lists only the aliases on the server:

```bash
virtualmin list-domains --name-only --alias
```

Plain `--name-only` already includes aliases. If you want every domain except the aliases, use `--no-alias`.

## Which options narrow the list?

Each option below is a filter, and filters combine: a domain has to pass all of them [4]. Virtualmin's own page for the command lists the full set [3].

| Option | Keeps |
| --- | --- |
| `--toplevel` | top-level virtual servers only: no sub-servers, sub-domains or aliases [4][5] |
| `--no-alias` | everything except alias domains |
| `--alias` | alias domains only; `--alias example.com` keeps the aliases of that one domain |
| `--user NAME` | domains owned by one account |
| `--enabled` or `--disabled` | active or suspended domains |
| `--with-web` or `--with-ssl` | domains that have a website, or an SSL website |
| `--with-feature dns` | domains with a given feature switched on, such as `dns` or `web` |
| `--plan NAME` | domains on one hosting plan |
| `--ip ADDRESS` | domains using that IPv4 or IPv6 address |

![Four domain types, top-level, sub-server, sub-domain and alias, against four options. With no option all four are listed; --toplevel keeps only top-level domains; --no-alias keeps everything except aliases; --alias keeps only alias domains.](https://ilogr.com/wp-content/uploads/2019/10/virtualmin-list-domains-options-by-domain-type.webp)

*Which option keeps which kind of domain, as Virtualmin 8.3.0 behaves.*

For example, the active top-level domains, in alphabetical order:

```bash
virtualmin list-domains --name-only --toplevel --enabled | sort
```

## How do you export the list to a spreadsheet?

Names alone paste straight into a spreadsheet. For more columns, ask for JSON and let `jq` (install it from your distribution's packages if it is missing) turn it into CSV:

```bash
virtualmin list-domains --json | jq -r '.data[] | [.name, .values.type[0], .values.username[0]] | @csv' > domains.csv
```

This writes one row per domain with the name, the type (Top-level server, Sub-server, Alias) and the account username, quoted so that commas inside a value do not break the file [6][7][8]. `--json` is in Virtualmin 8.3.0 [6]; on an older release, run `virtualmin list-domains --help` to see whether your version has it. JSON output needs the `JSON::PP` Perl module [7]. Under the hood, `--json` is the plain `--multiline` output turned into a data structure, with each label written in lower case and underscores [7]; that is why the type sits at `.values.type`.

**Warning about passwords.** The detailed `--multiline` and `--json` output can include the domain's password and its database passwords, in plain text when the server stores them that way [4]. Keep the `jq` filter above (it picks three fields), do not save the raw output, and do not paste it into a ticket or a chat.

From another machine, the remote API can return the same data over HTTPS. Pass `program=list-domains`, `multiline=1` and `json=1`; with `json=1` alone you get the plain table inside a single text field, not the structured list [7][9]. The documentation describes master-administrator authentication, while the 8.3.0 source also lets resellers and domain owners with the remote API permission list their own domains [6][9]. Check what your installed release allows, and protect the credentials.

## How do you list domains without Virtualmin?

Webmin alone does not know about hosted domains; the names live in the web server's configuration. Inspect the web server's configuration files.

**Apache.** On Debian and Ubuntu use `apache2ctl -S`, elsewhere `httpd -S` or `apachectl -S`. It prints the virtual hosts parsed from the configuration files [10]. To pull out only the names, one per line:

```bash
apachectl -S 2>&1 | awk '/\(.*:[0-9]+\)$/ { for (i = 2; i <= NF; i++) if ($i ~ /^\(/) { print $(i - 1); break } } /^ +alias / { print $2 }' | sort -u
```

The report's layout varies. An address and port with several virtual hosts lists each as `namevhost` with its aliases underneath, but an address and port with a single virtual host appears as one plain line, and in a test its `ServerAlias` names were not printed at all. The filter above handles both layouts, yet an empty result does not prove that Apache serves no site, and a lone virtual host's aliases are only in its configuration file.

**nginx.** `nginx -T` tests the configuration and prints the configuration files, including the files they include [11]. This filter joins the lines, cuts the text at each `;`, `{` and `}`, and prints the names after every `server_name`, so names split over several lines and directives sharing a line are both caught:

```bash
nginx -T | sed 's/#.*//' | tr '\n' ' ' | tr ';{}' '\n\n\n' | awk '$1 == "server_name" { for (i = 2; i <= NF; i++) print $i }' | sort -u
```

The test messages and any configuration error stay visible in the terminal, because only the configuration text goes through the filter. If the test fails, nginx prints no configuration and the list is empty, so fix the error first. This is a text filter, not a parser: a regular expression name that contains braces, for example, will be cut short.

**What the output looks like.** In a test with two virtual hosts and three names for the first one, both commands printed the same four names. Three things needed care. Apache printed a lone virtual host on a port of its own as a plain line, without its aliases. A line-by-line nginx filter missed names split over several lines and directives that shared a line; the joining filter above caught both, and the names from an included file. And a server block with `server_name _;`, another with a wildcard and a regular expression, added `_`, `*.example.net` and `~^(?<user>.+)\.example\.io$` to the list [12]. Those are catch-all, wildcard and pattern entries, not hosted domains, so remove them by hand. These commands read configuration files on disk, which can differ from the configuration currently serving requests after an edit or a failed reload. Files outside the configuration's include tree will not appear.

## What can you do with the list next?

A list of names is a starting point. Two checks make it useful before a clean-up or a move to a new server:

- **Which names still point at this server?** Compare each name's address with your server's. This loop queries each top-level domain and prints the name followed by the last line of its short A-record answer; an empty answer leaves the address blank [13]. It does not show every address when a name has several A records, and it does not check IPv6:

```bash
virtualmin list-domains --name-only --toplevel | while read -r d; do
  printf '%s %s\n' "$d" "$(dig +short A "$d" | tail -n 1)"
done
```

- **Which accounts are switched off?** `virtualmin list-domains --name-only --disabled` lists the suspended ones.

DNS only shows where a name points. A name that points elsewhere is probably unused here, but a name that points here may still have no visitors, so do not delete anything on the strength of the list alone.

If you are working in the Webmin interface as well, our guides to [checking a directory's size in Webmin's File Manager](https://ilogr.com/check-size-of-directory-in-webmin-file-manager/) and [enabling the HTML File Manager module](https://ilogr.com/enable-html-file-manager-module-webmin-users/) cover two other everyday jobs.

**Next step:** Run the name-only command once with no filter and once with `--toplevel`, then compare both counts with the sites you expect; the difference is your aliases and sub-servers.

## References

1. [Virtualmin: home page](https://www.virtualmin.com/). Used for: the fact that Virtualmin is built on Webmin.
2. [Virtualmin documentation: Command Line API](https://www.virtualmin.com/docs/development/command-line-api/). Used for: running the command-line programs as root.
3. [Virtualmin documentation: list-domains](https://www.virtualmin.com/docs/development/api-programs/list-domains/). Used for: the options and what each one does.
4. [Virtualmin 8.3.0 source: list-domains.pl](https://github.com/virtualmin/virtualmin-gpl/blob/8.3.0/list-domains.pl). Used for: filter behaviour, ordering, table width and password output.
5. [Virtualmin 8.3.0 source: create-domain.pl](https://github.com/virtualmin/virtualmin-gpl/blob/8.3.0/create-domain.pl). Used for: why alias domains are not top-level.
6. [Virtualmin 8.3.0 source: commands-lib.pl](https://github.com/virtualmin/virtualmin-gpl/blob/8.3.0/commands-lib.pl). Used for: the –json and –xml options, and the remote API permission checks.
7. [Virtualmin 8.3.0 source: json-lib.pl](https://github.com/virtualmin/virtualmin-gpl/blob/8.3.0/json-lib.pl). Used for: the structure of the JSON output.
8. [jq manual](https://jqlang.github.io/jq/manual/). Used for: how the @csv format quotes values.
9. [Virtualmin documentation: Remote API](https://www.virtualmin.com/docs/development/remote-api/). Used for: json=1, multiline=1 and the documented administrator login.
10. [Apache HTTP Server documentation: httpd](https://httpd.apache.org/docs/2.4/programs/httpd.html). Used for: what the -S option prints.
11. [nginx documentation: command-line parameters](https://nginx.org/en/docs/switches.html). Used for: the -T option that dumps the configuration.
12. [nginx documentation: server names](https://nginx.org/en/docs/http/server_names.html). Used for: wildcard and regular expression names in server_name.
13. [BIND 9 manual: dig](https://bind9.readthedocs.io/en/latest/manpages.html). Used for: the +short option.
