dd Command in Linux: Complete Guide to Disk Duplication, Data Conversion, and Parameters

dd command in Linux and it perimeters

dd has a reputation, and it’s earned one. It’s the command people jokingly call “disk destroyer” because a single typo in the wrong direction can wipe a drive with zero confirmation prompts. I’ve been careful — genuinely careful — every time I’ve used it, and that caution is exactly why it’s stayed one of the most reliable tools in my kit for imaging drives, creating swap files, benchmarking storage, and generating test data. This guide walks through dd properly: syntax, parameters, internals, and the safety habits that keep it from living up to its nickname.

What Is the dd Command?

dd copies and optionally converts data at the byte level, reading from an input source and writing to an output destination, in fixed-size blocks. Unlike cp, which is filesystem-aware and copies files as logical units, dd operates on raw data streams — which is exactly why it can read and write entire disk devices, not just files, and why it’s the tool of choice whenever you need bit-for-bit control over how data moves.

The name itself is a bit of Unix folklore: it originally stood for “data definition,” borrowed from IBM JCL terminology, though these days people often joke it means “disk destroyer” given how unforgiving it can be.

Basic Syntax

Unlike most Unix commands, dd doesn’t use conventional -flag value syntax. Instead it uses key=value pairs:

dd if=INPUT_FILE of=OUTPUT_FILE [OPTION]...
  • if= — input file (defaults to standard input if omitted)
  • of= — output file (defaults to standard output if omitted)

A Basic Copy Example

$ dd if=file1.txt of=file1_copy.txt bs=4k
0+1 records in
0+1 records out
18 bytes copied, 3.4154e-05 s, 527 kB/s

The summary line tells you how many full blocks and partial blocks were read/written (0+1 means zero full 4K blocks plus one partial block), the total bytes copied, the time taken, and the throughput.

Full Parameter Reference

ParameterDescription
if=FILEInput file or device to read from
of=FILEOutput file or device to write to
bs=BYTESBlock size for both reading and writing (e.g. bs=1M)
ibs=BYTESInput block size only
obs=BYTESOutput block size only
count=NCopy only N input blocks, then stop
skip=NSkip N input blocks before starting to copy
seek=NSkip N output blocks before starting to write
conv=CONVSComma-separated list of conversions (e.g. ucase, lcase, notrunc, sync, noerror)
status=LEVELControl progress reporting: none, noxfer, progress
iflag=FLAGSInput flags, e.g. direct, sync, nonblock
oflag=FLAGSOutput flags, e.g. direct, sync, append

Setting Block Size with bs

Block size dramatically affects throughput, especially on real disks. A too-small block size (like the historical default of 512 bytes) issues many more syscalls than necessary; a reasonably large block size (1M, 4M) amortizes that overhead:

$ dd if=/dev/zero of=zerofile.img bs=1M count=5
5+0 records in
5+0 records out
5242880 bytes (5.2 MB, 5.0 MiB) copied, 0.0577879 s, 90.7 MB/s

Here bs=1M count=5 creates a precisely 5 MiB file filled with zero bytes — a common way to allocate a fixed-size disk image or swap file placeholder.

count and skip: Working with Specific Regions

You can extract a specific chunk of a file or device by combining skip (offset into the input) and count (how much to read):

$ dd if=zerofile.img of=partial.img bs=1M count=1 skip=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00646848 s, 162 MB/s

This skips the first 1 MiB of zerofile.img and copies the next 1 MiB into partial.img — useful for extracting a specific partition’s worth of bytes from a raw disk image, or inspecting a header at a known offset.

conv=: Data Conversion On the Fly

dd can transform data as it copies. A simple demonstration converting text to uppercase:

$ echo "hello world" > lower.txt
$ dd if=lower.txt of=upper.txt conv=ucase
0+1 records in
0+1 records out
12 bytes copied, 6.873e-05 s, 175 kB/s
$ cat upper.txt
HELLO WORLD

Common conv= values worth knowing:

  • ucase / lcase — convert text to upper/lower case
  • notrunc — do not truncate the output file before writing (essential when writing into the middle of an existing file or device)
  • sync — pad every input block to the size of ibs with zero bytes if it’s short
  • noerror — continue copying after read errors instead of aborting (critical for imaging damaged media)
  • fsync — physically write data to the output device before finishing

A very common real-world combination for recovering data off a failing drive is conv=noerror,sync, so that a bad sector doesn’t halt the whole copy and instead gets replaced with zero-padding to keep block alignment intact.

status=progress: Watching Large Copies in Real Time

For anything that takes more than a second or two, status=progress gives live feedback:

$ dd if=/dev/zero of=test2.img bs=1M count=2 status=progress
2+0 records in
2+0 records out
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.0108568 s, 193 MB/s

On a slow copy (like imaging a real disk), this updates continuously with bytes copied and current throughput, which is invaluable for gauging how much longer an operation will take.

How dd Works Internally

dd is deliberately simple at the systems level: it performs a loop of read() and write() syscalls directly against file descriptors, using the block size you specify as the buffer size for each syscall. There’s no filesystem-level intelligence involved — dd doesn’t know or care whether if= points to a regular file, a block device (/dev/sda), a character device (/dev/zero, /dev/urandom), or a named pipe. It just reads raw bytes and writes raw bytes.

This is precisely why dd can operate on block devices that tools like cp generally shouldn’t be pointed at directly: when you run dd if=/dev/sda of=/dev/sdb, you’re copying every byte on the disk — partition table, filesystem metadata, and all — rather than copying files understood at the filesystem layer. This makes dd the standard tool for:

  • Creating exact disk images for forensics or backup
  • Cloning drives byte-for-byte
  • Writing bootable ISO images to USB drives
  • Wiping drives by overwriting with zeros or random data

The bs=, ibs=, and obs= parameters control how large each read/write syscall’s buffer is. Larger blocks generally mean fewer syscalls and higher throughput, up to a point where you start hitting diminishing returns or memory pressure — for most modern SSDs and spinning disks, block sizes in the 1M–4M range hit a good balance.

The iflag=direct / oflag=direct options bypass the kernel’s page cache entirely, issuing I/O directly to the device. This is important when benchmarking real disk throughput, since without it, dd may appear to finish suspiciously fast because it’s writing into cache rather than to physical media — the write only actually completes once the cache is flushed, which oflag=direct (or conv=fsync) forces to happen as part of the measured operation.

Real-World Use Cases

1. Creating a Bootable USB Drive

$ sudo dd if=ubuntu-24.04-desktop-amd64.iso of=/dev/sdX bs=4M status=progress oflag=sync

Note the use of /dev/sdX (the whole device, not a partition like /dev/sdX1) and oflag=sync to ensure data is fully written before dd reports completion — critical before you physically remove the drive.

2. Cloning an Entire Disk

$ sudo dd if=/dev/sda of=/dev/sdb bs=4M status=progress conv=noerror,sync

3. Creating a Disk Image File for Backup

$ sudo dd if=/dev/sda of=/backups/sda-full-image.img bs=4M status=progress

4. Wiping a Drive Securely

$ sudo dd if=/dev/zero of=/dev/sdX bs=4M status=progress
$ sudo dd if=/dev/urandom of=/dev/sdX bs=4M status=progress   # more thorough, slower

5. Benchmarking Disk Write Speed

$ dd if=/dev/zero of=/tmp/testfile bs=1M count=1024 oflag=direct status=progress

6. Creating a Fixed-Size Swap File

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

7. Extracting the Master Boot Record

$ sudo dd if=/dev/sda of=mbr_backup.img bs=512 count=1

Shell Scripting and Automation

A defensive wrapper script I use before any disk-level dd operation, because the stakes of a wrong device name are too high to skip sanity checks:

#!/bin/bash
# safe_dd_image.sh - image a device with confirmation and progress
set -euo pipefail

SRC="$1"
DEST="$2"

if [[ ! -b "$SRC" ]]; then
    echo "Error: $SRC is not a block device" >&2
    exit 1
fi

echo "About to run: dd if=$SRC of=$DEST bs=4M status=progress conv=noerror,sync"
read -rp "Type the source device name to confirm ($SRC): " confirm
if [[ "$confirm" != "$SRC" ]]; then
    echo "Confirmation did not match. Aborting." >&2
    exit 1
fi

sudo dd if="$SRC" of="$DEST" bs=4M status=progress conv=noerror,sync
sync
echo "Done."

Requiring the operator to retype the exact device name is a small speed bump, but it has saved me from at least one near-miss where I had the source and destination reversed in my head.

dd vs Related Commands

CommandPurpose
ddLow-level, byte-for-byte copy and conversion; works on files and raw devices
cpFilesystem-aware file copy; won’t sensibly copy whole block devices
catCan concatenate/copy streams but lacks block-size control, seek/skip, and conversion options
dcflddA dd fork with built-in hashing and better progress reporting, popular in forensics
ddrescue (GNU ddrescue)Purpose-built for recovering data from failing/damaged drives, smarter retry logic than dd conv=noerror
rsyncFilesystem-level sync, incremental, much better for regular backups of live systems
pvOften piped alongside dd (`pv file

For actual disk recovery from failing media, ddrescue is genuinely a better tool than raw dd — it retries damaged sectors intelligently and maps which regions succeeded, whereas dd conv=noerror,sync just barrels through with zero-padding.

Troubleshooting Common Issues

Problem: dd seems to finish instantly, but the destination isn’t actually correct. This is almost always the page cache lying to you — dd reported completion once data was handed to the kernel, not once it hit physical media. Always run sync after a dd disk-write operation, and consider conv=fsync or oflag=direct.

Problem: “No space left on device” partway through. Check that your count/bs math doesn’t exceed the destination’s actual capacity, and remember of= to a file (not device) will happily try to write past available disk space.

Problem: dd hangs or is extremely slow. Usually a block size mismatch with the underlying device’s optimal I/O size, or you’re reading from a device like /dev/random (which blocks waiting for entropy) instead of /dev/urandom. Also check for a failing/degraded disk introducing retries at the hardware level.

Problem: Accidentally used the wrong of= and destroyed data. There’s no undo. This is why every dd invocation involving a real device deserves a second look at if= and of= before hitting enter — double-check with lsblk or sudo fdisk -l immediately beforehand to confirm device names haven’t shifted (which can happen after reboots or hot-plugging).

Performance Optimization

  • Use a larger block size (bs=1M to bs=4M) for whole-disk operations; the historical default of 512 bytes is far too small for modern hardware and will bottleneck on syscall overhead.
  • Use status=progress to monitor throughput and catch a stalled or abnormally slow copy early.
  • For pure benchmarking, use oflag=direct to bypass cache and measure real device throughput rather than memory speed.
  • When cloning to a device of the same or similar underlying block size, matching bs= to that device’s physical/logical sector size (often reported via blockdev --getbsz /dev/sdX) can improve alignment and speed.
  • For copying sparse files (files with large runs of zero bytes, like VM disk images) use conv=sparse to avoid writing out the zero regions physically, saving both time and destination space.

Security Implications

dd is a genuinely dangerous command from a data-safety perspective — it doesn’t ask “are you sure,” doesn’t check whether of= already contains meaningful data, and doesn’t distinguish between a scratch file and your primary boot partition. Always:

  • Double-check if= and of= values, especially device names, immediately before running the command.
  • Prefer /dev/disk/by-id/... paths over /dev/sdX when scripting against specific physical drives, since /dev/sdX naming can shift between boots.
  • Run destructive dd operations with the minimum privilege necessary — typically sudo for a single command rather than a root shell, to reduce the blast radius of any other mistake in the same session.
  • Be aware that dd used for “wiping” a drive with zeros is not the same as cryptographically secure erasure for SSDs — due to wear leveling, a single-pass zero-write may not touch every physical cell. For SSDs, ATA Secure Erase or manufacturer tools are more appropriate for guaranteed data destruction.
  • Disk images created with dd contain the full raw content of the source, including any deleted-but-not-overwritten data and any sensitive material — handle and store .img files with the same care as the original device.

Compatibility Across Distributions

dd is part of GNU coreutils and ships by default on every mainstream Linux distribution — Ubuntu, Debian, Fedora, RHEL, Arch, openSUSE. The core if=/of=/bs= syntax is consistent, though some conv= and iflag=/oflag= options are GNU extensions not present in the more minimal BSD dd found on macOS and FreeBSD (which also uses a slightly different flag vocabulary, e.g. bs=1m lowercase suffix conventions differ). Embedded and minimal environments (like BusyBox-based systems used in some containers and routers) ship a stripped-down dd with far fewer options — check dd --help or the equivalent before relying on advanced flags in those environments.

Best Practices

  • Always verify device names with lsblk, sudo fdisk -l, or blkid immediately before running a destructive dd command.
  • Use status=progress for anything non-trivial in size so you have visibility into whether the operation is proceeding normally.
  • Follow disk-write operations with sync to guarantee data has actually reached the physical device before removing media.
  • Prefer purpose-built tools (ddrescue for damaged media, rsync for regular backups, dedicated ISO-writing tools like balenaEtcher for less error-prone GUI workflows) when dd‘s raw power isn’t strictly necessary.
  • Never run dd against a device path you haven’t just re-verified in the same terminal session.

Summary

dd is a low-level, block-oriented copy tool that operates directly on file descriptors without any filesystem awareness, which is exactly what makes it indispensable for disk imaging, cloning, boot media creation, and raw data conversion — and exactly what makes it dangerous if you get the input or output target wrong. Understanding block size, the conv= and iflag=/oflag= options, and the difference between cache-buffered and direct I/O will get you most of the way to using it confidently. Treat every invocation that touches a real device as a one-shot, no-undo operation, and double-check before you press enter.

References

  • GNU Coreutils Manual: dd — https://www.gnu.org/software/coreutils/manual/html_node/dd-invocation.html
  • Linux man-pages project: man 1 dd — https://man7.org/linux/man-pages/man1/dd.1.html
  • GNU ddrescue Manual — https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html
  • Ubuntu Manpage Repository — https://manpages.ubuntu.com/manpages/noble/en/man1/dd.1.html
  • Arch Linux Wiki: Disk cloning — https://wiki.archlinux.org/title/Disk_cloning
Total
0
Shares

Leave a Reply

Previous Post
cat command in Linux and it perimeters

cat Command in Linux: Complete Guide to File Concatenation, Display, and Parameters

Next Post
diff command in Linux and it perimeters

diff Command in Linux: Complete Guide to File Comparison, Line Differences, and Parameters

Related Posts