Guides

How to copy files and folders in Linux: the cp command, cp -r and cp -a

How to copy a file in Linux with cp, copy a folder with cp -r or cp -a, copy files to another directory, keep permissions, and fix common cp errors.

Published ~9 min read
On this page

Short answer: to copy a file in Linux, run cp source destination: cp report.pdf /srv/backup/ copies it into a directory, cp app.conf app.conf.bak makes a copy under a new name. To copy a folder, add -r (cp -r site /srv/backup/), or -a to also keep owners, permissions, timestamps and symlinks.

any Linux
cp app.conf app.conf.bak          # copy a file under a new name
cp report.pdf /srv/backup/        # copy a file to another directory
cp -r site /srv/backup/           # copy a folder and everything in it
cp -a site /srv/backup/           # the same, keeping owner, mode, times and links
cp -a site/. /srv/www/site/       # copy the contents of site into an existing folder

The outputs on this page were produced on Debian 12 with GNU coreutils 9.1, bash 5.2.15, rsync 3.2.7, pv 1.6.20 and GNU tar 1.34, as root in a scratch directory on 21 October 2026, and the messages are the ones that cp printed. The Permission denied case and the root alias on Rocky Linux and AlmaLinux are written from the coreutils documentation and those distributions’ defaults and were not run here. Ubuntu, Rocky Linux and AlmaLinux ship the same GNU cp, in other versions.

cp cheat sheet

TaskCommand
Copy a file under a new namecp a.conf b.conf
Copy a file to another directorycp a.conf /etc/myapp/
Copy and rename in one stepcp a.conf /etc/myapp/main.conf
Copy several files into a directorycp a.txt b.txt docs/
Target directory first (for find and xargs)cp -t docs/ a.txt b.txt
Copy a directorycp -r site backup/
Copy a directory, keep everythingcp -a site backup/
Copy only the contents, hidden files includedcp -a site/. backup/
Ask before overwritingcp -i a.conf b.conf
Never overwritecp -n a.conf b.conf
Only if the source is newercp -u a.conf b.conf
Keep a backup of what gets overwrittencp -b a.conf b.conf
Show each copycp -v a.conf b.conf
Recreate the path below a directorycp --parents etc/nginx/nginx.conf backup/
Follow symlinks and copy what they point tocp -rL current backup/

How do I copy a file to another directory?

Name the file, then the directory. A trailing slash on the directory is optional but worth the habit: if the directory does not exist, cp fails instead of quietly creating a file with that name. -v prints each copy:

Example output
$ cp -v report.pdf backup/
'report.pdf' -> 'backup/report.pdf'
$ cp -v a.txt b.txt backup/
'a.txt' -> 'backup/a.txt'
'b.txt' -> 'backup/b.txt'
$ cp -v -t backup/ a.txt report.pdf
'a.txt' -> 'backup/a.txt'
'report.pdf' -> 'backup/report.pdf'

With more than one source, the last name must be an existing directory. -t DIR puts the directory first, which is what you need behind find ... -exec cp -t DIR {} + or xargs, where the file names come last. To copy and rename at the same time, give the full new path: cp nginx.conf /etc/nginx/nginx.conf. An existing file at that path is overwritten without a question unless you add -i or -n.

How do I copy a folder in Linux?

Plain cp refuses a directory and skips it:

Example output
$ cp site site2
cp: -r not specified; omitting directory 'site'

Add -r (or -R, the same thing) to copy it recursively, or -a when the copy should keep owners and timestamps (next section). Where the copy ends up depends on whether the destination already exists, and running the same command twice gives two different results:

