The dd command in Linux is primarily used to convert and copy files. Its name comes from the historical IBM Job Control Language DD statement, not reliably from “data duplicator” or “disk destroyer.” Its usefulness comes from the simple fact that files, devices, and pseudo-devices all fit the same model. GNU Coreutils dd

It can be used to copy files, backup and restore an entire hard disk, create blank files, create ISO images, and perform other low-level storage tasks. What follows are the practical examples I return to.

Basic Syntax of dd

dd if=input-file of=output-file bs=4M status=progress

if is the input and of is the output. That tiny distinction matters a great deal when one of them is a disk.

GNU dd uses binary suffixes here: 1M is 1,048,576 bytes (MiB). Use MB when I specifically need 1,000,000-byte units.

of= is overwritten without asking. Before every device write I resolve a stable /dev/disk/by-id/... path, compare model/serial/size in lsblk, unmount its filesystems, and type the destination explicitly.

What Each Operand Means

Unlike many commands, dd mostly uses name=value operands instead of options beginning with a dash. The basic flow is always input, transformation, output:

OperandMeaning
if=input-fileRead from this input file or device. Without if, dd reads standard input.
of=output-fileWrite to this output file or device. Without of, dd writes to standard output.
bs=BYTESRead and write blocks of this size. It sets both the input and output block size.
count=NStop after copying N input blocks. With bs=1M count=100, that is normally 100 MiB.
skip=NSkip N input blocks before copying.
seek=NSkip N output blocks before writing. Unlike skip, this moves within the destination.
status=progressAsk GNU dd to print periodic transfer statistics while it runs.
conv=fsyncFlush output data and metadata to the device before dd exits.

The block size affects how much data each read and write attempts to move. It can affect performance, but a larger value does not make the copy more accurate. It also changes what count, skip, and seek mean, because those values count blocks rather than bytes by default.

The input and output can be ordinary files, whole disks, partitions, or special devices. /dev/zero supplies an endless stream of zero bytes. /dev/urandom supplies pseudorandom bytes. A path such as /dev/sr0 or /dev/disk/by-id/... exposes a device through the same file interface.

Reading dd’s Output

At completion, a GNU dd run has this general shape:

1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, [elapsed] s, [rate] MB/s
  • records in reports input blocks read. 1024+0 means 1,024 complete blocks and zero partial blocks.
  • records out reports output blocks written. A non-zero number after the plus sign means the final read or write was shorter than the requested block size.
  • 1073741824 bytes is the actual byte count. GNU dd also shows decimal and binary human-readable forms, here 1.1 GB and 1.0 GiB.
  • The final values are elapsed time and average transfer rate. They describe the whole run, not necessarily the device’s sustained hardware speed because caching can affect them.

With status=progress, GNU dd also updates a temporary progress line while copying. Its normal statistics go to standard error, not standard output. That matters when redirecting or capturing command output.

Image an Optical Disc

lsblk -o NAME,TYPE,SIZE,MODEL
dd if=/dev/sr0 of=cdrom.iso bs=2048 status=progress conv=fsync

Create Allocated Test Files

One GiB of zeros:

dd if=/dev/zero of=file-1GiB.img bs=1M count=1024 status=progress

This can be useful for testing, benchmarking, preallocating space, creating a disk image, or preparing a swap file. For ordinary preallocation, fallocate -l 1GiB file.img is faster and clearer. dd is useful when I specifically need written zero blocks.

One GiB from the kernel’s cryptographic pseudorandom generator:

dd if=/dev/urandom of=random-1GiB.img bs=1M count=1024 status=progress

This is useful as random test input, but I use platform cryptographic APIs or dedicated key-generation tools for keys. Rewriting a modern SSD with random bytes is not a reliable sanitization method because wear leveling and remapped blocks may retain data.

Create a Swap File

sudo dd if=/dev/zero of=/swapfile bs=1M count=1024 status=progress
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Then add /swapfile none swap sw 0 0 to /etc/fstab. The 0600 mode prevents other users reading memory pages written to swap.

Clone a Healthy Disk

First resolve both stable paths:

ls -l /dev/disk/by-id/
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINTS

After unmounting the source and destination filesystems:

sudo dd \
  if=/dev/disk/by-id/SOURCE_ID \
  of=/dev/disk/by-id/DESTINATION_ID \
  bs=16M status=progress conv=fsync

The destination must be at least as large as the source. conv=fsync flushes output data before dd exits; oflag=sync instead makes each output write synchronous and is usually much slower. I verify independently, for example by comparing a read-back hash when both devices are stable:

source_bytes=$(sudo blockdev --getsize64 /dev/disk/by-id/SOURCE_ID)
sudo cmp --bytes="$source_bytes" \
  /dev/disk/by-id/SOURCE_ID /dev/disk/by-id/DESTINATION_ID

For a failing disk I use GNU ddrescue, not conv=noerror,sync: its mapfile records what was read and supports efficient retries. GNU ddrescue manual

sudo ddrescue -f -n /dev/disk/by-id/SOURCE_ID disk.img disk.map
sudo ddrescue -d -f -r3 /dev/disk/by-id/SOURCE_ID disk.img disk.map

Write a Bootable USB Image

sudo dd if=linux.iso of=/dev/disk/by-id/USB_ID bs=16M status=progress conv=fsync

This works only for images designed to be written to a whole device. The target is the disk, not one of its partitions.

Copy or Inspect the Beginning of a Disk

Copying only the first part of a disk can be useful for inspecting early boot structures, a small forensic capture, or a very specific recovery job. It is not a complete backup of a modern partitioned disk.

Copy the first 100 MiB:

sudo dd if=/dev/disk/by-id/DISK_ID of=first-100MiB.img bs=1M count=100 status=progress

Here is that command one piece at a time:

  • if=/dev/disk/by-id/DISK_ID selects the disk as the input.
  • of=first-100MiB.img selects an ordinary image file as the output.
  • bs=1M uses 1 MiB input and output blocks.
  • count=100 copies at most 100 of those blocks, for 100 MiB in total when every block is full.
  • status=progress displays the number of bytes copied while the command runs.

That is a byte-range snapshot, not a general filesystem or boot backup. It may include an MBR and early boot data, but GPT keeps a backup header/table at the end of the disk, and boot files can live anywhere in a filesystem.

For a legacy MBR sector specifically:

sudo dd if=/dev/disk/by-id/DISK_ID of=mbr-sector.img bs=512 count=1
hexdump -C mbr-sector.img

The classic layout uses bytes 0–445 for boot code, 446–509 for four partition entries, and 510–511 for the 0x55aa signature. That description does not apply to a modern GPT as a complete backup.

To inspect the sector directly instead of saving it first:

sudo dd if=/dev/disk/by-id/DISK_ID bs=512 count=1 status=none | hexdump -C

The hexadecimal offsets down the left side identify positions within the 512-byte sector. Near the end:

  • Offset 0x1be, decimal byte 446, begins the 64-byte partition table.
  • The table contains four 16-byte partition records.
  • Each record includes the boot indicator, legacy starting and ending CHS addresses, partition type, starting LBA, and total sector count.
  • Offsets 0x1fe and 0x1ff, decimal bytes 510 and 511, normally contain 55 aa.

If I only want the four partition records, I can extract exactly those 64 bytes:

sudo dd if=/dev/disk/by-id/DISK_ID bs=1 skip=446 count=64 status=none | hexdump -C

That raw structure is useful for learning and forensic inspection. For a readable partition report, fdisk or parted is usually the better tool.

Clear Signatures or Sanitize a Device

If I only need to prepare a disk for repartitioning, I prefer a tool that states that intent:

sudo wipefs --no-act /dev/disk/by-id/DISK_ID
sudo wipefs --all /dev/disk/by-id/DISK_ID

The first command previews; the second removes recognized signatures. For actual SSD sanitization I use the device’s supported NVMe sanitize/format or ATA secure-erase facility after checking vendor guidance. Those commands are destructive and device-specific; a small dd overwrite is not equivalent.

dd remains excellent for exact byte copies. The important modern improvement is identifying the destination safely and using a purpose-built tool when recovery, filesystem consistency, or device sanitization is the real task.



Buy Me a Coffee