How to see who logged in to your server and what they ran with sudo
Your server already writes down every login and every sudo command. Here is where to find it on Ubuntu and Debian, how to tell which key was used, and how to get a message each time someone logs in.
Before you start
- You need a user with sudo rights. If you still log in as root, see our guide to making a sudo user.
- We tested every command on Ubuntu 24.04 and Debian 13. The examples use a user called
anna. Change the names to yours. - Debian 13 changed some of the old tools. Where Ubuntu and Debian differ, both are shown.
1. See who is logged in right now
who
w
who lists each open session with the user, the time and the address it came from. w shows the same and adds what each person is running at the moment:
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
anna pts/0 203.0.113.10 16:45 4.00s 0.00s ? sleep 1800
If you see your own session only, nobody else is logged in with a terminal right now.
2. See past logins
Debian 13 only: the last command moved to a new package. If you get "command not found", install it. It only records logins from now on, not the ones before:
sudo apt install wtmpdb libpam-wtmpdb
Then, on both systems:
last -n 20
Each line shows the user, the address, when they logged in and how long they stayed:
anna pts/0 203.0.113.10 Thu Nov 12 16:45 - still logged in
anna pts/0 203.0.113.10 Thu Nov 12 16:44 - 16:44 (00:00)
Useful variants: last anna for one user, last -F for full dates and times, and last reboot for restarts. "gone - no logout" means the session ended without a normal log out, for example after a dropped connection or a crash.
Know the limit: last only lists logins that opened a terminal. In our tests, running a single command over SSH (ssh server 'command') did not show up. File copies with scp or SFTP don't open a terminal either. Step 4 shows every login, so use that too.
3. Last login per user, and failed logins
When did each user last log in? On Ubuntu:
lastlog
On Debian 13, install lastlog2 first. Like last, it starts with an empty list:
sudo apt install lastlog2 libpam-lastlog2
lastlog2
System accounts such as www-data should show "Never logged in" on Ubuntu, and not appear at all in lastlog2. A system account with a recent login is a warning sign.
Failed logins on Ubuntu:
sudo lastb -n 20
Debian 13 has no lastb anymore. Use the journal instead (step 6), which works on both.
4. Every SSH login, in the journal
The SSH server writes each login to the system journal, including single commands and file copies:
sudo journalctl -u ssh -g Accepted
Nov 12 16:45:29 web1 sshd[893]: Accepted publickey for anna from 203.0.113.10 port 34562 ssh2: ED25519 SHA256:t54g8AM8y0VzTDlBPslFxjUBhXwIVPB9rU+GzBJAArE
Nov 12 16:46:09 web1 sshd[977]: Accepted password for bob from 203.0.113.10 port 50702 ssh2
Each line tells you who logged in, from which address, and how: with a key (publickey) or a password. If you turned off password login, you should never see "Accepted password". On Debian 13 the lines say sshd-session instead of sshd. That's normal.
Good to know:
- Use
-u ssh, not-u sshd. The service is calledsshon Ubuntu and Debian. Withsshdyou get "-- No entries --" even when there are logins. - Pick a time range with
--since, for example--since todayor--since "2026-11-01". - Ubuntu also has
/var/log/auth.logwith the same lines:sudo grep Accepted /var/log/auth.log. Debian 13 has noauth.logby default, only the journal.
5. Find out which key was used
The long code at the end of an "Accepted publickey" line is the key's fingerprint. Print the fingerprints of every key that can log in:
for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do echo "== $f"; sudo ssh-keygen -lf "$f"; done
== /home/anna/.ssh/authorized_keys
256 SHA256:t54g8AM8y0VzTDlBPslFxjUBhXwIVPB9rU+GzBJAArE anna@laptop (ED25519)
Find the line with the same fingerprint as in the journal. The last word before the key type is the key's comment, here anna@laptop. On your own computer, ssh-keygen -lf ~/.ssh/id_ed25519.pub prints your key's fingerprint, so you can tell your logins from others.
Tip: give every key a clear comment when you make it, such as -C "anna laptop". Then each login points to one person and one device. If a fingerprint matches no key in the list, that key may have been removed since.
6. Failed logins
sudo journalctl -u ssh -g "Failed|Invalid" --since today
Invalid user admin from 198.51.100.7 port 34576
Failed password for invalid user admin from 198.51.100.7 port 34576 ssh2
Failed password for anna from 198.51.100.7 port 54930 ssh2
Count the attempts per address:
sudo journalctl -u ssh -g "Failed password" --since today -o cat | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head
Don't panic about these. Every server with SSH open gets password guesses from all over the internet, all day. SSH keys make the guesses useless, and Fail2ban blocks the noisiest addresses. What should worry you: many failures and then an "Accepted" line from the same address.
7. See what was run with sudo
sudo journalctl -t sudo -g COMMAND
Nov 12 16:45:29 web1 sudo[912]: anna : TTY=pts/0 ; PWD=/home/anna ; USER=root ; COMMAND=/usr/bin/cat /etc/shadow
Nov 12 16:45:31 web1 sudo[920]: anna : 1 incorrect password attempt ; TTY=pts/0 ; PWD=/home/anna ; USER=root ; COMMAND=/usr/bin/true
Nov 12 16:46:09 web1 sudo[995]: bob : user NOT in sudoers ; PWD=/home/bob ; USER=root ; COMMAND=/usr/bin/id
Each line shows who ran the command, in which folder, and the full command. Two kinds of lines deserve a closer look:
- "incorrect password attempt": someone typed the wrong sudo password. Once is usually a typo.
- "user NOT in sudoers": a user without sudo rights tried to become root.
On Ubuntu the same lines are in auth.log: sudo grep COMMAND /var/log/auth.log.
The big gap: after sudo -i, sudo su or sudo bash, the log only shows that a root shell was opened (COMMAND=/bin/bash). Nothing typed inside that shell is logged. We checked: a file created inside sudo -i left no trace here. Step 9 closes that gap. The sudo lines also don't show the address. Match the user and the time with the logins from step 4.
8. Keep the logs after a restart
The journal keeps its logs on disk only if the folder /var/log/journal exists. New installs of Ubuntu 24.04 and Debian 13 create it, but not every server has it. Check:
ls -d /var/log/journal
sudo journalctl --list-boots
If the folder is missing, or the list only ever shows the current start-up, turn on saving to disk:
sudo mkdir -p /etc/systemd/journald.conf.d
sudo nano /etc/systemd/journald.conf.d/persistent.conf
[Journal]
Storage=persistent
sudo systemctl restart systemd-journald
After the next restart, journalctl --list-boots shows more than one line. The journal deletes its oldest entries when it reaches its size limit, so check that the limit keeps enough weeks for you. See how to stop logs from filling the disk.
For serious use, also copy the logs to another machine. A copy elsewhere is the only one an intruder can't wipe.
9. Optional: log every command run as root
The Linux audit system can log every program started as root by a person who logged in, also inside sudo -i. It writes a lot, so turn it on when you need it, not just because you can.
sudo apt install auditd
sudo nano /etc/audit/rules.d/root-commands.rules
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root-commands
-a always,exit -F arch=b32 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root-commands
In plain words: log every program started as root (euid=0) by someone who logged in as a normal user (auid>=1000), and tag it root-commands. The audit system remembers who logged in, even after sudo, so background jobs and services are left out. Someone who logs in as root directly isn't covered, which is one more reason to turn off root login. Load the rules and check them:
sudo augenrules --load
sudo auditctl -l
See the commands, with user names instead of numbers, or a summary of which programs ran most:
sudo ausearch -k root-commands -i --start today
sudo aureport -x --summary
The logs go to /var/log/audit/, and auditd rotates them on its own. Note: the audit system can't run inside the containers we test in, so we checked this step against the official manuals only, not on a running server.
10. Optional: get a message on every SSH login
A small script can note each login and send you an email. The SSH server runs it through PAM, the part of Linux that handles logins. Make the script:
sudo nano /usr/local/sbin/login-alert
#!/bin/sh
# Runs on every SSH login. It must never block or fail a login.
[ "$PAM_TYPE" = "open_session" ] || exit 0
PATH=/usr/sbin:/usr/bin:/sbin:/bin
MSG="SSH login: $PAM_USER from $PAM_RHOST on $(hostname) at $(date "+%F %T")"
logger -t login-alert "$MSG"
if command -v mail >/dev/null 2>&1; then
echo "$MSG" | timeout 30 mail -s "SSH login on $(hostname)" you@example.com >/dev/null 2>&1 &
fi
exit 0
Change you@example.com to your address. Then make it runnable and add one line at the end of the SSH login settings:
sudo chmod 755 /usr/local/sbin/login-alert
echo 'session optional pam_exec.so quiet /usr/local/sbin/login-alert' | sudo tee -a /etc/pam.d/sshd
Keep your current session open and log in from a new window to test. Then look for the note:
sudo journalctl -t login-alert
Why it's safe:
optionaltells PAM that a failing script must not stop the login. We tested a script that fails and a script that is missing: logins still worked.- The email is sent in the background (the
&), andtimeoutstops it after 30 seconds. With a mail command that hangs, logins took no longer than before. - No mail set up? The script only writes the note to the journal. To get emails, the server needs a working
mailcommand, for example from thebsd-mailxpackage, and a mail server that can send out.
Unlike last, this also fires for single commands run over SSH. It's an early warning, not a lock: someone with root can switch it off.
11. If you see a login you don't recognise
- Stay calm and stay logged in. Check
whoto see if the session is still open. - Save the logs before anything rotates them away:
sudo journalctl --since "7 days ago" | sudo tee /root/journal-copy.txt >/dev/null. Copy the file off the server. - Find out which key or password was used (steps 4 and 5) and what was run (step 7).
- Remove keys you don't know from the
authorized_keysfiles and change the passwords of the users involved. - If the login used sudo or root, treat the whole server as broken into. Follow our first steps after a hack, including when to reinstall.
Often the answer is harmless: a colleague, a deploy tool or a backup job that uses its own key. That's why clear key comments (step 5) save you a scare.
If something goes wrong
- "last: command not found" or "lastlog: command not found" on Debian 13: install
wtmpdb libpam-wtmpdborlastlog2 libpam-lastlog2(steps 2 and 3). - "Cannot open database (/var/log/wtmp.db)":
wtmpdbhas nothing recorded yet. It fills up after the next login with a terminal or the next restart. journalctl -u sshdshows "-- No entries --": use-u ssh.- The journal only goes back to the last restart: it isn't saved to disk. See step 8.
- "Permission denied" or empty output: run the command with
sudo. Normal users can't read the system logs. - You locked yourself out after editing
/etc/pam.d/sshd: use your provider's web console, remove the line you added, and check that it saysoptional, notrequired.
Official sources
- who, w, last and lastb and lastlog (Ubuntu 24.04 manuals)
- wtmpdb and lastlog2 (Debian 13 manuals), and the Debian 13 release notes on last, lastb and lastlog being replaced
- journalctl and journald.conf (systemd)
- ssh-keygen (OpenSSH) and sudoers, including logging (sudo.ws)
- auditctl, audit.rules, ausearch and aureport (Debian 13 manuals)
- pam_exec (Debian 13 manuals)
Next steps: if you haven't yet, switch to SSH keys and turn off password login, and work through the security checklist for a new PHP server.
ServerNest is being built to help you keep an eye on your own servers from one place. Join early access to try it first.