tail Command: Tutorial & Examples

Prints the last lines of a file, and can keep streaming new lines as they are written — the standard way to follow a growing log.

What It Is

tail does exactly what its name says: it shows you the end of a file. By default the last ten lines, and then it gets out of your way. On a server that one small ability is worth more than it sounds, because the most interesting part of almost every log file is the bottom — the newest entries, the thing that just happened.

But the reason every admin keeps tail within arm's reach isn't the static last-ten-lines trick. It's one flag — -f, for follow — that turns tail from a snapshot into a window. With -f, tail prints the end of the file and then doesn't stop. It sits there, and every time a new line is written to the file, it appears on your screen a heartbeat later. You are watching the log happen, live, as the program writes it.

If you've never run a server, start here: tail is completely safe — it only reads, it never changes a file or a process — and tail -f on a log is how you'll spend a surprising amount of your debugging life. We'll explain every flag, the one gotcha that bites everyone exactly once, and the modern systemd twist that changes where the logs even live.

Your First Look

Point it at a file and you get the last ten lines, nothing else:

tail /var/log/nginx/access.log
192.0.2.41 - - [15/Jun/2026:14:20:58 +0000] "GET /health HTTP/1.1" 200 2
203.0.113.7 - - [15/Jun/2026:14:20:59 +0000] "GET /login HTTP/1.1" 200 1840
198.51.100.22 - - [15/Jun/2026:14:21:01 +0000] "POST /api/orders HTTP/1.1" 500 73

Want a different number of lines? -n takes the count:

tail -n 50 /var/log/nginx/error.log

And the one you'll actually live in — -f — prints the tail and then stays open, streaming each new line as it's written:

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

That command doesn't return to your prompt. It can't — it's waiting. Leave it running in one terminal, go make the request you're debugging in another, and watch the line for your request scroll into view the instant the server handles it. Press Ctrl-C to stop following and get your shell back.

How I Use It

Here's what tail actually looks like in my hands, because it's less about flags than about a habit.

Almost every time, it's tail -f on a log while I do something else. Two terminals: one following the log, one making the thing happen. Restart a service in window A, watch it come up line by line in window B. Hit the broken endpoint, see the stack trace land. The whole loop — do a thing, watch the log react — is the core debugging move on a server, and tail -f is the window you watch it through.

When the log is busy, I narrow it with a pipe. A production nginx access log scrolls faster than anyone can read, so I follow it through grep and keep only the lines I care about:

tail -f /var/log/nginx/access.log | grep " 500 "

Now I'm watching only the 500 errors appear in real time — a quiet screen that lights up exactly when something breaks. (One subtlety: grep buffers its output when it's writing to a pipe rather than a terminal, so if a second pipe ever swallows your lines, reach for grep --line-buffered. That trap has cost more than one person an afternoon.)

When I just want the last few lines and out, it's plain tail or tail -n. "What was the last thing this thing logged before it died?" is a tail -n 50 question, answered in one line, no following required.

And when I want the opposite — the start of a file — I reach for its mirror twin, head. tail and head are a matched pair now: same flags, one reads from the bottom and one from the top, and if you learn one you've learned both. (They weren't born together, though — see the History below; tail is the elder by years.)

Pro Tip

Following several logs at once? tail -f app.log nginx.log mysql.log follows all of them in one window, and prints a ==> app.log <== header each time the source switches, so you can see which log a line came from. It's a cheap poor-man's dashboard when you're tracking a request as it crosses several services.

The Flags, Explained

