Skip to main content

When working with Drupal environments, there are times you need to duplicate a directory structure — for example, cloning a site or preparing a new environment without copying certain environment-specific folders.

A common case is excluding:

sites/default/files

This directory typically contains:

  • User uploads
  • Generated image styles
  • Cached assets
  • Environment-specific permissions

Copying it between environments can cause permission issues, unnecessary bloat, or unintended data leakage.

This article walks through the **correct and safe way** to copy a directory **while excluding a specific child directory**.

The problem with `cp -R`

A naive approach might look like this:

sudo cp -R ../frdc/sites frdc-12-01-2026/

While this works, it has a critical limitation:

> `cp` does not support exclusions.

There is no reliable way to tell `cp`:

> Copy everything *except* `sites/default/files`

Any workaround (using `find`, temporary deletes, or tar pipelines) introduces unnecessary risk — especially on a live Drupal codebase.

The correct tool: `rsync`

`rsync` is purpose-built for controlled file transfers and directory synchronisation.
It supports exclusions, preserves permissions, and is installed by default on most Linux and macOS systems.

Recommended command

rsync -av \
 --exclude='default/files' \
 ../frdc/sites/ \
 frdc-12-01-2026/sites/

What this command does

Let’s break it down:

  • rsync - robust file copy and sync tool
  • -a - archive mode (preserves permissions, ownership, symlinks, timestamps)
  • -v - verbose output (see what’s happening)
  • --exclude='default/files' - excludes only that directory
  • ../frdc/sites/ - source directory
  • frdc-12-01-2026/sites/ - destination directory

The trailing slash matters

Including `/sites/` (not `/sites`) means:

  • Copy the *contents* of `sites`
  • Not nest `sites/sites` accidentally

Resulting directory structure

After running the command, the destination will look like this:

sites/
└── default/
   ├── settings.php
   ├── services.yml
   ├── default.services.yml
   ├── settings.local.php
   └── files/        ❌ not copied

Everything under `sites/default` is copied except `files`.

Always do a dry run first

Before executing the real copy, it’s good practice to validate what will happen:

rsync -av --exclude='default/files' --dry-run \
 ../frdc/sites/ \
 frdc-12-01-2026/sites/

This shows exactly:

  • What files will be copied
  • What will be skipped
  • Without touching the filesystem

Why this approach is best practice for Drupal

Excluding `sites/default/files` is intentional and recommended because:

  • Files are environment-specific
  • Permissions often differ across DEV / UAT / PROD
  • Files can be synced separately if required
  • Drupal can regenerate styles and caches
  • Avoids copying large, unnecessary data

In most deployment workflows:

  • Code moves forward
  • Files are either shared, mounted, or synced independently

When to extend this approach

You can easily expand the exclusions if needed:

--exclude='default/files' \
--exclude='private' \
--exclude='translations'

Or externalise them into an exclude file for reuse in pipelines.

 

If you need to copy a directory but exclude a specific child directory, the rule is simple:

> Don’t use `cp`. Use `rsync`.

It’s safer, clearer, and designed for exactly this scenario - especially in Drupal and multi-environment deployments.

Related articles

Andrew Fletcher19 Jan 2026
Automatically splitting large files with sed based on file size
When working with large prompt or configuration files (for example data/prompts-master.py), it’s common to copy or inspect the file in chunks using sed. Early on, this is easy to manage manually:sed -n '1,324p' data/prompts-master.py sed -n '325,647p' data/prompts-master.pyHowever, this approach...
Andrew Fletcher25 Oct 2023
vi(m) command mode
In Vim, following is an outline of the vi(m) functions you can utilise whilst in command mode - yes they are case sensitiveactionoutcomeachange to insert mode (after cursor)Achange to insert mode (at end of line)dddelete one lineGgo to end of the file1Ggo to top of the fileichange to insert...
Andrew Fletcher25 Feb 2021
regex expressions a working sheet
Regular expressions (regex) are extremely useful in extracting information from any text by searching for one or more matches of a specific search pattern. The basic anchors - ^ and $ expression action ^The matches any string that starts with...