Chapter 7: Rsync Deployment
Rsync copies files from your workstation to the server. It's fast, reliable, and you control exactly what gets deployed. This chapter covers the essentials: how rsync works, what to exclude, automating deploys with a script, and handling post-deployment tasks.
7.1 How Rsync Works
Rsync compares source and destination, then copies only what changed. First deploy copies everything. Subsequent deploys are fast because most files haven't changed.
Basic syntax:
rsync [options] source/ destination/
The trailing slash matters:
rsync source/ dest/copies contents of source into destrsync source dest/copies the source directory itself into dest
Always use source/ with the trailing slash for deploying.
7.2 Essential Rsync Options
rsync -avz --delete ./ deploy@server:/var/www/myapp/
-a (archive): Preserves permissions, timestamps, symlinks. What you want for deployments.
-v (verbose): Shows what's being transferred. Helpful for debugging.
-z (compress): Compresses data during transfer. Faster over slow connections.
--delete: Removes files on server that don't exist locally. Keeps the deployment clean. Without this, deleted files linger forever.
Other Useful Options
--dry-run: Shows what would happen without doing it. Test before deploying.
rsync -avz --delete --dry-run ./ server:/var/www/myapp/
-P (progress): Shows transfer progress. Nice for large uploads.
-e: Specify SSH options (non-standard port, specific key).
rsync -avz -e "ssh -p 2222" ./ server:/var/www/myapp/
7.3 Exclude Patterns
You don't want to upload everything. Development files, IDE configs, and local artifacts should stay on your workstation.
Using --exclude
rsync -avz --delete \
--exclude='.git' \
--exclude='.idea' \
--exclude='node_modules' \
--exclude='.DS_Store' \
./ server:/var/www/myapp/
Using an Exclude File
For complex patterns, put them in a file. Create rsync-excludes.txt:
# Version control
.git
.gitignore
.gitattributes
# IDE and editor
.idea
.vscode
*.swp
*~
# OS files
.DS_Store
Thumbs.db
# Development
node_modules
tests
phpunit.xml
phpunit.xml.dist
.phpunit.result.cache
# Documentation (unless serving it)
README.md
CHANGELOG.md
docs
# Server-side files (don't overwrite these)
config/config.php
storage/logs/*
storage/cache/*
storage/uploads/*
public/uploads/*
Use it with:
rsync -avz --delete --exclude-from='rsync-excludes.txt' ./ server:/var/www/myapp/
7.4 The Deploy Script
Wrap rsync and post-deploy commands in a script. Create deploy.sh in your project root:
#!/bin/bash
set -e
# Configuration
SERVER="myserver" # SSH host from ~/.ssh/config
REMOTE_PATH="/var/www/myapp"
EXCLUDE_FILE="rsync-excludes.txt"
PHP_SERVICE="php8.3-fpm"
echo "Deploying to $SERVER..."
# Sync files
rsync -avz --delete \
--exclude-from="$EXCLUDE_FILE" \
./ "$SERVER:$REMOTE_PATH/"
# Run post-deploy commands on server
ssh "$SERVER" bash -c "'
cd $REMOTE_PATH
# Install/update dependencies
composer install --no-dev --optimize-autoloader --no-interaction
# Fix permissions
sudo chown -R deploy:www-data .
chmod -R 775 storage/
# Run database migrations
php scripts/migrate.php
# Reload PHP-FPM to clear opcache
sudo systemctl reload $PHP_SERVICE
echo \"Deploy complete on server\"
'"
echo "Done!"
Make it executable:
chmod +x deploy.sh
Deploy with:
./deploy.sh
7.5 Why Reload PHP-FPM?
PHP caches compiled scripts in memory (opcache) for performance. After rsync updates files on disk, PHP-FPM workers still have old bytecode cached.
In production, opcache.revalidate_freq is typically 0 - PHP never checks if files changed. Without a reload, PHP serves old code indefinitely.
systemctl reload gracefully restarts workers. Existing requests finish, new workers start with empty opcache. No connections dropped.
7.6 Post-Deploy Steps Explained
The deploy script runs several commands on the server after syncing files:
Composer Install
composer install --no-dev --optimize-autoloader --no-interaction
--no-dev: Skip development dependencies (PHPUnit, etc.)--optimize-autoloader: Build faster classmap--no-interaction: Don't prompt (automated deploy)
Run this after every deploy in case composer.lock changed.
Permissions
sudo chown -R deploy:www-data .
chmod -R 775 storage/
Ensures web server can read files and write to storage directories.
7.7 Deployment Checklist
First-Time Setup
- SSH key authentication working
- Server directory structure ready (/var/www/myapp)
- Config file created on server with real credentials
- rsync-excludes.txt created
- deploy.sh script ready and executable
- Tested a dry-run deploy
Before Each Deploy
- Code tested locally
- If building assets: npm run build (or equivalent)
- Dry-run to check what will change
Deploy
- Run ./deploy.sh
- Verify site works
- Check error logs if anything seems off
This is just one of 31 chapters. The complete guide covers everything from initial server setup to monitoring, backups, and disaster recovery.
Get the Full Guide - $59