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.logfollows 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 lastNlines instead of ten.tail -n 100 file.-n +N— the sign flips the meaning entirely. With a leading+,tailcounts from the top:tail -n +2means "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+Nworks onheadtoo.-c N— count bytes instead of lines.tail -c 200gives you the last 200 bytes — handy when a "line" is a megabyte of JSON, or when there are no newlines at all.-f— follow. 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-Fis 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. Starttail --retry -F /var/log/app.logbefore 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, sleepNseconds 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 -fgoes 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 renamesapp.logtoapp.log.1every night and creates a fresh, emptyapp.login its place. Yourtail -fwas following the old file by its descriptor — so it's now loyally watchingapp.log.1, a file nothing writes to anymore, while all the new lines pour into a brand-newapp.logit's never heard of. The log looks dead. It isn't.-fis loyal to the tenant, not the address; rotation is the tenant moving out, andtailkeeps knocking at an apartment that's already been re-let to someone who never gets mail.- The fix is
-F(capital). Where-ffollows the open file,-Ffollows the name: whenapp.loggets renamed and recreated,-Fnotices the file at that path is now a different one, lets go of the old, and re-opens the new.-Fis loyal to the address — whoever lives there now. For anything that survives a rotation,tail -F /var/log/app.logis what you actually want. Many people just always type-Fand 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.loga different inode.-fis bonded to the old inode;-Fwatches the name and follows it onto whichever inode currently wears it. (--follow=name --retryis the explicit long form of this, useful when the new file might appear a moment after the old one vanishes.) - Piping
tail -f | grepand seeing nothing? Nottail's fault —grepbuffers when its output isn't a terminal.grep --line-buffered(orstdbuf -oL) restores the live feel. -fon 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-Cto 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 bytestail -n +2 file— everything except the first line (drop a header)tail -f file— follow live;Ctrl-Cto stoptail -F file— follow live and survive log rotation (the one to default to)tail --retry -F file— start following before the file even existstail -f a.log b.log— follow several at once, with==> name <==headerstail -f file | grep --line-buffered PATTERN— live, filtered to what mattersjournalctl -u SERVICE -f— the systemd-eratail -ffor a managed service
See Also
head— the mirror twin; the first lines instead of the lastgrep— pair withtail -fto watch only the lines you care aboutless— page through a whole log;Shift-Finside it is its owntail -f- systemd — where
journalctl -flives, the modern log home - logrotate — the nightly file-swap that breaks plain
-f - inode — the real file under the filename, and why
-fand-Fdiffer - /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.