git-deploy-php deploys a Git repository to a remote server over FTP, FTPS or SFTP, uploading only the files that changed between the currently deployed revision and the one you are deploying.
It was created to simplify deployments to shared hosting where shell access may not be available, and to save bandwidth by transferring only the diff. On top of the original FTP tool this fork adds SFTP (with private-key auth), maintenance mode, post-deploy HTTP hooks with live streaming, and Composer handling (upload changed vendor files, or run composer install on the server over SSH).
- PHP 8.1+
- phpseclib/phpseclib
~3.0(only needed for SFTP). Installed automatically when you require this package via Composer.
Via Composer:
composer require --dev ciki/git-deploy-php
or simply drop the git-deploy script into your project root. The script expects to live in the project root - it resolves .gitdeploy/ (config) and vendor/ (autoloader, for SFTP) relative to its own location.
- Create a
.gitdeploy/directory in your project root. - Put a
deploy.ini(or<env>.ini) file in it (see below). - Run
php git-deploy(orphp git-deploy --env=<env>).
The first time it runs against a server, git-deploy assumes the server is empty and uploads every tracked file. On later runs it reads the REVISION file it stored on the server and uploads/deletes only what changed since then.
Note: the script ignores itself (git-deploy) and .gitignore - they are never uploaded.
The .ini file name selects the environment. deploy.ini is the default; --env=staging uses staging.ini. This is the easy way to separate staging and production:
php git-deploy # uses .gitdeploy/deploy.ini
php git-deploy --env=staging # uses .gitdeploy/staging.ini
A config file is a standard .ini with one [section] per server (you can deploy to several servers from one file). All keys are optional except the connection details.
| Key | Default | Description |
|---|---|---|
scheme |
ftp |
ftp, ftps or sftp. |
host |
Server hostname or IP. | |
port |
21 (22 for sftp) |
Server port. |
user |
Login user. | |
pass |
Password. For SFTP with privateKeyPath set, this is used as the key passphrase instead (leave empty for an unencrypted key). |
|
path |
/ |
Remote target directory. For a non-chrooted SFTP user this must be the absolute path. |
passive |
true |
FTP passive mode (FTP/FTPS only). |
privateKeyPath |
`` | SFTP only: path to a private key file. When set, login uses the key instead of pass. |
skip |
false |
Skip this server entirely. |
You can also express the connection inline as a URL section header:
[sftp://deploy@example.com:22/var/www/app]
| Key | Default | Description |
|---|---|---|
gitDeployDirectory |
`` | Deploy only files under this local subdirectory. Usually paired with a substitutions entry that strips the prefix. |
substitutions[] |
[] |
preg_replace patterns applied to map local paths to remote paths, e.g. substitutions['#web/#'] = 'www/'. |
While uploading, git-deploy can swap your front controller for a maintenance page (it renames indexPath aside and moves maintenancePath into its place, then reverses it when done).
| Key | Default | Description |
|---|---|---|
maintenanceMode |
true |
Enable the swap during upload (skipped on first-time deploy). |
indexPath |
web/index.php |
Front controller, taken offline during deploy. |
maintenancePath |
web/.maintenance.php |
Page served while deploying. |
| Key | Default | Description |
|---|---|---|
clean_directories[] |
[] |
Directories emptied on every deploy (e.g. temp/cache). If none are set, git-deploy just prints a reminder to clear caches. |
file_permissions[] |
[] |
Paths chmod-ed to 0777 (created if missing) on every deploy. |
When the deployed composer.lock changes its resolved packages (detected by comparing each package's version and source/dist reference, not just composer.lock's content-hash), git-deploy reconciles the server's dependencies. There are two strategies:
| Key | Default | Description |
|---|---|---|
uploadComposerLibs |
false |
false: run composer install on the server over SSH (SFTP only - see below). true: upload the changed vendor/<package>/ directories directly (works over FTP too; useful when the server has no Composer/SSH). |
sshPath |
`` | Working directory for the server-side composer install. Defaults to path; set it when path is chroot-relative and the SSH session needs the absolute path. |
composerCommand |
composer install -o --no-dev |
The command run on the server. e.g. sudo -u www-data composer install -o --no-dev to keep file ownership. |
The server-side composer install runs after the changed files (including the new composer.lock) are uploaded, so it sees the new lock, and before the REVISION file is written and before the after hook - so a failed install (non-zero exit) aborts the deploy without marking it successful. git-deploy requires an env-specific lock snapshot at .gitdeploy/<env>.composer.lock; it is created/updated automatically after each successful deploy.
| Key | Default | Description |
|---|---|---|
after[] |
[] |
URLs requested after the upload finishes, e.g. a script that runs DB migrations and clears caches. |
The response is streamed line by line, so long-running hooks (heavy ALTERs, backfills) show live progress and finish reliably. cURL is used with a write callback because PHP's built-in http:// stream wrapper buffers the whole body before returning. SSL peer verification is disabled (the URL comes from your trusted config and targets your own server). If the streamed body contains the marker DEPLOY FAILED, git-deploy aborts with a non-zero exit code - your hook can print that line to signal failure (the streamed HTTP status is locked at 200 once the first bytes flush).
[example]
; ftp | ftps | sftp
scheme = sftp
host = example.com
port = 22
user = deploy
path = /var/www/app/
; SFTP key auth - `pass` is the key passphrase (empty for an unencrypted key)
privateKeyPath = /home/me/.ssh/deploy_key
pass =
; take the app offline while uploading
maintenanceMode = true
indexPath = web/index.php
maintenancePath = web/.maintenance.php
; run composer install on the server when packages change
sshPath = /var/www/app/
composerCommand = "sudo -u www-data composer install -o --no-dev"
; run migrations / clear caches after upload
after[] = 'https://example.com/_deployment/after.php?token=secret'
; empty caches every deploy
; clean_directories[] = temp/cache
Roll back to the previously deployed revision (Composer-managed libraries are not reverted):
php git-deploy --revert
php git-deploy --env=staging --revert
php git-deploy -r <commit_hash>
php git-deploy --env=staging -r <commit_hash>
A tag or any other git-diff-understood identifier works too.
Preview without deploying (lowercase L):
php git-deploy -l
php git-deploy --env=staging -l
git-deploy stores a REVISION file in the remote path, containing the hash of the deployed revision. On each run it reads REVISION, diffs it against the target revision (git diff --name-status), and uploads the added/changed files and deletes the removed ones. It also writes PREVIOUS_REVISION so --revert can roll back.
For each changed file it uploads the content of the target revision (via git show), so uncommitted working-tree changes are never deployed. SFTP transfers use binary; FTP uploads always use binary mode too (ASCII mode corrupts large minified assets). Git submodules are detected at start-up.
Originally created by Bruno De Barros. This fork (Ciki/git-deploy-php) adds SFTP + key auth, maintenance mode, streamed after-deploy hooks, and Composer handling. Issues and contributions welcome at the fork's issue tracker.