tail Command in Linux with Examples

Master the Linux tail command with real-world examples, advanced use cases, and practical tips for efficient log monitoring and debugging.

Last updatedAuthorJoshua JamesRead time8 minGuide typeLinux Commands

Large logs are awkward to open in an editor when the only lines you care about are the newest ones. The tail command in Linux solves that problem by printing the end of a file immediately, then staying attached with -f when you need live updates during a restart, deployment, or incident response session.

Practical tail usage starts with line counts, byte ranges, standard input, live log following, rotation-safe monitoring, multi-file output, and filtering with grep, awk, and sed. The GNU coreutils tail manual documents the full option set used on most Linux distributions, while BusyBox systems expose a smaller tail applet that needs a few compatibility notes.

Understand the tail Command

tail Command Basics

By default, tail prints the last 10 lines of each file you give it. Add -n to choose a line count, -c to count bytes instead of lines, or -f to keep reading as the file grows. If you omit the file name, tail reads from standard input, which makes it useful after commands such as dmesg, journalctl, grep, or cat.

The basic syntax is:

tail [OPTIONS] [FILE]
  • OPTIONS: Flags such as -n 50, -f, -F, or -c 100 that change what tail prints.
  • FILE: The file to read. Use - or omit the file name to read from standard input.

Use tail when the end of a file matters more than the whole file: recent error messages, the latest deployment event, the newest records in a generated report, or the last bytes of a binary payload.

On systemd-based systems, journalctl -f follows the journal directly and is often a better fit for services that do not write traditional files under /var/log. You can still pipe journalctl output into tail when you want the last N entries from a wider journal query.

Essential tail Options by Task

These common patterns cover most day-to-day tail work:

TaskCommand PatternWhat It Does
Show the default endingtail file.logPrints the last 10 lines.
Choose a line counttail -n 5 access.logPrints the last 5 lines instead of the default 10.
Start at a specific linetail -n +100 app.logPrints from line 100 through the end of the file.
Read by bytestail -c 100 payload.binPrints the final 100 bytes, useful for binary or fixed-width data.
Follow a growing filetail -f app.logPrints new lines as the file grows until you press Ctrl+C.
Follow through log rotationtail -F app.logTracks the file name and retries if the file is replaced or temporarily missing.
Follow multiple filestail -f access.log error.logPrints file headers so you can tell which file produced each line.
Suppress file headerstail -q file1 file2Hides headers when multiple files are read.
Force file headerstail -v file.logShows the file name header even when only one file is read.
Stop when a process exitstail --pid "$pid" -f app.logGNU-only option that exits after the watched process ends.
Adjust polling intervaltail -s 5 -f app.logUses a 5-second sleep interval when polling is active.
Use NUL delimiterstail -z -n 1 records.datGNU-only option that treats NUL bytes as record separators.

Verify tail and Coreutils Availability

On normal Linux installations, tail comes from GNU coreutils and is already present. Confirm the shell can find it before troubleshooting package names:

command -v tail
/usr/bin/tail

GNU systems also support a short version check. Example output from a current GNU build looks like:

tail --version | head -n 1
tail (GNU coreutils) 9.11

The exact version depends on your distribution. If command -v tail fails on a very small image or container, install the coreutils package, not a package named tail.

APT-based systems use:

sudo apt install coreutils

DNF-based systems use:

sudo dnf install coreutils

Arch-based systems use:

sudo pacman -S coreutils

openSUSE systems use:

sudo zypper install coreutils

Alpine systems commonly run package commands as root. Use doas or sudo only if your image has that privilege tool configured:

apk add coreutils

Older searches for tailf, including Ubuntu 14.04-era package questions, refer to a deprecated util-linux helper. Current Linux systems should use tail -f for ordinary following and tail -F when log rotation can replace the file.

BusyBox tail Compatibility

Minimal distributions and embedded systems may provide BusyBox tail instead of GNU tail. The BusyBox documentation lists a smaller option set: -c, -n, -f, -q, -s, and -v. Check the applet help on the actual system before using GNU-only options:

busybox tail --help
tail [OPTIONS] [FILE]...

Print last 10 lines of each FILE (or stdin) to stdout. With more than one FILE, precede each with a filename header.

Options:
        -f              Print data as file grows
        -s SECONDS      Wait SECONDS between reads with -f
        -n N[kbm]       Print last N lines
        -c N[kbm]       Print last N bytes
        -q              Never print headers
        -v              Always print headers

N may be suffixed by k (x1024), b (x512), or m (x1024^2). If N starts with a '+', output begins with the Nth item from the start of each file, not from the end.

Use GNU coreutils when you need -F, --follow=name, --retry, --pid, --version, or -z. If a BusyBox build adds local extras, trust busybox tail --help for that system rather than assuming GNU behavior.