Everything worth knowing, and a couple of things even seasoned admins half-remember:

  • -n N — show the last N lines instead of ten. tail -n 100 file.
  • -n +N — the sign flips the meaning entirely. With a leading +, tail counts from the top: tail -n +2 means "everything from line 2 onward" — i.e. skip the first line. It's the standard one-liner for dropping a CSV header before piping the rest onward. The same +N works on head too.
  • -c N — count bytes instead of lines. tail -c 200 gives you the last 200 bytes — handy when a "line" is a megabyte of JSON, or when there are no newlines at all.
  • -ffollow. Print the end, then keep reading forever as the file grows. The flag this whole page is about.
  • -F — follow, but tougher. The same live stream, except it survives the file being rotated out from under it (see Gotchas — this distinction is the single most useful thing on the page).
  • --follow=name — the long form of what -F is built on: follow the name on disk, not the open file. Pair it with --retry.
  • --retry — keep trying to open the file even if it doesn't exist yet. Start tail --retry -F /var/log/app.log before the app has created its log, and it'll latch on the moment the file appears.
  • --pid=PID — follow until a given process dies, then stop on its own. "Watch this build's log, and quit when the build finishes."
  • -q / -v — quiet or verbose: suppress or force the ==> filename <== headers when you pass multiple files.
  • -s N — with -f, sleep N seconds between checks instead of the default. Rarely needed; occasionally kind to a slow disk.

How Following Actually Works

Knowing this one thing is the difference between the people who get burned by log rotation and the people who don't.

A normal program reads a file from the top, hits the end, and stops, because there's nothing more to read. tail -f reads to the end and then keeps waiting, periodically asking the kernel "anything new?" When the writing program appends a line, the file grows, and tail reads the bytes that weren't there a moment ago. That's the whole trick. The writer and tail never speak to each other; they're two strangers reading the same growing file from opposite ends, and the only thing keeping them coherent is the kernel underneath them both.

Notice too what tail -f doesn't do: it never reads the whole file. Point it at a 40 GB log and it doesn't churn through 40 GB to find the bottom — it seeks straight to the end and starts there, instantly. That's why tail -n 5 on a giant file is immediate while cat-ing the same file would saturate a disk for a minute.

But that trick only works on a real file — something tail can rewind. Hand it a pipe instead (somecommand | tail -n 5) and it can't seek; a pipe has no end to jump to and no way to go back. So tail lives a completely different life: it reads the whole stream forward as it arrives, keeping only the last few lines in a small circular buffer, throwing away everything older, and prints what's left in the buffer when the stream finally closes. Same command, two utterly different mechanisms, decided entirely by whether the thing on its input can be rewound. It's also why tail -f on a pipe does nothing useful — there's no file on disk to keep checking for new bytes, just a stream that ends when it ends.

The thing to internalize is what -f is following. Plain -f follows the open file itself — technically the file descriptor, the kernel's handle to that specific chunk of data on disk. And that handle stays glued to the original data even if the filename gets pointed somewhere else. Which sets up the one trap everybody falls into exactly once.

Gotchas

  • tail -f goes silent after midnight — and it's log rotation, not a bug. This is the classic. Servers don't let logs grow forever; a tool called logrotate renames app.log to app.log.1 every night and creates a fresh, empty app.log in its place. Your tail -f was following the old file by its descriptor — so it's now loyally watching app.log.1, a file nothing writes to anymore, while all the new lines pour into a brand-new app.log it's never heard of. The log looks dead. It isn't. -f is loyal to the tenant, not the address; rotation is the tenant moving out, and tail keeps knocking at an apartment that's already been re-let to someone who never gets mail.
  • The fix is -F (capital). Where -f follows the open file, -F follows the name: when app.log gets renamed and recreated, -F notices the file at that path is now a different one, lets go of the old, and re-opens the new. -F is loyal to the address — whoever lives there now. For anything that survives a rotation, tail -F /var/log/app.log is what you actually want. Many people just always type -F and never think about it again — a defensible habit.
  • The mechanism behind it is the inode. A filename is only a label; the real file is the inode underneath it, and a rotation gives the new app.log a different inode. -f is bonded to the old inode; -F watches the name and follows it onto whichever inode currently wears it. (--follow=name --retry is the explicit long form of this, useful when the new file might appear a moment after the old one vanishes.)
  • Piping tail -f | grep and seeing nothing? Not tail's fault — grep buffers when its output isn't a terminal. grep --line-buffered (or stdbuf -oL) restores the live feel.
  • -f on a file that never grows just hangs there. That's correct behaviour, not a freeze — it's waiting for lines that aren't coming. Ctrl-C to escape.

The systemd Twist

On most modern Linux boxes the log you want isn't a file at all anymore.

For decades, services wrote plain-text logs into files under /var/log, and tail -f /var/log/whatever was the universal way to watch any of them. Then systemd arrived and brought the journal — a single structured, binary log that captures the output of every service it manages. You can't tail -f a binary journal; the bytes aren't lines. So systemd ships its own equivalent:

journalctl -u nginx -f

That's tail -f for the systemd era. -u nginx scopes it to one service's unit, and -f follows it live with the exact same feel — the screen sits quiet, then a new line lands the moment nginx logs it. Same instinct, same muscle memory, different plumbing underneath. So the real rule on a modern box is: if the service is managed by systemd, reach for journalctl -f; if it writes its own file (nginx and mysql often still do), tail -F the file. Knowing which world you're in saves you ten confused minutes staring at an empty terminal.

History and Philosophy

Here's the thing that stops you the first time you really look at tail: it came before head. The tool for the end of a file existed years before the tool for the beginning. That feels backwards until you sit with why, and then it's lovely.

The start of a stream is trivial. You want the first ten lines? Read ten lines and stop. There was barely anything to automate — the stream editor sed did it in a few keystrokes (sed 10q, which just means "quit after line 10"), and for a long time that's exactly what people typed. The earliest Unixes didn't ship a head command at all; to the people who built them, head was the tool you didn't need, because the easy thing doesn't deserve a program of its own.

The end is genuinely hard. You can't know which lines are the last ten until you've reached the end — and you don't know where the end is until you get there. So tail has to do real work: seek to the very end and read backward groping for newlines, or, when it can't seek, swallow the whole stream and remember only the tail of it. head automates something easy; tail automates something hard. So of course tail got written first — it was the one that was actually worth writing. It first showed up in 1977, in an early Unix called the Programmer's Workbench — a version built for a whole roomful of working programmers — with no single author's name on it, just the tool a group of people needed and built.

And you have to picture why anyone needed it. In the late seventies, "show me the file" often meant a printing terminal — a machine hammering characters onto fan-fold paper at about thirty a second, no screen, no scroll-back. Once a line was printed it was ink on a sheet on the floor. Dump a few hundred lines to check one thing at the bottom and you'd set the machine chattering for the better part of half an hour, a whole tree's worth of paper unspooling onto the floor, to read ten lines you could have had in a second. head and tail weren't conveniences. They were mercy — a way to not start a printer on a half-hour errand to glance at the end of a file. Every time you run tail -n 10 and get your answer instantly, you're collecting on a small kindness someone built into the system back when the alternative was a pile of warm paper.

There's one more gift tail quietly gave us. -f made tail a verb. Admins say "I'll tail the log" now, and everyone knows what they mean, because a logfile was never really a file with an end — it's a stream the program keeps writing to, with no last line until the program dies. tail -f is the honest version of looking at one: it stops pretending there's a bottom and just shows you the moving edge.

Cheat Sheet

The moves worth keeping:

  • tail file — last 10 lines · tail -n 50 file — last 50 · tail -c 200 file — last 200 bytes
  • tail -n +2 file — everything except the first line (drop a header)
  • tail -f file — follow live; Ctrl-C to stop
  • tail -F file — follow live and survive log rotation (the one to default to)
  • tail --retry -F file — start following before the file even exists
  • tail -f a.log b.log — follow several at once, with ==> name <== headers
  • tail -f file | grep --line-buffered PATTERN — live, filtered to what matters
  • journalctl -u SERVICE -f — the systemd-era tail -f for a managed service

See Also

  • head — the mirror twin; the first lines instead of the last
  • grep — pair with tail -f to watch only the lines you care about
  • less — page through a whole log; Shift-F inside it is its own tail -f
  • systemd — where journalctl -f lives, the modern log home
  • logrotate — the nightly file-swap that breaks plain -f
  • inode — the real file under the filename, and why -f and -F differ
  • /var/log — the traditional home of the files you'll be tailing

Tailing logs by hand at 3am to catch the thing that broke?

CleverUptime watches the symptoms behind those log lines — services flapping, restarts, load and memory spikes — and flags the underlying cause in plain language, so you're not the one staring at a scrolling terminal hoping to spot it.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →