Webmin on Ubuntu 24.04: install and secure
Install Webmin on Ubuntu 24.04, then lock it down: SSH tunnel or IP allowlist, a real Let's Encrypt cert, two-factor, and Fail2ban on port 10000.
What you are building
Webmin is a web control panel that puts a browser front end on a whole Linux server — users, packages, cron jobs, the firewall, Apache, BIND, disks, and a hundred other modules — reachable over HTTPS on port 10000. The install itself is three commands and takes about a minute. The reason this guide spends most of its length on the second half is that Webmin logs in as root and can do anything root can do. A wide-open or unauthenticated Webmin is not "a risk to manage"; it is a full server compromise with a login page in front of it. So install it in ten minutes, then spend the rest of the hour making sure only you can reach it.
Prerequisites and one honest warning
A fresh Ubuntu 24.04 KVM VPS with root or a sudo user. Webmin is Perl and light — 1 GB of RAM is plenty and it idles at a few hundred megabytes. It listens on TCP 10000 through its own bundled web server (miniserv.pl), not Apache or nginx, so nothing else has to be installed first.
Two things to decide before you start. First, the account you log in with: Webmin authenticates against Unix accounts through PAM, so you log in as root or a user in the sudo group using that account's Unix password. Cloud images are usually key-only with no password set on the default user, and Webmin cannot log in a passwordless account. Set a password with sudo passwd youruser first, or the login will fail no matter what you type.
Second, and this is the warning: do not simply open port 10000 to the internet and walk away. Decide now whether you will reach Webmin over an SSH tunnel (recommended, nothing exposed) or restrict it to your own IP. That single decision shapes every step below, so read both options before you touch the firewall.
Install Webmin from the official repository
Webmin publishes a signed apt repository. The setup script adds the repo and its GPG signing key so future apt upgrade runs pull Webmin like any other package, and you get authenticated updates instead of a downloaded .deb that never patches itself.
curl -o webmin-setup-repo.sh https://raw.githubusercontent.com/webmin/webmin/master/webmin-setup-repo.sh
sudo sh webmin-setup-repo.sh
The script prints what it is about to do and asks Setup repository? (y/N) — answer y. When it finishes, install the package. The --install-recommends flag pulls the common Perl and SSL module dependencies so individual modules do not fail later with missing-library errors.
sudo apt-get install --install-recommends webmin
Webmin has renamed this script before — older guides reference setup-repos.sh — so if the raw URL returns a 404, grab the current one-liner straight from webmin.com/download rather than pinning a name. A correct install ends with a line like Webmin install complete. You can now login to https://your-host:10000/ as root. The service is enabled and running, config lives under /etc/webmin, requests are logged to /var/webmin/miniserv.log, and failed logins go to syslog — on Ubuntu 24.04, the systemd journal.
Confirm it is actually up and listening before you open a browser:
sudo systemctl status webmin --no-pager
sudo ss -tlnp | grep 10000
You want an active (running) status and a line showing miniserv.pl bound to 0.0.0.0:10000 — that address changes to 127.0.0.1:10000 once you take the tunnel route below. If ss shows nothing on 10000, Webmin did not start; read journalctl -u webmin -n 50 before going further.
First login, and the certificate warning
Point your browser at https://YOUR_SERVER_IP:10000. Two things happen on a fresh box.
If ufw is active — Ubuntu's own server image ships it inactive, but many providers pre-enable it — the page will not load at all, which is covered in the failure modes below. If the port is open, your browser throws a full-page block: "Your connection is not private" with the code NET::ERR_CERT_AUTHORITY_INVALID in Chrome, or SEC_ERROR_UNKNOWN_ISSUER / "Warning: Potential Security Risk Ahead" in Firefox. This is expected and not a break-in. Webmin generated a self-signed certificate at install time (/etc/webmin/miniserv.pem), and because no certificate authority vouches for it, the browser refuses to trust it silently. The connection is still encrypted; it simply is not vouched-for. Click through (Advanced, then Proceed) for now — we replace this certificate properly further down.
Log in with root or your sudo user and its Unix password, and you land on the System Information dashboard. One common slip here is typing http:// instead of https://. miniserv answers plain HTTP on that port with the exact text "This web server is running in SSL mode. Try the URL https://..." — the fix is literally to change http to https in the address bar.
The security decision: how will you reach Webmin?
Now the part that matters. A root-equivalent panel should not sit on the open internet answering login attempts from every scanner on the planet. You have two defensible options, in order of preference.
The SSH tunnel in Option B is the better one, because it exposes nothing at all. The IP allowlist in Option A is acceptable if your address is static. Doing neither is exactly the mistake this whole guide exists to prevent. If you would rather reach Webmin over a private network than either, put the box behind a self-hosted WireGuard VPN and bind Webmin to the tunnel address instead of the public one.
Option A: restrict Webmin to your IP
In the panel, open Webmin, then Webmin Configuration, then IP Access Control. Choose "Only allow from listed addresses" and enter your public IP, which you can find by running curl ifconfig.me on your laptop. Save. Webmin writes this to the allow= line in /etc/webmin/miniserv.conf and restarts itself.
The trap: if your home IP is dynamic and changes, or you fat-finger the address, you lock yourself out. The browser then shows "Access denied for <your IP>" and no login form, and there is no web path back in. You fix it from the server console over SSH or your provider's VNC:
sudo nano /etc/webmin/miniserv.conf
# find the line that begins allow=
# correct your IP, or delete the whole line to allow all again
sudo systemctl restart webmin
Deleting the allow= line restores fully open access, so only do that to recover, then immediately set a correct value.
Option B: bind to localhost and tunnel over SSH (recommended)
Better than any allowlist is to not listen on the public interface at all. Tell miniserv to bind only to loopback, then reach it through an encrypted SSH tunnel you already trust.
Edit /etc/webmin/miniserv.conf and add, or change, one line:
bind=127.0.0.1
Restart with sudo systemctl restart webmin. Webmin is now unreachable from the internet — a port scan of 10000 finds nothing, and the ss check from earlier now shows it bound to 127.0.0.1:10000. From your laptop, open a tunnel:
ssh -L 10000:localhost:10000 youruser@YOUR_SERVER_IP
Leave that session open and browse to https://localhost:10000. The traffic rides inside SSH, which is already authenticated by your key and encrypted, so you can safely accept the self-signed certificate here — the SSH layer is doing the real protection. Close the SSH session and Webmin is gone. Nothing to allowlist, nothing exposed, no extra service to harden.
If you set bind=127.0.0.1 and forget the tunnel, remote access simply stops — that is the feature working, not a fault. Recover the same way as Option A: from the console, remove the bind line or set bind=0.0.0.0, then restart. This is the same tunnelling habit you would use to reach a remote development box running Claude Code in tmux — one SSH session, everything private, nothing extra listening on the public interface.
Replace the self-signed certificate with a real one
If you do expose Webmin on a hostname under Option A, get rid of the browser warning with a real Let's Encrypt certificate. You need a DNS name — say panel.example.com — with an A record pointing at the server, and something answering the port-80 HTTP challenge during validation.
Webmin has this built in: Webmin, then Webmin Configuration, then SSL Encryption, then the Let's Encrypt tab. Enter the hostname, point the "website root directory" at a path that is served on port 80 for that domain, and request. Webmin obtains the certificate, updates the certfile= and keyfile= lines in miniserv.conf for you, and renews it automatically before it expires. Reload the page and the padlock is clean.
The one honest snag: Webmin's own server runs on 10000, not 80, so the http-01 challenge needs a real web server — Apache or nginx — answering for panel.example.com on port 80, or a DNS-based validation instead. On a Webmin-only box with nothing on port 80, the request fails with a validation error until you give Let's Encrypt a way to reach the challenge file. The mechanics — DNS records, the port-80 challenge, and renewal — are the same as issuing a certificate for any website. If you want that background, the Let's Encrypt TLS certificates with Certbot and nginx guide walks through the validation flow and DNS setup in detail. If you took the SSH-tunnel route you can skip this section entirely: the self-signed certificate is fine behind SSH, and a real certificate issued for panel.example.com would only produce a name-mismatch warning when you visit https://localhost:10000 anyway.
Turn on two-factor authentication
A password alone is thin protection for a root panel, so add a second factor. Open Webmin, then Webmin Configuration, then Two-Factor Authentication. Select the Google Authenticator provider — that is standard TOTP, so it works with Authy, 1Password, or any authenticator app — and save. Webmin installs the small Perl module it needs (Authen::OATH plus a QR generator) and enables the feature; this step alone does not protect any account yet.
Each account then enrols its own device. With 2FA enabled, open Webmin, then Webmin Users, pick the account, choose Enable Two-Factor For User, and Webmin shows a QR code; scan it with the app and enter one generated code to confirm. From then on, login asks for the six-digit token after the password. Enrol before you log out — if 2FA is required but your account never scanned a code, you can still clear the requirement from the console, but it is far less painful to enrol first.
Add Fail2ban to ban brute-force logins
Even a restricted login endpoint should punish repeated failures. Webmin reports failed logins to syslog in the form webmin[12345]: Invalid login as root from 203.0.113.9, or Non-existent login as ... for a username that is not even a Unix account — on Ubuntu 24.04 those lines land in the systemd journal, since a stock image has no /var/log/auth.log. Fail2ban ships a stock webmin-auth filter that matches exactly those two lines, and Ubuntu's Fail2ban defaults already read the journal, so you do not need a custom regex or a log path.
Create /etc/fail2ban/jail.d/webmin.local:
[webmin-auth]
enabled = true
port = 10000
filter = webmin-auth
backend = systemd
maxretry = 4
bantime = 1h
Reload with sudo systemctl restart fail2ban, then confirm the jail is live:
sudo fail2ban-client status webmin-auth
You should see the jail listed with a ban counter of zero to start (on the journal backend there is no File list: line). To prove the wiring end to end, fail a login on purpose a few times from another network, then re-run the status command and watch Currently banned climb. If it never does, make sure you are not testing from an address in Fail2ban's ignoreip, and run journalctl SYSLOG_IDENTIFIER=webmin on the server to confirm the Invalid login lines are actually being written. If Fail2ban is not installed on this box yet, the Fail2ban on Ubuntu 24.04 for SSH guide covers the install and the SSH jail you should run right alongside this one.
Restrict what each Webmin user can touch
Not everyone who needs the panel needs root over everything. Under Webmin, then Webmin Users, create additional Webmin logins and grant each only the modules it needs — a backups operator who sees only the cron and filesystem modules, for example. Editing a user shows a checklist of every module; unchecking one removes it from that user's menu and blocks the underlying URLs. This is defence in depth: even a stolen low-privilege Webmin session cannot rewrite /etc/shadow when the Users module is not in its list.
Keeping Webmin updated
Because you installed from the apt repository, sudo apt update && sudo apt upgrade pulls new Webmin releases along with the rest of the system. Patch promptly — a control panel is a favourite target. Webmin can also update itself from Webmin, then Webmin Configuration, then Upgrade Webmin, but the apt path is cleaner on Ubuntu because it keeps versions consistent with your other package management. Do not treat this as optional: several past Webmin CVEs were remote-code-execution bugs, and the only thing between "patched" and "compromised" was how fast the admin ran the upgrade.
Failure modes, with the strings you will see
"Your connection is not private" / NET::ERR_CERT_AUTHORITY_INVALID. Shown on the very first load. Cause: the self-signed certificate Webmin generated at install has no trusted issuer. It is not an attack; the channel is encrypted, just not vouched-for. Fix: proceed through the warning for now, then issue a real Let's Encrypt certificate, or accept it permanently if you reach Webmin through an SSH tunnel.
"This web server is running in SSL mode. Try the URL https://..." You typed http://server:10000. miniserv only speaks TLS on that port and is telling you so in plain text. Fix: change http to https in the address bar.
Page times out — ERR_CONNECTION_TIMED_OUT / "This site can't be reached". The request never arrives at Webmin. On Ubuntu this is almost always ufw dropping port 10000. Confirm with sudo ufw status; if 10000 is not listed, either open it with sudo ufw allow 10000/tcp or, better, leave it closed and use the SSH tunnel from Option B. Note the distinction: a timeout means a firewall is silently dropping packets, while ERR_CONNECTION_REFUSED instead means the port is reachable but Webmin is not running — check sudo systemctl status webmin.
"Access denied for <your IP>." You set IP Access Control under Option A and your current address is not on the allow list — a dynamic IP that changed, or a typo. There is no browser path back in. Fix from the console: edit the allow= line in /etc/webmin/miniserv.conf, correct or delete it, and run sudo systemctl restart webmin.
"Login failed. Please try again." with credentials you know are correct. The account has no Unix password, which is standard on key-only cloud images. Webmin authenticates through PAM against the Unix password, and that password does not exist for the user. Fix: run sudo passwd youruser on the server, then log in. If /var/webmin/miniserv.log shows Non-existent login as ..., you are typing a username that is not a Unix account at all.
FAQ
Is it safe to expose Webmin on the public internet?
Treat an internet-facing Webmin as a root shell with a login page, because that is exactly what it is. It is safe enough only in layers: a real certificate, two-factor authentication, Fail2ban, and either a tight IP allowlist or, better, no public exposure at all. The lowest-risk setup binds Webmin to 127.0.0.1 and reaches it through an SSH tunnel, so port 10000 answers no one on the open internet.
How do I get rid of the Webmin certificate warning?
The warning (NET::ERR_CERT_AUTHORITY_INVALID) appears because Webmin ships a self-signed certificate. Issue a real one from Webmin, Webmin Configuration, SSL Encryption, Let's Encrypt, using a DNS name that points at the server with something serving the port-80 challenge for validation. If you only ever reach Webmin over an SSH tunnel to localhost, the warning is harmless — SSH already encrypts and authenticates the connection — and you can safely accept the self-signed certificate.
How do I restrict Webmin to only my IP address?
Go to Webmin, Webmin Configuration, IP Access Control, choose "Only allow from listed addresses", and enter your public IP from curl ifconfig.me. Webmin stores this in the allow= line of /etc/webmin/miniserv.conf. Watch out for a dynamic home IP: if it changes you are locked out and must repair the allow= line from the server console, so a static address or the SSH-tunnel method is more reliable in practice.
Why does my login fail even with the right password?
Webmin authenticates through PAM against your Unix password, and cloud images are usually key-only with no password set on the default account — so there is nothing for PAM to match and the login is rejected. Run sudo passwd youruser on the server to set one, then log in. A Non-existent login as ... line in /var/webmin/miniserv.log instead means the username itself is not a real Unix account.
What is Webmin good for compared with plain SSH?
Webmin is a discoverability and convenience layer. It is genuinely useful for browsing log files, managing users and cron, editing firewall rules, and seeing disk and service state without memorising every command — handy for occasional admins or mixed-skill teams. Plain SSH is faster, scriptable, and exposes far less attack surface for routine work. Many admins run both: SSH for daily driving, and Webmin bound to localhost behind a tunnel for the occasional point-and-click job.