For BusyBox query patterns such as tail -n +1 -f app.log, the +1 form starts at the first line and then follows the file. It is still not the same as GNU tail -F, because BusyBox does not document rotation-safe -F or --follow=name.

On Windows, PowerShell has similar file-reading behavior with Get-Content -Tail 20 file.log and live following with Get-Content -Wait file.log. Inside WSL, use the normal Linux tail command.

Practical tail Command Examples

View Recent Lines with tail -n

The most common tail task is printing a chosen number of recent lines. For example, print the last 5 lines of access.log:

tail -n 5 access.log
192.168.1.10 - - [02/Jan/2026:14:28:01 +0000] "GET / HTTP/1.1" 200 612
192.168.1.11 - - [02/Jan/2026:14:28:04 +0000] "GET /assets/site.css HTTP/1.1" 200 1842
192.168.1.12 - - [02/Jan/2026:14:28:07 +0000] "POST /login HTTP/1.1" 401 498
192.168.1.13 - - [02/Jan/2026:14:28:12 +0000] "GET /dashboard HTTP/1.1" 302 0
192.168.1.14 - - [02/Jan/2026:14:28:15 +0000] "GET /favicon.ico HTTP/1.1" 404 153

For system logs, the same pattern works with real log paths, usually with sudo when the file is protected:

sudo tail -n 20 /var/log/syslog

On RHEL-family systems, traditional system messages commonly live under /var/log/messages instead:

sudo tail -n 20 /var/log/messages

Start tail Output at a Specific Line

A plus sign changes -n from “show the last N lines” to “start at line N.” This prints from line 100 through the end of the file:

tail -n +100 app.log

That form is useful when a log line number appears in another tool, or when you want everything after a known marker without opening the full file in an editor.

Read tail Input from a Pipeline

When no file is supplied, tail reads standard input. This makes it a natural final stage after a command that can produce too much output:

dmesg | tail -n 20

You can also trim journal output after a broader query:

journalctl -u nginx --no-pager | tail -n 50

Do not add -f to a pipe expecting tail itself to follow a file. For live journal output, make the upstream command follow with journalctl -f, then use other filters as needed.

Follow Logs in Real Time with tail -f and tail -F

Use -f when a file is actively growing and you want new lines as they are written. Press Ctrl+C to stop following:

