Cockpit vs Webmin for server management
Cockpit or Webmin on your Ubuntu VPS: what each panel can change, how each logs you in, why neither belongs on a public port, and when to use neither.
Cockpit vs Webmin: the short answer
Cockpit and Webmin are both web panels for managing a Linux server from a browser, and they answer different questions. Cockpit ships in your distribution's own repository and reads the machine through systemd, journald, polkit and udisks, so it shows you a server you still manage over SSH. Webmin is older and much broader: it writes configuration files for Apache, BIND, Postfix, MariaDB and dozens of other services that Cockpit never touches, and it runs its own web server as root to do that.
Install Cockpit when you want a live view of one box, a log reader, and an emergency terminal. Install Webmin when you need a form-based editor for a service you do not want to configure by hand. Put neither one on a public port with a password login. If you already run more than two or three servers, the honest answer is often neither, and the SSH plus Ansible path scales better than any panel.
What each panel can actually change
Cockpit's base install is small, and most areas are separate packages you can leave out:
- systemd services and timers: start, stop, enable, and read the unit file
- the journal, filtered by unit and priority, which is
journalctlwith a date picker - local accounts, group membership, and authorized SSH keys
- storage with
cockpit-storaged: partitions, LVM volume groups, filesystems and mount points - containers with
cockpit-podman, which manages Podman only - package updates with
cockpit-packagekit - graphs of CPU, memory, disk and network with
cockpit-pcp - a root terminal in the browser tab
Two areas look broken on an Ubuntu VPS and are not. Cockpit's Networking page is a front end for NetworkManager, and Ubuntu server images use netplan with systemd-networkd, so the page is missing or empty. Do not install NetworkManager on a remote box to get it back, because it takes over the interface and a mistake there costs you the SSH session too. Cockpit's firewall controls are a front end for firewalld, and Ubuntu uses ufw, so you get no firewall controls at all. You keep running sudo ufw status in a terminal.
Webmin covers far more ground, because it is a collection of per-service modules rather than one program:
- Apache, nginx, BIND, Postfix, Dovecot, MariaDB, PostgreSQL and Samba configuration through forms
- users, groups and disk quotas
- cron jobs and the system clock
- package updates, plus a file manager with upload and download
- firewall front ends, including one for iptables and one for firewalld
- configuration file backups, and cluster modules that push one change to other Webmin servers
Webmin edits the real files under /etc. There is no hidden database behind the forms, so if /etc is in version control, sudo git -C /etc diff after saving a form shows exactly what the module wrote. That is the fastest way to learn what any Webmin page really does. The Webmin install and first login walkthrough goes through the module tree in detail. Virtualmin and Usermin are separate products built on the same engine, for shared hosting and for end users, and they inherit everything said here about exposure.
How each one authenticates
Cockpit has no user database. Its login page runs the PAM (pluggable authentication modules) stack in /etc/pam.d/cockpit, so the accounts are your Unix accounts and the passwords are your Unix passwords. Root is refused by default because /etc/cockpit/disallowed-users lists it. Privileged actions go through polkit, and the interface asks for your password again before it changes anything, which is why the page header can read "Limited access" until you escalate.
That design has one consequence people meet on a hardened box. If you followed key-only SSH login with password authentication disabled, the account can have no usable password at all, so the Cockpit login is rejected while ssh still works. Check it on the server:
sudo passwd -S deployOutput starting deploy L means the password is locked, so PAM has nothing to accept and no password you type can work. P means a usable password is set. Cockpit's own login page does not accept SSH keys. Keys are used only when Cockpit connects onward from the machine you logged into to another host.
Webmin keeps its own users in /etc/webmin/miniserv.users, separate from /etc/passwd, and it can also be told to authenticate against Unix accounts. A Webmin user granted all modules is root on that machine, whatever their login shell says. Webmin ships its own TOTP (time-based one-time password) support and its own blocking of hosts after repeated failed logins, both switched on inside Webmin Configuration. Cockpit gets a second factor only if you add one to PAM, for example with libpam-google-authenticator.
How each one is updated
Cockpit is packaged by your distribution. On Ubuntu 24.04 it comes from the archive, and the upstream project recommends the backports pocket for a newer build:
. /etc/os-release
sudo apt update
sudo apt install -t ${VERSION_CODENAME}-backports cockpit
sudo systemctl status cockpit.socket
apt policy cockpitapt policy prints the version you installed and the repository it came from. If backports carries no newer build, apt falls back to the archive version, which is fine. cockpit.socket should read active (listening). Security fixes then arrive through the same unattended-upgrades run as your kernel, from a publisher you already trust.
Webmin is not in Ubuntu's archive. The official install adds Webmin's own repository and signing key first:
curl -o webmin-setup-repo.sh https://raw.githubusercontent.com/webmin/webmin/master/webmin-setup-repo.sh
sudo sh webmin-setup-repo.sh
sudo apt-get install webmin --install-recommendsRead that script before you run it, because it runs as root. From then on every apt upgrade on the server also pulls from Webmin's repository, so you have added a second publisher with root-level trust on the box. That is the real cost of Webmin, and it deserves a plain example: CVE-2019-15107 was a backdoor in several 1.9x packages that gave unauthenticated command execution, and it reached users because the project's build host was compromised, not its source repository. Distribution packaging does not make that impossible. It does add a build and review step you are not maintaining yourself.
Why neither belongs on a public port
Cockpit listens on TCP 9090 and Webmin on TCP 10000, both over TLS (transport layer security) with a self-signed certificate, so the first thing you see is a browser warning. Creating and trusting a self-signed certificate explains what that warning does and does not tell you. Both ports are scanned constantly, and both panels lead to root, so a guessed or reused password is a full compromise of the server.
The safe pattern is to bind the panel to localhost and reach it through an SSH tunnel. For Cockpit, override the socket unit:
sudo systemctl edit cockpit.socket[Socket]
ListenStream=
ListenStream=127.0.0.1:9090The empty ListenStream= on its own line is required. systemd appends to list settings, so without it the unit keeps the original 0.0.0.0:9090 and adds the new address, and your panel is still public. Apply the override and check what is listening:
sudo systemctl daemon-reload
sudo systemctl restart cockpit.socket
sudo ss -lntp | grep 9090The output must show 127.0.0.1:9090. An address of *:9090 or 0.0.0.0:9090 means the override did not take effect. Now open the tunnel from your own machine and browse to https://localhost:9090:
ssh -N -L 9090:127.0.0.1:9090 deploy@203.0.113.10Keep the local port equal to the remote port. Cockpit compares the browser's Origin header with the address it believes it is serving, so a tunnel from local port 9999 loads the login page and then fails at login, and journalctl -u cockpit records the rejected origin. If you need a different local port, name it in /etc/cockpit/cockpit.conf:
[WebService]
Origins = https://localhost:9999 https://127.0.0.1:9999Restart with sudo systemctl restart cockpit.socket to pick that up. For Webmin the equivalent setting lives in /etc/webmin/miniserv.conf:
bind=127.0.0.1sudo systemctl restart webmin
sudo ss -lntp | grep 10000
ssh -N -L 10000:127.0.0.1:10000 deploy@203.0.113.10Webmin also checks the Referer header on form posts and refuses requests that appear to come from another host, which is what breaks a first attempt at a reverse proxy. The referers= line in the same file is where you allow the proxy's hostname, and webprefix= is where you tell Webmin it lives under a path.
An authenticated reverse proxy is the other option: nginx in front, with an Authentik single sign-on layer doing the login. It works, and it is second best. The panel still runs as root behind the proxy, and you now maintain two front doors instead of one. A tunnel adds no listening service to the internet at all, and it reuses the SSH key you already protect.
Which panel on a box that already runs production services
Cockpit, for two reasons that matter when other people depend on the machine. It is socket activated, so cockpit-ws runs only while a session is open and there is no permanent root daemon waiting on a port. And it owns nothing: remove the package and every service keeps running exactly as before, because Cockpit stores no configuration of its own. Webmin's miniserv.pl stays resident whether or not anyone is logged in. Check what yours costs with systemctl status webmin, which prints the resident memory of the running process.
If you need Webmin's DNS or mail modules, give them a server of their own. A Webmin box that does one job, bound to 127.0.0.1, is a contained risk. Webmin sharing a host with your customer-facing application is not. Do the base work before either panel goes on: the first ten minutes on a new VPS covers the non-root user and the firewall that both panels assume is already there.
When the answer is neither
A panel is per-server and manual, and it leaves no record of what changed or why. That is fine for one box. At five boxes you are repeating yourself, and at twenty you are guessing which server missed the change. Cockpit can add other hosts to one session over SSH, but recent versions disable that by default and want AllowMultiHost=yes in /etc/cockpit/cockpit.conf, and it still leaves you clicking the same change five times.
The alternative is plain SSH with your configuration in a git repository. Managing several Linux servers from one place covers the shape of that setup, and a first Ansible playbook applies the same firewall rule to every host from one file you can review as a diff. Container work goes the same way: docker compose up -d over SSH from a file in git, as in the Docker Compose basics guide, beats clicking through any panel, and Cockpit does not manage Docker in the first place.
Use a panel for what a terminal is bad at, such as reading a metrics graph or spotting which of forty units failed. Use code for anything you will do more than twice.
Failure modes and the strings you will see
Cockpit rejects a password that SSH accepts. The account is key-only. sudo passwd -S alice prints L in the second field, so PAM has no password to check. Set one with sudo passwd alice, or keep that account for SSH and log in to Cockpit as another user.
Cockpit refuses root even with the correct password. /etc/cockpit/disallowed-users lists root. Log in as a normal user with sudo rights. That is the intended path, because polkit then records which human escalated.
Cockpit shows no Networking or Firewall page. Those pages need NetworkManager and firewalld. An Ubuntu VPS runs netplan with systemd-networkd and ufw, so the pages do not appear. Nothing is broken, and the fix is to keep using ufw over SSH.
The Cockpit login page loads through the tunnel, then login fails. Your local port differs from the remote port, so the Origin check fails and journalctl -u cockpit shows it. Match the ports, or set Origins in /etc/cockpit/cockpit.conf.
Webmin form posts fail after you put it behind a proxy. The Referer check rejects them. Add the proxy hostname to referers= in /etc/webmin/miniserv.conf, and set webprefix= when the panel is served under a path.
You are not sure whether a panel is exposed. sudo ss -lntp | grep -E '9090|10000' answers that from the server itself, and Webmin writes every login attempt to /var/webmin/miniserv.log, which is worth reading once after any change to how it listens.
FAQ
Is Cockpit or Webmin better for a single Ubuntu VPS?
For most people Cockpit, because it comes from Ubuntu's own repository, is patched with the rest of the system, and runs only while a browser session is open. Choose Webmin when you need a form-based editor for a service Cockpit does not touch, such as BIND or Postfix, and accept in exchange that its web server runs as root all the time and its updates come from Webmin's own repository.
Can I run Cockpit and Webmin on the same server?
Yes. They use different ports, 9090 and 10000, and they do not conflict, because each edits the system directly rather than owning it. It is still a poor trade. Each panel is a separate root-capable login on the same machine, so you double the exposure to save a few clicks. If you install both, bind both to 127.0.0.1 and reach them over an SSH tunnel.
Is it safe to open port 9090 or 10000 to the internet?
Not with a password login. Both panels lead to root, and both ports are found by routine scanning within hours of opening. Bind the panel to 127.0.0.1, then run ssh -N -L 9090:127.0.0.1:9090 user@host and browse to https://localhost:9090. Confirm with sudo ss -lntp | grep 9090, which must show 127.0.0.1:9090 rather than 0.0.0.0:9090. An authenticated reverse proxy is an acceptable second option.
Why does my Cockpit login fail when SSH with a key works?
Cockpit authenticates through PAM with a Unix password, and its login page does not accept SSH keys. On a hardened server the account often has no usable password. Run sudo passwd -S youruser: an L in the second field means the password is locked, so PAM has nothing to accept and every attempt is rejected. Set a password with sudo passwd youruser, or use a different account for the panel.
Does Cockpit manage Docker containers?
No. Cockpit's container page comes from cockpit-podman and manages Podman. The old Docker module was dropped years ago and is not coming back. If your services run under Docker, manage them with a compose file in version control over SSH, and let Cockpit handle the system around them, such as the journal and the disks.