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
| Parameter | Description |
|---|---|
if=FILE | Input file or device to read from |
of=FILE | Output file or device to write to |
bs=BYTES | Block size for both reading and writing (e.g. bs=1M) |
ibs=BYTES | Input block size only |
obs=BYTES | Output block size only |
count=N | Copy only N input blocks, then stop |
skip=N | Skip N input blocks before starting to copy |
seek=N | Skip N output blocks before starting to write |
conv=CONVS | Comma-separated list of conversions (e.g. ucase, lcase, notrunc, sync, noerror) |
status=LEVEL | Control progress reporting: none, noxfer, progress |
iflag=FLAGS | Input flags, e.g. direct, sync, nonblock |
oflag=FLAGS | Output 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 casenotrunc— 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 ofibswith zero bytes if it’s shortnoerror— 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
| Command | Purpose |
|---|---|
dd | Low-level, byte-for-byte copy and conversion; works on files and raw devices |
cp | Filesystem-aware file copy; won’t sensibly copy whole block devices |
cat | Can concatenate/copy streams but lacks block-size control, seek/skip, and conversion options |
dcfldd | A 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 |
rsync | Filesystem-level sync, incremental, much better for regular backups of live systems |
pv | Often 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=1Mtobs=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=progressto monitor throughput and catch a stalled or abnormally slow copy early. - For pure benchmarking, use
oflag=directto 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 viablockdev --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=sparseto 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=andof=values, especially device names, immediately before running the command. - Prefer
/dev/disk/by-id/...paths over/dev/sdXwhen scripting against specific physical drives, since/dev/sdXnaming can shift between boots. - Run destructive
ddoperations with the minimum privilege necessary — typicallysudofor a single command rather than a root shell, to reduce the blast radius of any other mistake in the same session. - Be aware that
ddused 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
ddcontain the full raw content of the source, including any deleted-but-not-overwritten data and any sensitive material — handle and store.imgfiles 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, orblkidimmediately before running a destructiveddcommand. - Use
status=progressfor anything non-trivial in size so you have visibility into whether the operation is proceeding normally. - Follow disk-write operations with
syncto guarantee data has actually reached the physical device before removing media. - Prefer purpose-built tools (
ddrescuefor damaged media,rsyncfor regular backups, dedicated ISO-writing tools likebalenaEtcherfor less error-prone GUI workflows) whendd‘s raw power isn’t strictly necessary. - Never run
ddagainst 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