How to preserve symlinks when copying with rsync

Directory trees often use symbolic links to select an active release, share one file from several locations, or keep configuration paths stable. A migration changes that layout when a link becomes an ordinary file or directory at the destination.

Archive mode includes --links, so rsync recreates each source symlink with the same target text instead of copying the referent. Adding --copy-links or --copy-dirlinks changes that behavior and can pull content from outside the intended transfer tree.

Relative targets are interpreted from the copied link's new directory and usually continue to resolve when the surrounding tree moves with them. Absolute targets keep their original path text, which can send applications back to the old host layout even though the destination entry is still a symlink.

  1. Inspect the source symlinks and record their target text.
    $ ls -l /srv/fixture/source/data/latest-report.txt /srv/fixture/source/data/missing-report.txt
    lrwxrwxrwx+ 1 root root 23 Jul 11 07:09 /srv/fixture/source/data/latest-report.txt -> ../documents/report.txt
    lrwxrwxrwx  1 root root 31 Jul 11 07:10 /srv/fixture/source/data/missing-report.txt -> ../documents/missing-report.txt

    missing-report.txt is an optional broken-link control for a staged migration. Omit it when the source tree has no known broken symlink.

  2. Copy the tree into a new or empty destination with archive mode and itemized changes.
    $ rsync --archive --itemize-changes /srv/fixture/source/ /srv/fixture/destination/
    .d....og... ./
    cd+++++++++ data/
    >f+++++++++ data/archive.sparse
    >f+++++++++ data/export.sql
    cL+++++++++ data/latest-report.txt -> ../documents/report.txt
    cL+++++++++ data/missing-report.txt -> ../documents/missing-report.txt
    >f+++++++++ data/session.tmp
    cd+++++++++ documents/
    >f+++++++++ documents/notes.txt
    >f+++++++++ documents/report-hardlink.txt
    >f+++++++++ documents/report.txt

    Each cL row shows that rsync created a symlink and retained the target text shown after ->. No delete option is present, but matching destination entries can still be replaced; keep the old tree available until the copied layout passes verification.

  3. Confirm that the destination entries are symlinks.
    $ ls -l /srv/fixture/destination/data/latest-report.txt /srv/fixture/destination/data/missing-report.txt
    lrwxrwxrwx 1 root root 23 Jul 11 07:09 /srv/fixture/destination/data/latest-report.txt -> ../documents/report.txt
    lrwxrwxrwx 1 root root 31 Jul 11 07:10 /srv/fixture/destination/data/missing-report.txt -> ../documents/missing-report.txt
  4. Read the destination link's stored target text.
    $ readlink /srv/fixture/destination/data/latest-report.txt
    ../documents/report.txt

    The output must match the target text recorded for the source link.

  5. Read the copied file through the valid destination link.
    $ cat /srv/fixture/destination/data/latest-report.txt
    Quarterly operations report
  6. Find destination symlinks whose targets do not resolve.
    $ find /srv/fixture/destination -xtype l -print
    /srv/fixture/destination/data/missing-report.txt

    No output means every copied link resolves. When a deliberate broken-link control is present, only that known path should be listed; investigate any additional result before switching applications to the destination tree.