Skip to content

Instantly share code, notes, and snippets.

@ntd
Last active March 9, 2026 15:59
Show Gist options
  • Select an option

  • Save ntd/325b869127011deae24395e5db3ed1df to your computer and use it in GitHub Desktop.

Select an option

Save ntd/325b869127011deae24395e5db3ed1df to your computer and use it in GitHub Desktop.
Using `deployer` (without provisioning) to deploy Silverstripe 6 webapps
<?php
/**
* This is the shared deployer code I use in my projects. Put it
* somewhere in your filesystem and then, for each Silverstripe project,
* add a `deploy.yml` similar to this one:
* ```yaml
* import: /path/to/deployer8-silverstripe6.php
*
* config:
* application: 'www.my.site'
*
* hosts:
* staging:
* hostname: myvps
* deploy_path: '/var/www/staging'
* production:
* hostname: myvps
* deploy_path: '/var/www/{{application}}'
* ```
* `dep provision` is **not** needed here: webserver, PHP and database
* configuration are your responsibility. Just be sure to have a working
* ssh connection to your server and to install composer, cachetool,
* unzip, acl and rsync.
*/
namespace Deployer;
require 'recipe/silverstripe.php';
// Any setting is overridable in `deploy.yml`
set('bin/php', '/usr/bin/php8.4');
set('bin/composer', '{{bin/php}} /usr/local/bin/composer');
set('bin/cachetool', '{{bin/php}} /usr/local/bin/cachetool');
set('bin/dbdump', <<<EOT
/usr/bin/mysqldump --compact --insert-ignore --add-drop-table \
-h"\$SS_DATABASE_SERVER" -u"\$SS_DATABASE_USERNAME" \
-p"\$SS_DATABASE_PASSWORD" "\$SS_DATABASE_NAME"
EOT
);
set('webroot', './');
set('dotenv', '.env.{{alias}}');
set('upload_options', [
'-R',
// The `-FF` argument allows to override the rules below
// with local `.rsync-filter` files: see `man rsync`
'-FF',
// Default filter rules: work if your whole silverstripe
// application is under `app` or `main`
'--filter=+ /app/***',
'--filter=+ /composer.*',
'--filter=+ /main/***',
'--filter=+ /themes/***',
'--filter=- /public/assets/',
'--filter=- /public/resources/',
'--filter=- /public/_resources/',
'--filter=+ /public/***',
'--filter=- *',
]);
// The user/group guessing logic in deployer is too fragile
set('http_user', 'www-data');
set('http_group', 'www-data');
task('deploy:env', function () {
upload(
get('dotenv'),
'{{release_or_current_path}}/.env',
[ 'progress_bar' => false ]
);
});
// Use `rsync` for updating code on the remote host
task('deploy:update_code', function () {
upload(
get('webroot'),
get('release_or_current_path'),
[ 'options' => get('upload_options') ]
);
});
// If `deployer` is run by different user from the web user (as I do),
// `sake db:build` will fail when regenerating the static error pages
// if the old ones were generated from the web.
// Avoid the problem by removing them before the regeneration process.
task('silverstripe:remove_static_pages', function () {
run('rm {{release_or_current_path}}/{{ shared_assets }}/error-*.html', nothrow: true);
})
->hidden();
before('silverstripe:build', 'silverstripe:remove_static_pages');
before('silverstripe:buildflush', 'silverstripe:remove_static_pages');
desc('Clear opcache cache');
task('cachetool:clear:opcache', function () {
run('{{bin/cachetool}} opcache:reset -n --fcgi');
});
after('deploy:success', 'cachetool:clear:opcache');
desc('Clear APCU cache');
task('cachetool:clear:apcu', function () {
run('{{bin/cachetool}} apcu:cache:clear -n --fcgi');
});
after('deploy:success', 'cachetool:clear:apcu');
after('deploy:failed', 'deploy:unlock');
task('backup:info', function () {
info("backup of <fg=green;options=bold>{{what}}</> from <fg=magenta;options=bold>{{where}}</>");
})
->hidden();
desc('Create a local dump of the remote database');
task('backup:database', function () {
// Source .env to get database settings
$dump = run('source {{current_path}}/.env && {{bin/dbdump}}');
// TODO `$dump` contains the whole database:
// this approach is not really scalable
$db = get('application', 'dump');
file_put_contents("./$db.sql", $dump);
});
desc('Fetch assets from the remote');
task('backup:assets', function () {
download(
'{{deploy_path}}/shared/{{shared_assets}}/',
'./{{shared_assets}}/',
[ 'options' => [ '--delete', '-a' ] ]
);
});
task('backup:success', function () {
info('backup terminated successfully!');
})
->hidden();
desc('Backup your project');
task('backup', [
'backup:info',
'backup:database',
'backup:assets',
'backup:success',
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment