tail Command in Linux (With Examples)

Last updated Friday, February 6, 2026 9:09 am Joshua James 18 min read

The tail command is your go-to utility for viewing the end of files and monitoring logs in real time. When you need the last 50 error log entries from a crashing web server, want to watch authentication attempts as they happen, or need to track deployment logs during a live release, tail gives you instant access to the freshest data without opening massive files in an editor.

Beyond simply printing the last few lines, tail handles real-time log following with automatic rotation detection, byte-level binary inspection, multi-file monitoring, and filtering pipelines through grep, awk, and sed. The examples below progress from basic line output through production-ready monitoring workflows you can adapt to your own servers and scripts.

Understand the tail Command

tail Command Basics

Think of tail as a spotlight that shows the end of a file without loading the entire thing. While a text editor like nano or vim opens the whole file (which can take seconds or minutes for multi-gigabyte logs), tail seeks directly to the end and shows just what you need. Its complement, head, does the same for the beginning of a file, and together they let you isolate any section without opening an editor.

The basic syntax follows this pattern:

tail [OPTIONS] [FILE]
  • OPTIONS: Flags that modify behavior (e.g., -n 20 for the last 20 lines, -f to follow in real time)
  • FILE: The file to read. Omit this to read from standard input (useful when piping from other commands)

For instance, tail /var/log/syslog displays the last 10 lines of the system log (10 is the default). Need more context? Add -n 50 to see the last 50 lines. Want live updates? Add -f to watch new entries appear as the system writes them.

On systems using the systemd journal for logging, journalctl -f or journalctl -u servicename -f provides a similar live feed from the journal. The examples in this guide use traditional log files, but the same piping and filtering techniques work with journalctl output.

Essential tail Options by Task

The table below organizes common options by task so you can quickly find the right flag for your goal:

TaskOptionsWhat They Do
View specific number of lines-n NUMBERShows the last NUMBER lines instead of default 10 (e.g., tail -n 100 app.log)
Start output from line N-n +NUMBEROutputs from line NUMBER onward instead of the last N lines (e.g., tail -n +1 file prints the entire file)
View specific bytes-c BYTESShows the last BYTES bytes, useful for binary files or checking file endings (e.g., tail -c 50 data.bin)
Follow file in real time-fWatches file and prints new lines as they are written; press Ctrl+C to stop (e.g., tail -f /var/log/nginx/access.log)
Follow with rotation handling-FLike -f but reopens file if renamed (log rotation safe); equivalent to --follow=name --retry
Monitor multiple files-f file1 file2Follows multiple files, printing headers to distinguish sources (e.g., tail -f access.log error.log)
Suppress headers-qHides file name headers when viewing multiple files (cleaner output for scripts)
Force headers-vAlways shows file name headers, even for single files (useful for clarity in pipelines)
Stop following when process exits--pid PIDStops tail when process PID terminates; GNU coreutils only, not supported on BusyBox (e.g., tail --pid 1234 -f app.log)
Adjust polling interval-s SECONDSSets the sleep interval between checks when polling; on Linux, tail -f uses inotify by default, so -s applies when inotify is unavailable (e.g., network filesystems) or with --pid

When “optional” matters: Most tail options are truly optional since tail works fine without them, but scenarios like live log monitoring (-f), handling rotated logs (-F), or inspecting binary data (-c) make specific options essential for those tasks. Start with the basics (tail -n and tail -f) and add others as your monitoring workflows demand them.

Install the tail Command

The tail command ships as part of the GNU coreutils package on all modern Linux distributions, meaning it comes pre-installed on virtually every system. If you encounter a minimal container or embedded system without it, you can add coreutils with a single package command.

Verify tail is available:

tail --version
tail (GNU coreutils) 9.4
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later.

If the command returns version information like the output above, you are ready to use tail. The version number varies by distribution (e.g., 9.4 on Ubuntu 24.04, 9.7 on Debian 13, 9.7 on Fedora 43). If the command is not found, install coreutils for your distribution:

Ubuntu and Debian-based Distributions

sudo apt install coreutils

Fedora, RHEL, Rocky Linux, and AlmaLinux

sudo dnf install coreutils

Arch Linux and Manjaro

sudo pacman -S coreutils

openSUSE

sudo zypper install coreutils

Alpine Linux

sudo apk add coreutils

Alpine Linux and other minimal distributions ship BusyBox tail by default. BusyBox tail supports core options like -n, -f, -F, -c, -q, -v, and -s, but lacks long options such as --version and --pid. Install the coreutils package to get the full GNU tail with all options documented in this guide.

In practice, you will rarely need to install tail manually since coreutils is a fundamental dependency for virtually all Linux systems.

Practical tail Command Examples

Example 1: View Recent Log Entries

When you need the latest entries in a log file, tail shows you exactly what you need without loading the entire file. This makes troubleshooting faster, whether you are diagnosing system crashes or tracking application behavior.

Scenario 1: Troubleshooting a Web Server Crash

You are troubleshooting an Apache web server crash and need to check the last 20 lines of the error log to find the cause.

Command (Debian/Ubuntu):

tail -n 20 /var/log/apache2/error.log

Command (RHEL/Fedora/Rocky Linux):

tail -n 20 /var/log/httpd/error_log

Output:

[Wed Jan 02 12:45:10.123456] [error] AH00128: File does not exist: /var/www/html/favicon.ico
[Wed Jan 02 12:46:02.654321] [error] AH00037: ModSecurity: Request denied with code 403

Why It Matters:

  • Shows recent errors that might have caused the crash.
  • Saves time by avoiding large file loads.

Scenario 2: Verifying System Boot Logs

After a reboot, you want to check if any warnings or errors occurred during the startup process.

Command (Debian/Ubuntu):

tail -n 10 /var/log/syslog

Command (RHEL/Fedora/Rocky Linux):

tail -n 10 /var/log/messages

Output:

Jan 02 12:50:00 myhost systemd[1]: Starting Cleanup of Temporary Directories...
Jan 02 12:50:10 myhost systemd[1]: Finished Cleanup of Temporary Directories.
Jan 02 12:50:15 myhost kernel: [12345.678] Warning: Disk usage nearing capacity.
  • Shows system health right after reboot.
  • Helps you catch warnings like low disk space before they become problems.

Scenario 3: Checking Application Logs After an Update

You have updated an application and need to ensure the latest changes were applied successfully without introducing errors.

Command:

tail -n 15 /var/log/app.log

Output:

Jan 02 12:55:00 [INFO] Application update started.
Jan 02 12:55:10 [INFO] Feature X deployed successfully.
Jan 02 12:55:15 [ERROR] Feature X: Missing configuration file.

Why It Matters:

  • Confirms if updates completed successfully and highlights any errors that need attention.
  • Focuses on recent events, making deployment debugging faster.

Example 2: Monitor Logs in Real Time

The -f (follow) option lets you watch files in real time. This is crucial for monitoring live log updates, diagnosing ongoing issues, and making sure systems run as expected. Press Ctrl+C when you are ready to stop following a file.

If you are monitoring log files that might be rotated (e.g., renamed by a log rotation utility), the default -f option will continue to follow the original file descriptor, which might point to the renamed (and no longer active) file. In such cases, use tail -F. The -F option is equivalent to --follow=name --retry; it will follow the filename and automatically reopen the file if it gets recreated or renamed, making it more robust for production log monitoring.

Scenario 1: Monitoring Web Traffic in Real Time

You are managing an NGINX web server and need to monitor incoming traffic to identify user activity and potential issues.

Command:

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

Output (Real-Time Updates):

192.168.1.2 - - [02/Jan/2025:13:05:10 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.3 - - [02/Jan/2025:13:05:15 +0000] "POST /api/login HTTP/1.1" 401 512

Why It Matters:

  • Lets you spot problematic trends immediately, such as repeated 401 errors from specific IPs.
  • Helps identify high-traffic endpoints or check server performance under load.

Scenario 2: Debugging Application Deployments

During a live application deployment, you want to monitor log files for errors and confirm that the application starts successfully.

Command:

tail -f /var/log/app.log

Output (Real-Time Updates):

Jan 02 13:06:00 [INFO] Starting application...
Jan 02 13:06:10 [INFO] Listening on port 8080.
Jan 02 13:06:15 [ERROR] Failed to load configuration file.
  • Catches and fixes deployment issues quickly, minimizing downtime.
  • Shows a clear timeline of events during startup, helping you match logs with user-reported problems.

Scenario 3: Analyzing Temporary Files

You are debugging a script that writes logs to a temporary file, and you need to monitor the file’s output as it is updated.

Command:

tail -f /tmp/debug-output.log

Output:

[INFO] Script execution started.
[WARNING] Missing configuration detected.
[ERROR] Operation failed due to invalid input.

Why It Matters:

  • Temporary files are often used by scripts for debugging or intermediate storage.
  • Monitoring these files in real time helps you catch and resolve issues during execution without needing persistent logs.

Example 3: Analyze Files by Bytes

The -c option shows you the last portion of a file by bytes instead of lines. This helps when working with binary files, debugging corrupted data, or analyzing truncated logs.

Scenario 1: Debugging a Corrupted Binary File

You need to inspect the last 100 bytes of a corrupted binary log file to identify potential data loss or errors.

Command:

tail -c 100 /var/log/database.bin

Running this command outputs raw bytes, which appear as garbled or unprintable characters in the terminal. To interpret the bytes as hexadecimal, pipe the output through xxd or hexdump -C:

tail -c 100 /var/log/database.bin | xxd

Output (Hexadecimal View):

00000000: ef4b 2135 007f 3d2a 8b1c 4e72 a903 f5d1  .K!5..=*..Nr....

Why It Matters:

  • Pinpoints the specific byte range causing errors in file processing.
  • Gives you a starting point for recovery tools or debugging workflows.

Scenario 2: Inspecting Truncated Log Files

You are analyzing an incomplete log file and need to verify if the final portion contains critical entries.

Command:

tail -c 200 /var/log/server.log

Output:

Jan 02 15:00:00 Service started
Jan 02 15:05:45 Connection to database lost
Jan 02 15:06:00 Attempting reconnection...
  • Shows quickly whether key information exists in a truncated log, saving time during troubleshooting.
  • Lets you inspect only relevant parts of oversized logs efficiently.

Scenario 3: Validating Binary Data

You are debugging a networked application that writes raw binary payloads to disk, and analyzing the last bytes can confirm if data was transmitted correctly.

Command:

tail -c 50 /var/log/network_payload.bin | xxd

Output (Hexadecimal View):

00000000: 7856 3412 ffaa 99cc 0011 2233 4455 6677  xV4.......3DUfw

Why It Matters:

  • Helps identify anomalies or corrupted bytes in network transmissions.
  • Aids in debugging file transfer protocols or compression algorithms.

Example 4: Monitor Multiple Files

The tail command lets you monitor multiple files at once, making it invaluable for troubleshooting systems with interconnected logs. Each file’s output shows with a header, clearly marking its source.

Scenario 1: Monitoring Access and Error Logs Side-by-Side

You are debugging a web application and need to monitor NGINX access and error logs concurrently to identify how user requests correlate with errors.

Command:

tail -f /var/log/nginx/access.log /var/log/nginx/error.log

Output:

==> /var/log/nginx/access.log <==
192.168.1.1 - - [02/Jan/2025:14:30:11] "GET /index.html HTTP/1.1" 200 1234
192.168.1.2 - - [02/Jan/2025:14:30:15] "POST /api/login HTTP/1.1" 403 512

==> /var/log/nginx/error.log <==
2025/01/02 14:30:11 [error] 1234#0: *1 open() "/var/www/html/missing.html" failed (2: No such file or directory)
2025/01/02 14:30:15 [error] 5678#0: *2 client sent invalid request body
  • This workflow helps diagnose issues in real time during heavy traffic or while debugging deployments.
  • Matching access and error logs helps pinpoint which user requests trigger server errors.

Scenario 2: Comparing Logs Across Services

You are managing a multi-service application and need to monitor both the application’s backend logs and database logs to troubleshoot an issue.

Command:

tail -f /var/log/app.log /var/log/db.log

Output:

==> /var/log/app.log <==
Jan 02 14:31:00 [INFO] User submitted form on /contact.
Jan 02 14:31:02 [ERROR] Database query timeout.

==> /var/log/db.log <==
Jan 02 14:31:00 Query received: SELECT * FROM contacts WHERE id=5;
Jan 02 14:31:02 Query failed: Connection timeout.

Why It Matters:

  • Shows how one service’s issue impacts another by monitoring both logs at once.
  • This is crucial for troubleshooting complex systems with dependent services.

Scenario 3: Tracking System and Security Logs

You are investigating a security breach and need to analyze both system logs and authentication logs in real time.

Command (Debian/Ubuntu):

tail -f /var/log/syslog /var/log/auth.log

Command (RHEL/Fedora/Rocky Linux):

tail -f /var/log/messages /var/log/secure

Output:

==> /var/log/syslog <==
Jan 02 14:32:15 myhost systemd[1]: Starting Update Job...
Jan 02 14:32:17 myhost kernel: [12345.678] Warning: Unusual disk activity detected.

==> /var/log/auth.log <==
Jan 02 14:32:16 myhost sshd[1238]: Failed password for invalid user guest from 192.168.1.105
Jan 02 14:32:20 myhost sshd[1239]: Accepted password for admin from 10.0.0.10
  • Combining logs lets you see system-level activities (like disk warnings) alongside user-level actions (like login attempts).
  • This complete view helps you identify and mitigate potential breaches.

Example 5: Filter Log Entries with grep

The grep command guide covers text search in depth, and when combined with tail, grep lets you filter log entries precisely in real time. This workflow helps when working with large logs where finding specific information quickly matters.

Scenario 1: Monitoring Login Failures in Real Time

You are investigating unauthorized access attempts on your system and want to track login failures as they happen.

Command (Debian/Ubuntu):

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

The --line-buffered flag forces grep to flush each matching line immediately so real-time monitoring stays responsive.

Command (RHEL/Fedora/Rocky Linux):

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

Output:

Jan 02 14:15:30 myhost sshd[1234]: Failed password for invalid user admin from 192.168.1.100
Jan 02 14:16:05 myhost sshd[1235]: Failed password for root from 10.0.0.5

Why It Matters:

  • This workflow surfaces suspicious login attempts immediately, letting you respond faster to potential security threats.
  • You can monitor specific keywords (like “Failed password”) in real time without sifting through irrelevant log entries.

Scenario 2: Tracking Specific User Activity

You want to filter authentication logs for actions involving a specific user, such as johndoe.

Command (Debian/Ubuntu):

tail -n 100 /var/log/auth.log | grep "johndoe"

Command (RHEL/Fedora/Rocky Linux):

tail -n 100 /var/log/secure | grep "johndoe"

Output:

Jan 02 14:17:00 myhost sshd[1236]: Accepted password for johndoe from 192.168.1.101
Jan 02 14:19:15 myhost sshd[1237]: Failed password for johndoe from 10.0.0.6
  • Narrowing the scope to a single user allows you to track their activity comprehensively, such as login attempts or suspicious behavior.

Scenario 3: Identifying IP-Based Patterns

You are troubleshooting a DDoS attack and need to isolate repeated login attempts from a specific IP address.

Command (Debian/Ubuntu):

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

Command (RHEL/Fedora/Rocky Linux):

tail -f /var/log/secure | grep --line-buffered "192.168.1.100"

Output:

Jan 02 14:15:30 myhost sshd[1234]: Failed password for invalid user admin from 192.168.1.100
Jan 02 14:15:35 myhost sshd[1235]: Failed password for root from 192.168.1.100

Why It Matters:

  • Filtering by IP helps identify malicious sources and aids in blocking attackers at the firewall or server level.
  • This technique is vital during live incident response scenarios.

Example 6: Extract Data with awk and sed

Enhance tail workflows by processing its output with text-processing tools like awk and sed (stream editor). These utilities let you filter, manipulate, and format log data dynamically, making it easier to extract useful insights.

Scenario 1: Extracting Specific Fields from Log Data

You are monitoring an NGINX access log and want to isolate the IP addresses and URLs being requested.

Command:

tail -f /var/log/nginx/access.log | awk '{print $1, $7}'

Output:

192.168.1.1 /home.html
10.0.0.2 /login.html

Why It Matters: This workflow helps identify which clients (IP addresses) access specific resources (URLs) in real time. For example:

  • Detecting unusual traffic patterns or spikes in requests for specific endpoints.
  • Debugging issues caused by requests to specific files or APIs.

The awk '{print $1, $7}' command in this example assumes the standard NGINX combined log format where fields are separated by spaces, and the IP address is the first field ($1) and the requested URL path is the seventh field ($7). If your NGINX log_format is different, you may need to adjust the field numbers accordingly.

Scenario 2: Normalizing Log Formats for Analysis

You are comparing error patterns across different application versions, but the log format changed slightly between releases. Using sed to normalize timestamp formats helps you focus on actual differences.

Command:

tail -n 50 /var/log/app.log | sed 's/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/DATE/g'

Output:

DATE ERROR: Unable to connect to database.
DATE ERROR: Timeout occurred.
  • Replacing variable data like timestamps or request IDs makes it easier to spot patterns when comparing logs from different time periods or sources.
  • This technique works well when using diff tools or frequency analysis to find recurring issues.

When using sed for pattern matching, avoid non-POSIX regex like \s (which GNU sed treats as a literal ‘s’); use [[:space:]] instead for cross-platform compatibility.

Scenario 3: Filtering Error Messages from System Logs

You need to extract error messages, including their timestamps, from the last 50 lines of a system log.

Command (Debian/Ubuntu):

tail -n 50 /var/log/syslog | awk '/error/'

Command (RHEL/Fedora/Rocky Linux):

tail -n 50 /var/log/messages | awk '/error/'

The awk '/error/' pattern is case-sensitive. If your logs use mixed-case entries like “Error” or “ERROR”, use awk 'tolower($0) ~ /error/' to match all variations, or pipe through grep -i error instead.

When awk matches a pattern like /error/, it prints the entire matching line by default, so awk '/error/' is equivalent to awk '/error/ {print $0}'. The shorter form is preferred for readability.

Output:

Jan 02 14:15:12 myhost systemd[1]: error: Disk space low.
Jan 02 14:16:34 myhost kernel: error: Network unreachable.

Why It Matters:

  • This tailored command extracts only relevant error messages while retaining critical metadata (timestamps).
  • It helps pinpoint issues faster by showing exactly when and where errors occurred.

Advanced tail Techniques

Once you have the basics down, a few extra techniques can streamline daily operations and handle scenarios where simple tail -f is not enough.

Stop Following When a Process Exits

tail --pid 1234 -f /var/log/app.log

The --pid option stops tail automatically when the specified process terminates. This is ideal during service restarts or batch jobs where you want monitoring to end when the process finishes, rather than leaving a tail -f running indefinitely. Replace 1234 with the actual PID of the process you want to track. This option is GNU coreutils only and is not available on BusyBox.

Follow a File from the Beginning

tail -n +1 -F /var/log/app.log

Using -n +1 dumps the file from the first line, and combining it with -F keeps following the file while automatically reopening if log rotation swaps the underlying file. This is useful for capturing an entire log from the moment a service starts.

Reduce Polling Frequency on Quiet Systems

tail -s 5 -f /var/log/slow-service.log

On Linux, tail -f uses inotify (event-driven notifications) by default, so it reacts to changes instantly without polling. The -s flag sets the polling interval in seconds and takes effect when inotify is unavailable, such as on NFS-mounted filesystems or when combined with --pid. Setting a longer interval like 5 seconds saves CPU on systems where near-instant updates are not critical.

Combine tail with grep for Targeted Live Monitoring

tail -f /var/log/nginx/error.log | grep --line-buffered "upstream timed out"

When piping tail -f through grep, always use grep --line-buffered to ensure matches appear immediately rather than waiting for a buffer to fill. Without this flag, you may see long pauses between matching lines even though the log file is actively growing.

Troubleshoot Common tail Errors

Permission Denied When Reading Log Files

Many system logs are owned by root or the adm group, and attempting to read them as a regular user produces a permission error:

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

Use sudo to read protected log files:

sudo tail -f /var/log/syslog

Verify access by confirming the output appears. If you need regular access without sudo, add your user to the adm group:

sudo usermod -aG adm $USER

Log out and back in for the group change to take effect, then verify your membership:

groups
youruser : youruser adm sudo

Confirm adm appears in the output, then retry tail /var/log/syslog without sudo to verify the fix.

Buffering Delays When Piping tail Output

If you notice lag when piping tail -f output through grep or awk, the downstream command is likely buffering output in blocks rather than flushing line-by-line.

For grep, add --line-buffered:

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

For other commands like awk or sed that lack a built-in line-buffering flag, use stdbuf:

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

The stdbuf -oL command forces line buffering on the stdout of the following command, ensuring output appears immediately. After applying either fix, matching lines should appear within a second of being written to the log file rather than arriving in delayed bursts.

Log Rotation Causes tail -f to Stop Updating

When a log rotation utility (like logrotate) renames the active log file and creates a new one, tail -f continues following the old (now renamed) file descriptor. New log entries go to the new file, but tail does not see them.

Switch to tail -F to handle rotation automatically:

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

The -F option (equivalent to --follow=name --retry) tracks the filename rather than the file descriptor and automatically reopens the file when it is recreated. You will see a message like tail: '/var/log/nginx/access.log' has been replaced; following new file when rotation occurs.

tail –version Fails on Alpine or Minimal Systems

On Alpine Linux or other minimal distributions that use BusyBox, running tail --version produces an error:

tail: unrecognized option: version
BusyBox v1.37.0 (2025-01-01 00:00:00 UTC) multi-call binary.

BusyBox provides a lightweight tail that supports core options (-n, -f, -F, -c, -q, -v, -s) but does not support long options like --version or --pid. To get the full GNU tail:

sudo apk add coreutils

After installing, verify with tail --version to confirm GNU coreutils is now in use.

FAQ

What is the difference between tail -f and tail -F?

tail -f follows a file by its file descriptor, so if the file is renamed or rotated, tail keeps reading the old (now renamed) file. tail -F follows the filename instead and automatically reopens the file if it is replaced or recreated, making it the better choice for monitoring log files managed by logrotate or similar tools.

How do I display more than or fewer than 10 lines with tail?

Use the -n flag followed by the number of lines you want. For example, tail -n 50 file.log shows the last 50 lines. To show output starting from a specific line number onward, use a plus prefix: tail -n +100 file.log prints from line 100 to the end of the file.

Can tail read from standard input instead of a file?

Yes. When no file is specified, tail reads from standard input. This makes it useful in pipelines, for example: dmesg | tail -n 20 shows the last 20 lines of kernel messages, and cat /var/log/syslog | tail -n 5 shows the last 5 syslog entries.

What is the tail command in Linux?

The tail command is a core Linux utility (part of GNU coreutils) that prints the last part of a file. By default it shows the last 10 lines, but you can change this with the -n flag. Its most common use is monitoring log files in real time with tail -f, which continuously prints new lines as they are written to the file.

Is there a tail equivalent on Windows?

Windows does not include a native tail command, but PowerShell provides equivalent functionality. Use Get-Content -Tail 20 file.log to view the last 20 lines, or Get-Content -Wait file.log to follow a file in real time (similar to tail -f). On Windows Subsystem for Linux (WSL), the standard GNU tail command is available directly.

Conclusion

The tail command delivers instant visibility into log files, deployment outputs, and data streams without loading entire files into an editor. By combining options like -f for real-time monitoring, -F for rotation-safe following, and -c for byte-level inspection with piping workflows through grep, awk, and sed, you can diagnose issues faster, track security events as they happen, and build reliable monitoring scripts.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffee Buy 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:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="URL">link</a> link
<blockquote>quote</blockquote> quote block

Leave a Comment

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

Let us know you are human: