# journalctl Commands for Server Admins: Filter, Follow, Search and Keep Logs (Tested)

Source: https://srvscripts.com/guides/journalctl-commands-cheat-sheet/
Updated: 2026-10-11
Publisher: srvScripts (https://srvscripts.com/)

**Short answer:** `journalctl -u SERVICE` shows one service’s log, `-f` follows it live, `--since "1 hour ago"` limits the time, `-p err` shows only errors, `-b` limits it to the current boot and `--grep PATTERN` searches. `journalctl --disk-usage` shows how much space the journal uses, and `journalctl --vacuum-time=14d` deletes older entries. If `/var/log/journal` does not exist, the journal is lost at every reboot: create it with `mkdir -p /var/log/journal`, then run `systemd-tmpfiles --create --prefix /var/log/journal` and `journalctl --flush`.

We ran every command on this page on our lab servers on 11 October 2026: systemd 252 on AlmaLinux 9.8 (cPanel 138 and DirectAdmin 1.712) and on Debian 12. The outputs shown are from those servers, shortened. Note that the SSH service is `sshd` on AlmaLinux and `ssh` on Debian.

## journalctl quick reference

| Task | Command |
| --- | --- |
| Newest entries, jump to the end | journalctl -e |
| Last 50 lines, no pager | journalctl -n 50 --no-pager |
| Follow live (like tail -f) | journalctl -f |
| One service | journalctl -u exim |
| Several services together | journalctl -u sshd -u dovecot |
| Time range | journalctl --since "2026-10-11 03:00" --until "2026-10-11 03:05" |
| Relative time | journalctl --since "1 hour ago" or --since today |
| Errors and worse | journalctl -p err -b |
| Warnings to errors only | journalctl -p warning..err |
| Kernel messages | journalctl -k |
| Previous boot | journalctl -b -1 (needs a persistent journal) |
| List boots | journalctl --list-boots |
| By program name (syslog tag) | journalctl -t CROND |
| By process ID or user ID | journalctl _PID=1234, journalctl _UID=0 |
| Search text (regex) | journalctl -u sshd --grep "Invalid user" |
| Newest first | journalctl -r |
| Message text only | journalctl -o cat |
| ISO timestamps | journalctl -o short-iso |
| All fields as JSON | journalctl -n 1 -o json-pretty |
| Which values a field has | journalctl -F _SYSTEMD_UNIT |
| Disk usage | journalctl --disk-usage |
| Delete old entries | journalctl --vacuum-time=14d or --vacuum-size=500M |
| Check for corruption | journalctl --verify |

## Filter by service, program and process

`-u` takes a systemd unit name. Find the right one with `systemctl list-units --type=service`. On our labs the useful units were:

| Server | Units we used |
| --- | --- |
| cPanel 138 (AlmaLinux 9) | sshd, exim, dovecot, httpd, mariadb, pdns |
| DirectAdmin 1.712 (AlmaLinux 9) | directadmin, exim, httpd, php-fpm83, php-fpm74 (one per PHP version) |
| Debian 12 | ssh, cron, apache2, chrony |

Some messages do not belong to a service. Cron jobs on AlmaLinux are logged with the tag `CROND`, so `journalctl -t CROND` finds them; on Debian the tag is `cron`. To see which tag a line uses, print one entry as JSON and look at `SYSLOG_IDENTIFIER`:

```
journalctl -n 1 -o json-pretty | grep -E '"(_SYSTEMD_UNIT|SYSLOG_IDENTIFIER|_COMM|PRIORITY)"'
	"_SYSTEMD_UNIT" : "sshd.service",
	"SYSLOG_IDENTIFIER" : "sshd-session",
	"_COMM" : "sshd-session",
	"PRIORITY" : "6",
```

On AlmaLinux 9.8 the SSH login lines come from `sshd-session`, not `sshd`, so `-t sshd` misses them. `-u sshd` catches both.

## Filter by time and boot

```
journalctl --since today
journalctl --since "1 hour ago"
journalctl --since "2026-10-11 03:00" --until "2026-10-11 03:05"
journalctl -b            # this boot
journalctl -b -1         # the boot before
journalctl --list-boots
```

On our cPanel lab, `journalctl -b -1` printed `Specifying boot ID or boot offset has no effect, no persistent journal was found.` That server kept its journal only in memory, so older boots were gone. See [making the journal survive a reboot](#make-the-journal-survive-a-reboot).

## Errors only: priority levels

`-p` filters by [syslog priority](https://srvscripts.com/tools/syslog-priority-decoder/): `emerg` (0), `alert` (1), `crit` (2), `err` (3), `warning` (4), `notice` (5), `info` (6), `debug` (7). A single level means that level and everything more serious; a range like `warning..err` shows only that band.

```
journalctl -p err -b --no-pager | tail
journalctl -p warning..err --since today --no-pager | wc -l   # 179 lines on our cPanel lab
```

Expect noise. On our cPanel lab, most of the `err` lines came from a single Python traceback that repeated.

## Search the journal

`--grep` takes a regular expression and only works if journalctl was built with PCRE2. Both our AlmaLinux 9.8 and Debian 12 builds were (`journalctl --version` shows `+PCRE2`). It is case-insensitive unless the pattern has capitals; `--case-sensitive` changes that.

```
journalctl -u sshd --since "-24h" --grep "Invalid user" -n 2
journalctl -u sshd --since "-24h" --no-pager | grep -c "Failed password\|Invalid user"   # 1,398 on our cPanel lab

# Top attacking IPs in the last 24 hours
journalctl -u sshd --since "-24h" -o cat | grep -E "Failed password|Invalid user" \
  | grep -oE "from [0-9a-f.:]+" | sort | uniq -c | sort -rn | head
```

That count shows why SSH should not answer password logins from the whole internet: see [harden SSH on AlmaLinux 9](/guides/harden-ssh-almalinux-9/) and [fail2ban vs CSF](/guides/fail2ban-vs-csf/).

## Follow logs live

```
journalctl -f                       # everything
journalctl -fu exim                 # one service
journalctl -f -u sshd -u dovecot    # two services
journalctl -f -p err                # only new errors
```

Stop with Ctrl+C. Inside a script, use `timeout 60 journalctl -f ...` so it ends by itself.

## Output formats

| Option | Gives you |
| --- | --- |
| -o short (default) | Syslog-style lines |
| -o short-iso | 2026-10-11T05:38:56-0500 lab1 sshd-session[643164]: ... |
| -o cat | The message text only, good for grep and awk |
| -o json / json-pretty | Every field, for scripts and log shippers |
| -o verbose | Every field in readable form |
| --utc | Timestamps in UTC instead of the server’s time zone |
| -x | Adds explanations from the message catalogue where available |

## Make the journal survive a reboot

With the default `Storage=auto`, journald stores logs on disk only if `/var/log/journal` exists; otherwise it keeps them in `/run/log/journal`, in memory, and they are lost at reboot. Our DirectAdmin and Debian labs had the directory. Our cPanel lab (an AlmaLinux 9.8 cloud image) did not, so every reboot erased its logs. That server had no rsyslog either, so there was no other copy.

```
ls -d /var/log/journal || echo "journal is NOT persistent"
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
journalctl --flush        # move what is in memory to disk
journalctl --disk-usage
```

On our lab, `journalctl --flush` moved 73 MB from `/run/log/journal` to `/var/log/journal` straight away. No restart was needed. An alternative is to set `Storage=persistent` in a drop-in file (below).

## Limit the journal’s size

Put limits in a drop-in file instead of editing `/etc/systemd/journald.conf`, so updates do not overwrite them:

```
mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/limits.conf