CommandDestination beforeResult
cp -r site site2site2 does not existsite2 is the copy: site2/index.html
cp -r site site2site2 exists (second run)Copied inside it: site2/site/index.html
cp -r site/ site3site3 does not existSame as without the slash: cp ignores a trailing slash on the source
cp -r site/. site4/site4 existsThe contents, .env included, land in site4
cp -r site/* site5/site5 existsThe contents without hidden files: .env was missing
cp -rT site site6site6 existsThe contents of site go into site6, never site6/site

The site/. form is the one to remember. . is the directory itself, so cp copies everything in it, hidden files included, into the target and merges with what is already there. Same-named files in the target are overwritten silently: in the test, cp -a m_src/. m_dst/ replaced index.html and left the extra keep.txt alone. site/* looks similar, but the shell expands the * before cp runs and skips names that start with a dot.

cp -a vs cp -r: owners, timestamps and links

-r copies the data and the tree. Everything else is new: the copy belongs to whoever runs cp, and every file gets the current time. -a (archive) is -dR --preserve=all in the man page, and the difference shows up as soon as you list the copies. Source: app.conf owned by www-data, mode 640, dated 1 January 2025, with a hard link hard.conf and a symlink current.conf:

Attributecp -rcp -rpcp -a
Owner www-dataNo, became rootYesYes
Mode 640Yes (see umask below)YesYes
Date 1 January 2025No, todayYesYes
Hard link between two namesNo, two separate filesNoYes
Symlink inside the treeCopied as a symlinkCopied as a symlinkCopied as a symlink
Extended attributes, SELinux contextNoNoYes, where the filesystem supports them

Mode is the subtle one. Without -p or -a, a new file gets the source’s mode minus the umask: with the usual umask 0022, a file with mode 777 came out as 755, while cp -p kept 777. Preserving the owner needs root; as a normal user, -a still copies, but the files belong to you. Use -a for backups, website moves and anything a service reads with a specific user; use -r when you want a fresh copy that belongs to you. To pick single attributes, use --preserve=mode,timestamps or turn one off with --no-preserve=ownership.

Overwriting: -i, -n, -u, -v and -f

Example output
$ cp -i new.conf old.conf
cp: overwrite 'old.conf'? n
$ cp -n new.conf old.conf; echo $?
0
$ cp -uv new.conf old.conf          # new.conf is the older one: nothing happens
$ touch new.conf; cp -uv new.conf old.conf
'new.conf' -> 'old.conf'
  • -i asks before overwriting; anything but y skips the file.
  • -n never overwrites. On coreutils 9.1 it skips silently, even with -v, and exits with 0. Per the GNU coreutils release notes, newer versions changed what -n reports and added --update=none, which 9.1 rejects with option '--update' doesn't allow an argument. In scripts that run on several systems, test for the target with [ -e target ] instead.
  • -u copies only when the source is newer than the target or the target is missing, a cheap one-way sync for a few files.
  • -f removes a target that cannot be opened for writing and tries again. It does not switch off -i: cp -i -f and cp -f -i both asked in the test. That differs from mv, where the last option wins.

That last point matters on Rocky Linux and AlmaLinux, which set alias cp='cp -i' in root’s ~/.bashrc (Debian ships the same line commented out in /usr/share/base-files/dot.bashrc). Typing cp -f there still prompts. Skip the alias with \cp or command cp: in an interactive bash with that alias, \cp z1 z2 overwrote without asking. Scripts never see the alias.

Keep the old file: --backup

Example output
$ cp -bv new.conf old.conf
'new.conf' -> 'old.conf' (backup: 'old.conf~')
$ cp -v --backup=numbered new.conf old.conf
'new.conf' -> 'old.conf' (backup: 'old.conf.~1~')
$ cp -bv -S .bak y.conf x.conf
'y.conf' -> 'x.conf' (backup: 'x.conf.bak')

-b renames the file being overwritten instead of losing it. One surprise: the default method, existing, makes numbered backups as soon as one numbered backup exists. After two numbered runs had left old.conf.~1~ and old.conf.~2~, -b -S .bak wrote old.conf.~3~ and ignored the suffix. Set the method explicitly with --backup=simple or --backup=numbered when it matters.

CommandWhat was copied
cp link.conf c1The file the link points to, as a regular file
cp -P link.conf c2The link itself
cp -r current d1The link itself (-r does not follow links)
cp -a current d3The link itself
cp -rL current d2The directory it points to, with its files
cp -rH current d4The directory, because current was named on the command line
cp -r current/ d5The directory: with the trailing slash, the path resolves through the link

current was a link to releases/v2, the layout many deploy scripts use. Without -L or the trailing slash, a backup made with cp -r current holds a link and no files, and the link points at a relative path that may not exist where the copy lands. A link whose target is missing makes plain cp fail with cp: cannot stat 'link.conf': No such file or directory, although ls shows the name. The ln guide covers how links resolve.

Copy into folders that do not exist yet

cp does not create missing directories. cp a nodir3/x stopped with cannot create regular file 'nodir3/x': No such file or directory. Create them first with mkdir -p, or use one of two shortcuts:

Example output
$ cp -v --parents etc/nginx/nginx.conf etc/nginx/sites-available/default cfgbak/
etc -> cfgbak/etc
etc/nginx -> cfgbak/etc/nginx
'etc/nginx/nginx.conf' -> 'cfgbak/etc/nginx/nginx.conf'
etc/nginx/sites-available -> cfgbak/etc/nginx/sites-available
'etc/nginx/sites-available/default' -> 'cfgbak/etc/nginx/sites-available/default'
$ install -D -m 644 a.txt deep/new/dir/a.txt

--parents recreates the source path below the target directory, a tidy way to save a handful of configs with their paths. install -D creates every missing directory of the destination and sets the mode in the same step.

Sparse files and disk images

A VM disk image or a database file can be mostly holes: large on paper, small on disk. cp detects that by default (--sparse=auto). A 20 MiB test file with three bytes of data used 4 KiB on disk, and so did its copy; --sparse=never wrote all 20 MiB. cmp found the contents identical in both cases. du shows the real usage, du --apparent-size the nominal size.

Does cp show progress?

No. GNU cp has no progress option, and -v only prints the name of each file, not how far the copy has got. For a long copy, use a tool that measures:

any Linux
rsync -ah --progress disk.img /mnt/backup/          # per file
rsync -ah --info=progress2 /srv/data/ /mnt/data/    # one line for the whole job
pv disk.img > /mnt/backup/disk.img                   # one file through pv
tar -C /srv/data -cf - . | pv | tar -C /mnt/data -xf -   # a directory through pv

On the 20 MiB test file, rsync ended with 20.97M 100% ... (xfr#1, to-chk=0/1) and pv drew a bar ending in 100%. Both are separate packages (apt install rsync pv on Debian and Ubuntu). rsync has the bonus that a second run only copies what changed, so an interrupted copy continues instead of starting over.

cp errors and what they mean

MessageMeaningFix
-r not specified; omitting directoryA source is a directorycp -r
are the same fileSource and target are one file: the same path, ./path, or two hard linksCheck the paths; nothing was copied
cannot statThe source does not exist, or is a symlink whose target is missingCheck with ls -l
target 'x': No such file or directorySeveral sources, and the last name is missingmkdir -p it first
target 'x': Not a directorySeveral sources, and the last name is a fileGive a directory as the last name
cannot create regular file ...: Not a directoryTarget written as dir/, but there is no such directorymkdir -p, or drop the slash to copy under that name
cannot copy a directory, ..., into itselfYou copied a directory into its own subdirectory, cp -r src src/subCopy to a path outside the source
cannot overwrite non-directory ... with directoryThe target name exists as a fileRename or remove the file first
Permission deniedNo read permission on the source, or no write permission on the target directorySee below
No space left on deviceThe target filesystem is fullNo space left on device

The “into itself” case leaves something behind: cp reported the error and exited with 1, yet src/sub existed afterwards with one level of copied files in it. Remove it before you try again, or it ends up inside the next copy.

cp: cannot create regular file: Permission denied

cp needs read permission on every source file, plus write and execute permission on the directory it writes into. Copying into /etc, /var/www or another user’s home as a normal user fails for the second reason. Either copy with sudo cp, or fix the directory with the chown and chmod guides, so that the service user owns what it needs and nothing more. Files copied with sudo cp belong to root unless you add -a or -p, which is a common reason a web app cannot read its own config after a restore.

cp vs rsync vs scp

cprsyncscp
Built forCopies on the same machineSyncing trees, locally or over SSHOne-off copies over SSH
To another serverNoYesYes
Second runCopies everything againCopies only what changedCopies everything again
Interrupted copyStart overRerun continues (-P keeps partial files)Start over
ProgressNone--progress, --info=progress2Progress meter by default
Keep owner, mode, times-a-a-p (times and modes only)
Mirror with deletesNo--deleteNo
Installed by defaultAlways (coreutils)Often not on minimal imagesWith the OpenSSH client

For a few files on one machine, cp is the simplest tool. For large or repeated copies use rsync, between servers rsync or scp, and when the original should disappear afterwards, mv, which renames instantly on the same filesystem instead of copying.

Copying on a server: a few habits

  • Back up a config before you edit it: sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak. -a keeps owner and mode, so the backup can be put back with cp -a as it was.
  • Do not leave backups where they are loaded. A service that includes a whole folder, such as an nginx include sites-enabled/*;, reads a default.bak in that folder as one more config. Keep copies in a separate folder.
  • Copy a website with -a, not -r, so files keep the owner PHP or nginx expects.
  • A copy is not a backup of the server. On a VMHeaven VPS, a panel snapshot before a larger change covers the whole disk, and can be taken by hand or on a daily, weekly or monthly schedule.

Frequently asked

How do I copy a file to another directory in Linux?

Run cp file /path/to/dir/, for example cp report.pdf /srv/backup/. Several files go in one command: cp a.txt b.txt /srv/backup/. To give the copy a new name at the same time, write the full new path: cp app.conf /etc/myapp/main.conf.

How do I copy a folder in Linux?

Run cp -r folder destination/, or cp -a to also keep owners, permissions, timestamps and symlinks. If the destination folder already exists, cp puts the folder inside it; to copy only its contents, hidden files included, use cp -a folder/. destination/.

What is the difference between cp -r and cp -a?

cp -r copies the files and the directory tree, but the copies belong to the user running cp and get the current time. cp -a (archive) also keeps owner, group, permissions, timestamps, hard links and symlinks, which is what backups and website moves need. Keeping another user as owner requires root.

Why does cp say omitting directory?

Because one of the sources is a directory and you did not pass -r. GNU cp prints cp: -r not specified; omitting directory 'name' and copies only the files. Add -r, or -a to keep owners and timestamps as well.

Related articles

All guides