Skip to main content

Ownership and Permissions in Rsync

File permissions directly affect whether your web server can serve files, whether your application can write logs, and whether an attacker can exploit loose access controls. Rsync can preserve, override, or reset ownership and permissions during transfers.

Why This Matters

After a migration or restore, incorrect permissions are the #1 cause of broken web applications. Understanding how rsync handles permissions prevents the "white screen of death" and permission-denied errors.

Linux Permission Fundamentals

Every file on Linux has three properties that rsync can control:

PropertyWhat It ControlsExample
OwnerWhich user owns the filewww-data, root, deploy
GroupWhich group the file belongs towww-data, staff
PermissionsWho can read, write, or execute644, 755

Permission Numbers Explained

Permission: rwxr-xr-x = 755
│││││││││
│││││││││
Owner: rwx = 4+2+1 = 7 (read, write, execute)
Group: r-x = 4+0+1 = 5 (read, execute)
Others: r-x = 4+0+1 = 5 (read, execute)

Standard Web Server Permissions

Content TypePermissionNumericReasoning
Directoriesrwxr-xr-x755Web server needs to read and traverse
Regular filesrw-r--r--644Web server reads; only owner writes
Config files with secretsrw-600Only owner can read/write
Executable scriptsrwxr-xr-x755Must be executable
Upload directoriesrwxrwxr-x775Web server process needs to write

Rsync Permission Flags

What Archive Mode (-a) Preserves

The -a flag preserves ownership and permissions by default:

Component FlagWhat It PreservesIncluded in -a?
-pPermissions (chmod bits)Yes
-oOwner (requires root)Yes
-gGroupYes
-tModification timestampsYes
# Archive mode — preserves all metadata
rsync -av /var/www/html/ /backup/www/
Owner Preservation Requires Root

The -o flag (preserve owner) only works when rsync runs as root. Without root, rsync will transfer files but set the owner to the current user — which is usually what you want for backups to a different user account.

Setting Ownership During Sync

--chown=USER:GROUP

Change file ownership as they're transferred:

# Set web server ownership during deployment
rsync -av --chown=www-data:www-data \
/deploy/app/ /var/www/html/

# Deploy as root but set correct application user
sudo rsync -av --chown=www-data:www-data \
/staging/app/ /var/www/html/

After Sync (Alternative)

If --chown isn't available (older rsync versions), fix ownership afterward:

sudo chown -R www-data:www-data /var/www/html/

Setting Permissions During Sync

--chmod=MODE

Apply permissions as files are transferred:

# Set directories to 755, files to 644
rsync -av --chmod=D755,F644 /deploy/app/ /var/www/html/

# Set everything to 755
rsync -av --chmod=755 /deploy/scripts/ /usr/local/bin/

The D and F prefixes specify different permissions for directories and files:

PrefixApplies ToExample
DDirectories onlyD755 = rwxr-xr-x
FFiles onlyF644 = rw-r--r--
(none)Both files and directories755

Combined: Ownership + Permissions

The most common pattern for web application deployment:

# Deploy with correct ownership AND permissions in one step
sudo rsync -av \
--chown=www-data:www-data \
--chmod=D755,F644 \
/deploy/app/ /var/www/html/

Common Scenarios

Web Application Deployment

# Deploy PHP/Laravel/WordPress app
sudo rsync -avz \
--chown=www-data:www-data \
--chmod=D755,F644 \
--exclude='.env' \
/deploy/release/ /var/www/html/

# Make specific files more restrictive
chmod 600 /var/www/html/.env
chmod 600 /var/www/html/wp-config.php

Migration Between Servers

When servers have different user/group setups:

# Source: files owned by ubuntu:ubuntu
# Destination: needs www-data:www-data

rsync -avz \
--chown=www-data:www-data \
user@source:/var/www/html/ /var/www/html/

Backup and Restore

# Backup — preserve all metadata (requires root)
sudo rsync -av /var/www/html/ /backup/www/

# Restore — set correct ownership for destination server
sudo rsync -av \
--chown=www-data:www-data \
/backup/www/ /var/www/html/

Config File Sync with Restricted Permissions

# Sync Nginx configs with restrictive permissions
sudo rsync -av --chmod=F644,D755 \
/staging/nginx/ /etc/nginx/

# Database config (should be readable only by owner)
sudo rsync -av --chmod=600 \
/staging/.my.cnf /root/.my.cnf

Checking Permissions Before and After

Before Sync

# List permissions in detail
ls -la /var/www/html/

# Check specific file
stat /var/www/html/index.php

# Find files with wrong permissions
find /var/www/html/ -type f ! -perm 644 -ls
find /var/www/html/ -type d ! -perm 755 -ls

# Find files owned by wrong user
find /var/www/html/ ! -user www-data -ls

After Sync — Verification

# Verify ownership
ls -la /var/www/html/ | head -20

# Count files with incorrect permissions
echo "Files not 644:" $(find /var/www/html/ -type f ! -perm 644 | wc -l)
echo "Dirs not 755:" $(find /var/www/html/ -type d ! -perm 755 | wc -l)

# Verify owner
echo "Not www-data:" $(find /var/www/html/ ! -user www-data | wc -l)

Quick Fix: Reset All Permissions

When permissions are a mess and you need to start fresh:

# Reset ownership
sudo chown -R www-data:www-data /var/www/html/

# Reset directories to 755
sudo find /var/www/html/ -type d -exec chmod 755 {} \;

# Reset files to 644
sudo find /var/www/html/ -type f -exec chmod 644 {} \;

# Make specific files restrictive
sudo chmod 600 /var/www/html/.env
sudo chmod 600 /var/www/html/wp-config.php

# Make scripts executable
sudo chmod 755 /var/www/html/artisan # Laravel
sudo chmod 755 /var/www/html/manage.py # Django
Combine All Into One Rsync

Instead of fixing after sync, do it during sync:

sudo rsync -av \
--chown=www-data:www-data \
--chmod=D755,F644 \
/source/ /var/www/html/

This is cleaner, faster, and ensures every file gets the correct permissions — including new files that appear in future syncs.

Web Server Permission Matrix

Web ServerDefault UserDefault GroupConfig Check
Nginxwww-datawww-datagrep -i user /etc/nginx/nginx.conf
Apachewww-datawww-datagrep -i user /etc/apache2/apache2.conf
OpenLiteSpeednobodynogroupCheck LiteSpeed admin panel
PHP-FPMwww-datawww-datagrep -i user /etc/php/*/fpm/pool.d/www.conf
info

The web server user varies by distribution. On Debian/Ubuntu it's typically www-data. On CentOS/RHEL it's nginx or apache. Always verify with the config check commands above.

Common Pitfalls

PitfallConsequencePrevention
Using chmod 777Any user (including attackers) can writeUse 755 for dirs, 644 for files
Syncing without -aPermissions and ownership lostAlways use archive mode
Forgetting --chown during migrationWrong user owns files → web server can't readSet ownership during sync
Root-owned config files in web rootApplication can't read its own configMatch ownership to web server user
Upload directories with 644Users can't upload filesUse 775 for writable directories
Not verifying after syncBroken site only discovered after deploymentCheck with ls -la and find

Quick Reference

# Preserve all metadata
sudo rsync -av /source/ /destination/

# Set ownership during sync
sudo rsync -av --chown=www-data:www-data /source/ /var/www/html/

# Set permissions during sync (dirs=755, files=644)
rsync -av --chmod=D755,F644 /source/ /destination/

# Combined ownership + permissions
sudo rsync -av --chown=www-data:www-data --chmod=D755,F644 /source/ /var/www/html/

# Check permissions after sync
find /var/www/html/ -type f ! -perm 644 -ls
find /var/www/html/ ! -user www-data -ls

What's Next