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.
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:
| Property | What It Controls | Example |
|---|---|---|
| Owner | Which user owns the file | www-data, root, deploy |
| Group | Which group the file belongs to | www-data, staff |
| Permissions | Who can read, write, or execute | 644, 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 Type | Permission | Numeric | Reasoning |
|---|---|---|---|
| Directories | rwxr-xr-x | 755 | Web server needs to read and traverse |
| Regular files | rw-r--r-- | 644 | Web server reads; only owner writes |
| Config files with secrets | rw- | 600 | Only owner can read/write |
| Executable scripts | rwxr-xr-x | 755 | Must be executable |
| Upload directories | rwxrwxr-x | 775 | Web server process needs to write |
Rsync Permission Flags
What Archive Mode (-a) Preserves
The -a flag preserves ownership and permissions by default:
| Component Flag | What It Preserves | Included in -a? |
|---|---|---|
-p | Permissions (chmod bits) | Yes |
-o | Owner (requires root) | Yes |
-g | Group | Yes |
-t | Modification timestamps | Yes |
# Archive mode — preserves all metadata
rsync -av /var/www/html/ /backup/www/
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:
| Prefix | Applies To | Example |
|---|---|---|
D | Directories only | D755 = rwxr-xr-x |
F | Files only | F644 = rw-r--r-- |
| (none) | Both files and directories | 755 |
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
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 Server | Default User | Default Group | Config Check |
|---|---|---|---|
| Nginx | www-data | www-data | grep -i user /etc/nginx/nginx.conf |
| Apache | www-data | www-data | grep -i user /etc/apache2/apache2.conf |
| OpenLiteSpeed | nobody | nogroup | Check LiteSpeed admin panel |
| PHP-FPM | www-data | www-data | grep -i user /etc/php/*/fpm/pool.d/www.conf |
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
| Pitfall | Consequence | Prevention |
|---|---|---|
Using chmod 777 | Any user (including attackers) can write | Use 755 for dirs, 644 for files |
Syncing without -a | Permissions and ownership lost | Always use archive mode |
Forgetting --chown during migration | Wrong user owns files → web server can't read | Set ownership during sync |
| Root-owned config files in web root | Application can't read its own config | Match ownership to web server user |
Upload directories with 644 | Users can't upload files | Use 775 for writable directories |
| Not verifying after sync | Broken site only discovered after deployment | Check 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
- Secure Transfer — SSH encryption and key management
- Compression and Bandwidth — Optimize transfer performance
- Options and Flags — Complete flag reference