tail -f /var/log/nginx/access.log
192.168.1.20 - - [02/Jan/2026:15:05:10 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.21 - - [02/Jan/2026:15:05:15 +0000] "POST /api/login HTTP/1.1" 401 512

Use -F for production log files that a rotation tool may rename and recreate:

tail -F /var/log/nginx/access.log

GNU tail -F is equivalent to --follow=name --retry. It tracks the path, not only the already-open file descriptor, so it can reopen the log after rotation.

Monitor Multiple Files with tail

Pass more than one file when you need to compare related logs side by side. tail prints headers as each file contributes output:

tail -f /var/log/nginx/access.log /var/log/nginx/error.log
==> /var/log/nginx/access.log <==
192.168.1.10 - - [02/Jan/2026:15:12:01 +0000] "GET /missing.html HTTP/1.1" 404 153

==> /var/log/nginx/error.log <==
2026/01/02 15:12:01 [error] 1234#0: *44 open() "/var/www/html/missing.html" failed (2: No such file or directory)

Add -q only when file names would be noise in a script. During manual troubleshooting, the headers are usually worth keeping.

Filter tail Output with grep, awk, and sed

The grep command guide is the natural companion when you need matching log lines only. Use --line-buffered with live tail -f pipelines so matches appear immediately:

tail -f /var/log/auth.log | grep --line-buffered "Failed password"

On RHEL-family systems, authentication events commonly use /var/log/secure instead:

tail -f /var/log/secure | grep --line-buffered "Failed password"
Jan 02 15:18:30 server sshd[1234]: Failed password for invalid user admin from 192.168.1.100 port 52944 ssh2
Jan 02 15:18:41 server sshd[1235]: Failed password for root from 10.0.0.5 port 53102 ssh2

Use awk when you want specific fields from a structured line. In the standard NGINX combined log format, the client address is field 1 and the requested path is field 7:

tail -n 2 access.log | awk '{print $1, $7}'
192.168.1.1 /home.html
10.0.0.2 /login.html

Use sed command examples when you need to normalize repeated text before comparison. This command replaces ISO-style dates with a stable placeholder so recurring errors are easier to compare:

tail -n 50 app.log | sed 's/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/DATE/g'
DATE ERROR: Unable to connect to database.
DATE ERROR: Timeout occurred.

The awk '/error/' pattern is case-sensitive. If logs mix error, Error, and ERROR, use awk 'tolower($0) ~ /error/' or grep -i error so the case difference does not hide relevant lines.

Inspect Bytes with tail -c

Use -c when the file ending is not line-oriented, or when you need the last few bytes of a payload, checksum trailer, or generated binary file. Pipe the result through od for a readable hex and character view:

tail -c 16 payload.bin | od -An -tx1 -c
  0a  62  79  74  65  73  3d  31  30  32  34  0a  45  4e  44  21
  \n   b   y   t   e   s   =   1   0   2   4  \n   E   N   D   !

For plain text logs, prefer -n. Byte counts can split a line in the middle, which is useful for binary checks but less readable for normal log review.

Advanced tail Techniques

Stop Following When a Process Exits

GNU tail can stop automatically when the process writing the file exits. This is useful for builds, imports, and one-shot jobs where a normal tail -f would keep running after the work is done:

make >build.log 2>&1 & build_pid=$!
tail --pid "$build_pid" -f build.log

The process being watched and the tail command must run on the same machine. If the PID is wrong, reused, or not the process writing the file, tail may exit too early or too late.

Reduce Polling Frequency on Quiet Filesystems

On Linux, GNU tail -f usually uses inotify for prompt event-driven updates. The -s interval matters when polling is active, such as on some network filesystems, with some nonstandard files, or when paired with --pid checks:

tail -s 5 -f /mnt/nfs/app.log

Use a longer interval when near-instant updates do not matter. Use the default when you need errors to appear as soon as possible.

Read NUL-Delimited Records with tail -z

GNU tail -z treats NUL bytes as record separators instead of newlines. This is mainly useful in pipelines that preserve arbitrary file names or binary-safe records:

printf 'one\0two\0three\0' | tail -z -n 1 | tr '\0' '\n'
three

This option is GNU-specific. BusyBox tail does not document -z, so avoid it in scripts meant for minimal systems unless you have verified the target image.

Avoid Obsolete tail Syntax in Scripts

Older examples sometimes use compact forms such as tail -1 file.log or historical plus syntax. Prefer the explicit POSIX-style forms in new scripts:

tail -n 1 file.log
tail -n +4 file.log
tail -c 4 file.bin

The explicit forms are easier to read and avoid edge cases where older compatibility parsing can interpret an argument differently than you intended.

Troubleshoot Common tail Errors

Permission Denied When Reading Logs

Protected system logs often require elevated access. A regular user may see:

tail: cannot open '/var/log/syslog' for reading: Permission denied

Use sudo for one-off troubleshooting:

sudo tail -f /var/log/syslog

For repeated access, first check the file ownership and group instead of guessing the right group name:

ls -l /var/log/syslog
-rw-r----- 1 syslog adm 95342 Jan 02 14:20 /var/log/syslog

On Debian and Ubuntu systems where the log is group-readable by adm, add your user to that group, then log out and back in:

sudo usermod -aG adm "$USER"

Verify the new session sees the group before retrying without sudo:

groups
youruser adm sudo

Journal-only workflows may use a different access path, such as the systemd-journal group for journal reads. Check the file or journal access model on the target distribution before adding broad group memberships.

tail -f Stops Updating After Log Rotation

If a rotation tool renames the active log and creates a fresh file, tail -f can keep watching the old file descriptor. New entries go to the new path, but your terminal appears quiet.

Switch to -F for rotation-safe following:

tail -F /var/log/nginx/access.log

When GNU tail notices replacement, it prints a status line like:

tail: '/var/log/nginx/access.log' has been replaced; following new file

That message is a good sign during rotation, not a failure.

tail Reports No Such File or Directory

If the file does not exist yet, a normal read fails immediately:

tail: cannot open 'app.log' for reading: No such file or directory

If a service will create the file shortly, use GNU -F so tail keeps retrying:

tail -F app.log

If the file should already exist, verify the path first with ls -l app.log or check the service configuration that owns the log location.

Delayed Output When Piping tail Through grep or awk

When a live tail -f pipeline appears to pause even though the log is active, the downstream command may be buffering output.

For GNU grep, add --line-buffered:

tail -f app.log | grep --line-buffered "ERROR"

For tools that do not have their own line-buffering option, GNU stdbuf can force line-buffered output:

tail -f app.log | stdbuf -oL awk '/error/'

BusyBox and some non-GNU environments may not provide stdbuf. In that case, prefer a tool with an explicit line-buffering option or run the filter on saved output instead of a live stream.

GNU tail Options Fail on BusyBox

BusyBox systems usually reject GNU-only options such as --version, --pid, --follow=name, and -z:

tail: unrecognized option: version

Confirm the applet option list:

busybox tail --help

If the script needs GNU behavior, install coreutils and confirm that the shell resolves tail to the GNU binary:

command -v tail
tail --version | head -n 1

For portable scripts, stick to the BusyBox-documented options unless you control the target image.

Conclusion

The tail command is the fastest way to inspect the end of a file, watch logs as they change, and trim noisy command output to the part that matters. Use -n for line counts, -f for live files, -F for rotated logs, -c for byte-level checks, and GNU-only options such as --pid or -z only when the target system actually provides GNU coreutils.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: