Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

diff is the reference command to detect changes between two files. It is the engine behind git diff, patch, and most code review tools. In system administration, you use it to compare configurations before/after, check migrations and propagate changes between servers.

The fundamentals guide covers the basic syntax. This guide focuses on real uses: unified format, recursive comparison and the diff → patch workflow.

What you will learn

  • Read diff's three output formats (normal, -u, -y)
  • Compare two whole directories with diff -r
  • Generate a patch file and apply it with patch
  • Use the return codes in a checking script

In which context?

You are putting a server back into production after an intervention. You want to make sure the configuration is exactly the one in place before, not "roughly the same", exactly the same. Or you must propagate a fix to a configuration file from one environment to another, without overwriting the local adaptations. Or again you manage dozens of servers and you suspect one of them has a configuration that has drifted. diff is the trusted tool in these situations: it says precisely what changed, line by line, without ambiguity. It is also the engine of git diff: both produce the same unified format, with the same @@ headers and the same line prefixes, so knowing how to read one means knowing how to read the other.

diff's output formats

diff can present the same difference in three ways, and the choice is not cosmetic: only the unified format carries enough information to be replayed by patch. The examples below compare two versions of an nginx configuration, v1.conf and v2.conf. The file passed as the first argument is always the old one, the second the new one; swapping them swaps the direction of additions and deletions.

Normal format (default)

Without options, diff only shows the lines that change, with no context line around them. It is the most compact format, but it is unusable by patch as soon as the target file has moved.

Terminal window
diff v1.conf v2.conf
2,3c2,3
< server_name example.com;
< listen 80;
---
> server_name example.com www.example.com;
> listen 443 ssl;
5,6c5,7
< worker_processes 2;
< access_log /var/log/nginx/access.log;
---
> worker_processes 4;
> access_log /var/log/nginx/access.log combined;
> error_log /var/log/nginx/error.log warn;

Reading: 2,3c2,3 means "lines 2-3 of the left file change to become lines 2-3 of the right file". Lines prefixed with < come from the left file, those with > from the right file.

The three possible actions are a (add), d (delete) and c (change).

Unified format, the most useful

The -u option adds two header lines with the file names and their modification dates (to the nanosecond, with the time zone), then surrounds each change with three lines of context that did not change. It is this context that lets patch find where to apply the change even if the line numbers have moved.

Terminal window
diff -u v1.conf v2.conf
--- v1.conf 2026-04-09 15:27:28.028159111 +0200
+++ v2.conf 2026-04-09 15:27:31.614052218 +0200
@@ -1,6 +1,7 @@
# Configuration nginx
-server_name example.com;
-listen 80;
+server_name example.com www.example.com;
+listen 443 ssl;
root /var/www/html;
-worker_processes 2;
-access_log /var/log/nginx/access.log;
+worker_processes 4;
+access_log /var/log/nginx/access.log combined;
+error_log /var/log/nginx/error.log warn;

It is the format of git diff. Each block starts with @@, which gives the line numbers. Lines with no prefix are the context (unchanged), - what disappears, + what arrives.

The context (3 lines by default) locates the change even if the file was modified in the meantime. You can adjust it with -U:

Terminal window
diff -u --unified=5 v1.conf v2.conf # 5 lines of context
diff -u --unified=0 v1.conf v2.conf # no context

Side-by-side format

The -y option shows the two files in two columns, identical lines included: the output is therefore as long as the file itself. It is for human reading on short files, never to produce a patch.

Terminal window
diff -y v1.conf v2.conf
# Configuration nginx # Configuration nginx
server_name example.com; | server_name example.com www.example.com;
listen 80; | listen 443 ssl;
root /var/www/html; root /var/www/html;
worker_processes 2; | worker_processes 4;
access_log /var/log/nginx/access.log; | access_log /var/log/nginx/access.log combined;
> error_log /var/log/nginx/error.log warn;

The | marks a changed line, > a line added on the right, < a line removed on the right.

Compare two directories

Comparing two trees answers two different questions: which files exist on one side only, and which ones exist on both sides but differ. diff -r handles both in a single pass. Beware, it compares the content and nothing else: neither permissions, nor owner, nor dates are examined.

diff -r, recursive

The -r option descends into every subdirectory and compares the files with the same relative path.

Terminal window
diff -r dir_v1 dir_v2
Only in dir_v2: extra.conf
diff -r dir_v1/nginx.conf dir_v2/nginx.conf
2,3c2,3
< server_name example.com;
...
Only in dir_v1: script_v1.sh
Only in dir_v2: script_v2.sh

diff -r also reports the files present in only one of the two directories (Only in). It is useful to detect a configuration drift between two servers.

To get just the list of differences without the details:

Terminal window
diff -rq dir_v1 dir_v2
Only in dir_v2: extra.conf
Files dir_v1/nginx.conf and dir_v2/nginx.conf differ
Only in dir_v1: script_v1.sh
Only in dir_v2: script_v2.sh

Ignore some files

--exclude takes a file name pattern, not a path, and can be repeated as many times as needed.

Terminal window
diff -r --exclude='*.log' --exclude='*.pid' dir_v1 dir_v2

Useful to compare application directories while ignoring the runtime files, which change at every start.

Return codes

diff returns an exit code usable in scripts:

CodeMeaning
0Identical files
1Different files
2Error (file not found, etc.)
Terminal window
if diff -q reference_file.conf current_file.conf > /dev/null 2>&1; then
echo "Configuration consistent"
else
echo "ALERT: drift detected"
diff -u reference_file.conf current_file.conf
fi

The diff → patch workflow

The diff -u → patch workflow is used to propagate a change from one file to another, even when the two have diverged, or to distribute a fix.

  1. Generate the patch

    Terminal window
    diff -u v1.conf v2.conf > changes.patch

    The changes.patch file contains all the changes in unified format.

  2. Check the patch before applying it

    Terminal window
    patch --dry-run v1.conf < changes.patch

    The --dry-run option simulates without modifying the file. Useful to validate before touching production.

  3. Apply the patch

    Terminal window
    patch -b v1.conf < changes.patch

    The -b (--backup) option keeps the original version in v1.conf.orig. Without it, patch creates no backup: the file is modified in place, with no safety net. It is a widespread mistake to believe the backup is automatic.

  4. Check the result

    Terminal window
    diff v1.conf v2.conf # must be empty

Undo a patch

Rolling back requires the original patch file: without it, no way back is possible, unless you have the backup produced by -b.

Terminal window
patch -R v1.conf < changes.patch

The -R option applies the patch in reverse (rollback).

Multi-file patches

For a whole project:

Terminal window
# Generate (-N includes the files present on one side only)
diff -ruN dir_v1/ dir_v2/ > project.patch
# Apply from the project root
patch -p1 < project.patch

Useful options in administration

On configuration files, the noise rarely comes from the substance: reworked indentation, an added empty line, carriage returns from a Windows editor. These options filter out this noise to keep only the real differences. Do not use them to generate a patch: a diff produced with -w or -B no longer reflects the file as it is, and patch will fail.

OptionEffect
-iIgnores case
-wIgnores all whitespace
-bIgnores changes in the amount of whitespace, end of line included
-ZIgnores trailing whitespace only
-BIgnores empty lines
--strip-trailing-crIgnores \r (Windows files)
Terminal window
# Compare two configs ignoring comments and empty lines
grep -v '^#' v1.conf | grep -v '^$' > v1_clean.conf
grep -v '^#' v2.conf | grep -v '^$' > v2_clean.conf
diff v1_clean.conf v2_clean.conf

Administration use cases

The three situations below cover most of the daily use of diff in operations: checking what an intervention really changed, confronting two servers meant to be identical, and measuring the gap between a server's real state and its versioned reference.

Check a configuration migration

The reference copy must be taken before any change: without it, there is nothing left to compare once the intervention is over.

Terminal window
# Before migration: back up
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
# After migration: see what changed
diff -u /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf

Compare the config between two servers

diff only works on local files: you must therefore bring both versions back first. A remote cat is fine for a readable configuration file, but loses the permissions and the owner.

Terminal window
ssh server1 'cat /etc/nginx/nginx.conf' > nginx_server1.conf
ssh server2 'cat /etc/nginx/nginx.conf' > nginx_server2.conf
diff -u nginx_server1.conf nginx_server2.conf

Detect the drift of a configuration directory

The -q option only lists the files that differ, which gives a short report, usable in a periodic check. Remember to exclude the .git directory of the reference copy, otherwise it always shows up as Only in.

Terminal window
# Versioned reference in a Git repository
diff -rq /etc/nginx/ ~/git/infra/nginx/

Key points

  • diff -u is the standard format for patches and reviews: read it like a git diff.
  • diff -r compares whole directories and reports missing files.
  • The diff -u → patch → patch -R workflow propagates or undoes a change in a controlled way.
  • The 0/1/2 return code is usable in checking scripts.
  • Without -u, patch lacks context: always use diff -u to generate a patch, and diff -ruN for a whole project.

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn