How to Sync Only Directory Contents Without Parent Folder in Ansible


4 views

When using Ansible's synchronize module, you'll encounter this common scenario: you want to copy the contents of a directory to a target location without preserving the parent directory structure. By default, running:

- name: Basic directory sync
  synchronize:
    src: files/to/synchronize
    dest: /tmp/1

Creates /tmp/1/synchronize/ containing your files, when what you really want is all files directly in /tmp/1/.

The key is leveraging rsync's --relative and --no-implied-dirs flags through Ansible's rsync_opts parameter:

- name: Sync directory contents only
  synchronize:
    src: files/to/synchronize/
    dest: /tmp/1/
    rsync_opts:
      - "--relative"
      - "--no-implied-dirs"
    archive: yes

Important notes about this solution:

  • The trailing slashes on both src and dest are crucial
  • archive: yes preserves permissions and timestamps
  • Works with recursive directory structures

For smaller directory trees, you might consider Ansible's copy module with content: yes:

- name: Copy directory contents
  copy:
    src: "files/to/synchronize/"
    dest: "/tmp/1/"
    remote_src: yes
    content: yes

Limitations to be aware of:

  • Doesn't support delta transfers like rsync
  • Less efficient for large directories
  • No native checksum verification

For complex scenarios where you need to exclude certain patterns:

- name: Sync with exclusions
  synchronize:
    src: "files/to/synchronize/"
    dest: "/tmp/1/"
    rsync_opts:
      - "--relative"
      - "--no-implied-dirs"
      - "--exclude=*.tmp"
      - "--exclude=.git/"
    checksum: yes
    delete: yes

This configuration will:

  • Skip temporary files and Git directories
  • Use checksums for change detection
  • Remove files in destination not present in source

When using Ansible's synchronize module, you'll notice it preserves the entire source directory structure by default. For example, syncing files/to/synchronize to /tmp/1 creates /tmp/1/synchronize - an extra directory level you may not want.

The key is to leverage rsync's --relative and --no-implied-dirs flags through Ansible's rsync_flags parameter:

- name: Sync directory contents without parent folder
  synchronize:
    src: files/to/synchronize/
    dest: /tmp/1/
    rsync_flags:
      - "--relative"
      - "--no-implied-dirs"
      - "--copy-links"
    recursive: yes
    delete: yes

1. Trailing slashes matter: Both src and dest should end with /
2. --relative preserves path relativity without the parent dir
3. --no-implied-dirs prevents automatic directory creation
4. Add delete: yes for true mirror synchronization

For smaller directory trees, you might prefer the copy module with remote_src:

- name: Copy directory contents
  copy:
    src: "files/to/synchronize/"
    dest: "/tmp/1/"
    remote_src: no

The synchronize method is significantly faster for large directories as it uses rsync's delta-transfer algorithm. Benchmark tests show 3-5x speed improvement over copy for directories exceeding 1GB.

If you encounter permission problems, add these rsync flags:

rsync_flags:
  - "--chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r"
  - "--times"
  - "--omit-dir-times"