
Anyone who has managed a handful of servers knows the moment. A monitoring alert fires, and the quickest answer is buried in a terminal session on a box that was set up two years ago by someone who has since left. SSH is fine for that, and it always will be. But there are plenty of times when a clean browser view of services, logs, disks, network interfaces and storage saves ten minutes of command-line digging. Cockpit exists for exactly that.
Cockpit is a lightweight, web-based administration console that ships in the Ubuntu archive. It is not a control panel that takes over your server. It reads the same systemd, journald, NetworkManager or netplan, and storage APIs you already use, and it shows them in a browser. Close the tab and nothing is left running, because the service is socket-activated and only wakes up when someone connects.
Ubuntu 26.04 LTS, codename Resolute Raccoon, was released by Canonical on April 23, 2026. That makes it the LTS most new deployments will target for the next several years. This guide shows how to install Cockpit on Ubuntu 26.04 LTS properly: the package install, the firewall, certificates, reverse proxying, access control and the odd failures that show up in production. It also covers a version quirk specific to 26.04 that is worth knowing before you commit to a setup.
What Cockpit Is and When It Makes Sense
Cockpit is a server manager with a browser front end. Behind the interface, cockpit-ws handles web sessions and cockpit-bridge talks to the system on your behalf. When you log in, you are logging in as a real system user. There is no separate Cockpit account database, which is a big part of why it is easy to reason about from a security standpoint.
Where it fits well
Cockpit is a good match in a few situations:
- Small to mid-sized fleets where a full configuration management or monitoring stack would be overkill.
- Teammates who are comfortable with Linux but do not want to memorise
journalctlflags,nmclisyntax orlvmincantations. - Quick triage on a misbehaving production server: check failed units, tail logs, look at disk usage and see which process is eating memory.
- Lab and homelab setups with KVM virtual machines or Podman containers.
- Training environments, where seeing what the system does helps people learn what the CLI does.
Where it does not fit
It will not replace Ansible, Terraform or a proper observability platform. Do not use it as a deployment tool. Treat it as a window into one machine, or a handful of machines, and it will earn its place.
Prerequisites Before You Start
Check a few things first. It saves time later.
- A working Ubuntu 26.04 LTS installation, server or desktop. Cockpit behaves the same either way.
- A non-root user with sudo rights. Cockpit lets you log in as root only if you deliberately allow it, and you should not.
- Network access to the Ubuntu mirrors for apt.
- Port 9090/TCP reachable from wherever you will manage the box, or a plan to proxy it.
- Decent hygiene on the host: current packages, a working clock (NTP), and a hostname that actually resolves.
Confirm the release you are on:
lsb_release -a
cat /etc/os-release
uname -r
You should see Ubuntu 26.04 and the codename resolute. That codename matters later if you use backports.
Step 1: Update the System
Start from a clean base. Pulling Cockpit on top of stale package indexes is how people end up with held-back dependencies and confusing errors.
sudo apt update
sudo apt full-upgrade -y
If the kernel or systemd was upgraded, reboot before continuing. It is tempting to skip, but on a fresh server it costs thirty seconds and removes a whole category of “was it the upgrade?” doubt.
sudo reboot
Step 2: Install Cockpit on Ubuntu 26.04 LTS
Cockpit lives in the Ubuntu universe repository. On most server images that component is already enabled. If it is not, enable it first:
sudo add-apt-repository universe
sudo apt update
Now install the base package:
sudo apt install cockpit -y
Before pressing Enter, check what apt would pull in and what it would leave out:
apt policy cockpit
At the time of writing, the 26.04 archive carries Cockpit version 360. This is a point to understand. A discussion on the Cockpit project’s GitHub from late August 2026 notes that the Resolute archive was still on 360 (the April release), while Debian’s Trixie-Backports had 365 and Ubuntu’s Noble had a 362 backport. Cockpit’s own maintainers said they did not know who handles the Ubuntu backports and could not help with that. The upstream installation page says Ubuntu LTS releases get updated Cockpit through official backports, but for Resolute that was not available when that discussion happened. Check apt policy on your own system to see whether things have changed since.
Does that matter? For most people, no. Version 360 is a perfectly capable console. It only becomes a concern if you need a specific newer module or a bug fix that landed later.
Enable the socket
Cockpit uses systemd socket activation. You enable the socket, not the service:
sudo systemctl enable --now cockpit.socket
Verify:
systemctl status cockpit.socket
ss -tlnp | grep 9090
The socket listens on port 9090 and starts cockpit.service only when a client connects. That is why systemctl status cockpit.service often shows “inactive (dead)” on a healthy system. It is not broken. Nobody has knocked yet.
Step 3: Open the Firewall
Ubuntu ships with UFW installed but disabled by default. On a production machine it should be on. Do things in the right order here, because enabling UFW without allowing SSH first is a reliable way to lock yourself out of a remote server.
sudo ufw allow OpenSSH
sudo ufw allow 9090/tcp
sudo ufw enable
sudo ufw status verbose
The 9090/tcp rule is the plain approach. Cockpit also installs a UFW application profile on some releases, so sudo ufw allow cockpit can work, but check with sudo ufw app list. The numeric rule works everywhere.
Restrict by source
Opening 9090 to the whole internet is a poor idea. If you have a fixed office IP or a VPN range, restrict it:
sudo ufw delete allow 9090/tcp
sudo ufw allow from 203.0.113.0/24 to any port 9090 proto tcp
Replace the example range with your own. On AlmaLinux, Rocky or other RHEL-family systems, the equivalent is:
sudo dnf install cockpit -y
sudo systemctl enable --now cockpit.socket
sudo firewall-cmd --permanent --add-service=cockpit
sudo firewall-cmd --reload
On Debian, the install is sudo apt install cockpit, with the backports option when you want a newer build.
Step 4: Log In to the Web Console
Open a browser and visit:
https://your-server-ip:9090
Note the https. Cockpit generates a self-signed certificate on first start, so every browser will warn you about it. For a first look that is fine. Accept the warning, then sign in with an existing system user.
After login, you will see a limited-access banner. Click Turn on administrative access and re-enter your sudo password. Cockpit uses this model deliberately. You browse as a normal user and elevate only when you need to change something, which mirrors how sudo works at the shell.
If you are working from Yogyakarta, Jakarta or anywhere with a higher-latency path to a cloud region, expect the first page load to feel slower than SSH. The console is JavaScript-heavy at startup. Once loaded, interaction is quick.
Step 5: Install the Add-On Modules
The base package gives you system overview, logs, services, networking, accounts, terminal and software updates. Extra modules add depth. Install only what you will actually use.
sudo apt install -y cockpit-storaged cockpit-networkmanager cockpit-packagekit cockpit-sosreport
Optional extras:
sudo apt install -y cockpit-podman cockpit-machines cockpit-pcp
What each one does:
| Module | Purpose | Typical use |
|---|---|---|
| cockpit-storaged | Disks, LVM, RAID, NFS, partitions | Adding a data disk, checking SMART status |
| cockpit-networkmanager | Network interface management | Bonding, VLANs, static addresses |
| cockpit-packagekit | Software updates from the UI | Applying security patches |
| cockpit-podman | Container management | Running and inspecting Podman containers |
| cockpit-machines | KVM/libvirt virtual machines | Lab VMs, test environments |
| cockpit-pcp | Historical performance metrics | Looking back at a CPU spike |
| cockpit-sosreport | Diagnostic report generation | Support tickets |
Do not install everything out of habit. Each module is more code, more attack surface and more to patch.
A note on networking on Ubuntu Server
Ubuntu Server uses netplan with systemd-networkd by default, while Cockpit’s network page works best with NetworkManager. This is the most common source of confusion. If the Networking page shows interfaces as “unmanaged” or refuses to edit them, that is why.
Switching the renderer on a remote production server is risky. A typo can drop the machine off the network. For a remote host, leave netplan alone and use Cockpit only to view network state. If you are on a local VM or console-accessible machine, you can switch the renderer in /etc/netplan/*.yaml:
network:
version: 2
renderer: NetworkManager
Test with sudo netplan try, which rolls back automatically if you lose connectivity.
Step 6: Replace the Self-Signed Certificate
A permanent browser warning teaches people to click through warnings. That is a bad habit on a server console. Fix it.
Cockpit loads certificates from /etc/cockpit/ws-certs.d/. It uses the file that sorts last alphabetically with a .cert extension. The default self-signed one is 0-self-signed.cert, so any file with a higher-sorting name wins.
Using Let’s Encrypt
This assumes a public DNS name pointing at the server, and port 80 temporarily reachable.
sudo apt install certbot -y
sudo certbot certonly --standalone -d cockpit.example.com
Then copy the certificate chain and the key into the Cockpit directory as separate files with the same base name:
sudo cp /etc/letsencrypt/live/cockpit.example.com/fullchain.pem /etc/cockpit/ws-certs.d/50-letsencrypt.cert
sudo cp /etc/letsencrypt/live/cockpit.example.com/privkey.pem /etc/cockpit/ws-certs.d/50-letsencrypt.key
sudo chgrp cockpit-ws /etc/cockpit/ws-certs.d/50-letsencrypt.key
sudo chmod 640 /etc/cockpit/ws-certs.d/50-letsencrypt.key
sudo systemctl restart cockpit
Check which one is active:
openssl s_client -connect cockpit.example.com:9090 -servername cockpit.example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates
Renewal hook
Let’s Encrypt certificates last 90 days. Copying files manually once and forgetting is how a Cockpit login page starts showing an expired certificate three months later. Add a deploy hook:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/cockpit.sh > /dev/null <<'EOF'
#!/bin/bash
D=/etc/letsencrypt/live/cockpit.example.com
cp "$D/fullchain.pem" /etc/cockpit/ws-certs.d/50-letsencrypt.cert
cp "$D/privkey.pem" /etc/cockpit/ws-certs.d/50-letsencrypt.key
chgrp cockpit-ws /etc/cockpit/ws-certs.d/50-letsencrypt.key
chmod 640 /etc/cockpit/ws-certs.d/50-letsencrypt.key
systemctl restart cockpit
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/cockpit.sh
Test it with sudo certbot renew --dry-run.
Internal servers without public DNS can use a private CA or a DNS-01 challenge. The principle stays the same: drop a matching .cert and .key pair into the directory and restart.
Step 7: Configure Cockpit Behaviour
Settings live in /etc/cockpit/cockpit.conf. The file does not exist by default. Create it.
[WebService]
Origins = https://cockpit.example.com:9090 wss://cockpit.example.com:9090
ProtocolHeader = X-Forwarded-Proto
AllowUnencrypted = false
LoginTitle = Production Web Node 03
[Session]
IdleTimeout = 15
Banner = /etc/cockpit/issue.cockpit
[Log]
Fatal = criticals warnings
What matters here:
- IdleTimeout is in minutes. Fifteen is reasonable. An open admin console on an unattended laptop is a real risk.
- LoginTitle is more useful than it sounds. When you have six tabs open to six different servers, a custom title keeps you from running a restart on the wrong one.
- Origins is required when you proxy Cockpit or use a hostname different from what the server sees. Omitting it produces a blank page or WebSocket errors.
- Banner shows a legal notice before login, useful for compliance.
Apply changes:
sudo systemctl restart cockpit
Changing the listening port
Port 9090 is well known, and scanners probe it. Changing it is not security, but it does cut noise in your logs. Override the socket unit:
sudo systemctl edit cockpit.socket
Add:
[Socket]
ListenStream=
ListenStream=9443
The empty ListenStream= line clears the default. Without it you end up listening on both ports. Then:
sudo systemctl daemon-reload
sudo systemctl restart cockpit.socket
sudo ufw allow 9443/tcp
sudo ufw delete allow 9090/tcp
Binding only to a private interface is better still:
[Socket]
ListenStream=
ListenStream=10.0.0.5:9090
If the machine has a WireGuard or private network address, this makes Cockpit unreachable from the public interface entirely. For internet-facing servers, this is the pattern worth using.
Running Cockpit Behind Nginx
Plenty of teams already terminate TLS at Nginx. Proxying Cockpit is possible, but the WebSocket handling trips people up.
First tell Cockpit that plain HTTP is acceptable from the proxy and which origin to expect:
[WebService]
Origins = https://cockpit.example.com
ProtocolHeader = X-Forwarded-Proto
AllowUnencrypted = true
Only use AllowUnencrypted = true when Cockpit is bound to localhost, so the plain HTTP leg never leaves the machine. Bind the socket to 127.0.0.1:9090 with the override from the previous section.
Then the Nginx server block:
server {
listen 443 ssl http2;
server_name cockpit.example.com;
ssl_certificate /etc/letsencrypt/live/cockpit.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cockpit.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:9090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
gzip off;
}
}
Test and reload:
sudo nginx -t && sudo systemctl reload nginx
The Upgrade and Connection headers are not optional. Without them the login page loads, then the terminal and live graphs fail silently. Also set proxy_read_timeout 3600s; if people leave terminal sessions open. The default 60 seconds will drop idle WebSocket connections.
A bonus of proxying: Nginx can add IP allowlists, basic auth as a second gate, rate limiting and extra logging in front of the console.
Security Hardening
A web-based root-capable console is a high-value target. Treat it that way from day one.
Limit who can log in
Cockpit honours /etc/cockpit/disallowed-users. The default already blocks root. Verify:
cat /etc/cockpit/disallowed-users
For finer control, use PAM. Create a group and restrict access:
sudo groupadd cockpit-admins
sudo usermod -aG cockpit-admins alice
Then add a line to /etc/pam.d/cockpit above the existing auth lines:
auth required pam_succeed_if.so user ingroup cockpit-admins
Test with a second session open before you log out of the first, because a mistake here is easy to make and annoying to undo.
Use two-factor authentication
Cockpit authenticates through PAM, so a TOTP module works. Install libpam-google-authenticator, have each user run google-authenticator, then add to /etc/pam.d/cockpit:
auth required pam_google_authenticator.so
Be careful with this on shared accounts, and keep a recovery path (console or SSH key access) that does not depend on the web login.
Protect against brute force
Install fail2ban and give it a Cockpit jail. Failed logins are written to the journal with the identifier cockpit-ws.
sudo apt install fail2ban -y
Create /etc/fail2ban/filter.d/cockpit.conf:
[Definition]
failregex = pam_unix\(cockpit:auth\): authentication failure.* rhost=<HOST>
cockpit-ws.*: couldn't authenticate.* from <HOST>
ignoreregex =
And /etc/fail2ban/jail.d/cockpit.local:
[cockpit]
enabled = true
port = 9090
filter = cockpit
backend = systemd
journalmatch = _SYSTEMD_UNIT=cockpit.service
maxretry = 4
findtime = 10m
bantime = 1h
sudo systemctl restart fail2ban
sudo fail2ban-client status cockpit
Log formats can differ between Cockpit versions. Trigger a failed login on purpose and look at journalctl -u cockpit.service -n 20 to confirm your regex matches what is really written. Never assume.
Keep it patched
Cockpit is a network service. Turn on unattended security upgrades:
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
Prefer a VPN or SSH tunnel
The safest exposure for Cockpit is none. Reach it through WireGuard, Tailscale or an SSH tunnel:
ssh -L 9090:127.0.0.1:9090 user@server
Then browse to https://localhost:9090. No firewall hole, no public listener.
Managing Multiple Servers
Cockpit can connect to other hosts over SSH from a single dashboard. On the primary server, add a host through the user menu > Add new host option. Cockpit uses SSH, so every remote machine needs cockpit-bridge available. The cockpit-system package covers that on most installs.
Use SSH keys rather than passwords. Verify host key fingerprints when prompted. And keep the number of machines sensible. Ten servers in one dashboard is convenient. A hundred is a monitoring problem that deserves a real tool.
Troubleshooting Common Problems
This is where experience pays off. Most Cockpit problems fall into a few familiar buckets.
Connection refused on port 9090
Check the basics in order:
systemctl is-enabled cockpit.socket
systemctl status cockpit.socket
ss -tlnp | grep 9090
sudo ufw status
If the socket is inactive, start it. If it is listening but unreachable remotely, the firewall or a cloud security group is blocking it. Cloud providers often have a separate network firewall in front of the instance. UFW alone will not open it.
Login fails with the correct password
Possible causes:
- The user is listed in
/etc/cockpit/disallowed-users. - A PAM restriction (group or 2FA) is rejecting the account.
- The account is locked or expired. Check with
sudo passwd -S usernameandsudo chage -l username. - Root login is being attempted. Use a sudo user.
Read the journal for the actual reason:
sudo journalctl -u cockpit.service -u cockpit.socket --since "10 minutes ago"
Blank page or “Disconnected” behind a proxy
Almost always the WebSocket upgrade headers or the Origins setting. Confirm both the Nginx headers and /etc/cockpit/cockpit.conf. Restart Cockpit after editing. The browser developer console will usually show a rejected WebSocket handshake.
“Your browser is not supported” or a stale interface
Hard refresh the page, or clear site data. Cockpit caches static assets aggressively, and after an upgrade the browser can mix old and new files.
Network page says interfaces are unmanaged
As covered earlier, this is the netplan and systemd-networkd versus NetworkManager mismatch. Decide deliberately whether to switch the renderer, and never do it blind on a remote machine.
Software updates page is empty or errors
The Software Updates page relies on PackageKit. Make sure it is installed and the daemon is healthy:
sudo apt install packagekit cockpit-packagekit -y
sudo systemctl restart packagekit
If apt is locked by another process (such as unattended-upgrades), PackageKit will wait or fail. Check with ps aux | grep -E 'apt|dpkg'.
Certificate not being picked up
Check file names and permissions. The .cert and .key need the same base name, and the key must be readable by the cockpit-ws group. Remember that the last alphabetical match wins. A leftover file named 99-old.cert will beat your new one.
Package version older than expected
If you see version 360 on Ubuntu 26.04 while documentation mentions newer features, you are not misconfigured. The archive version simply lags, as discussed in the Cockpit project’s GitHub thread. Check apt policy cockpit and apt-cache madison cockpit. If you truly need something newer, the options are waiting for a backport, building from upstream source in a test environment, or running a newer release in a container or VM. For production, stick with the packaged build. A slightly older, patched, supported package beats a hand-built one nobody remembers how to update.