wineworlds / deployer-recipes
A collection of recipes for deployer
Package info
github.com/wineworlds/deployer-recipes
Type:deployer-recipes
pkg:composer/wineworlds/deployer-recipes
Requires
- deployer/deployer: ^v7.3
README
A collection of recipes for Deployer — with a full, production-tested toolkit for Shopware 6: deploy, one-time provisioning, publishing a local instance, moving a shop between servers, and selective data migration (relaunch).
Install
composer require --dev deployer/deployer composer require --dev wineworlds/deployer-recipes
Import a recipe in your deploy.yaml (see below). Then run tasks with
vendor/bin/dep <task> <host>.
Recipes
| Recipe | Import path | What you get |
|---|---|---|
| Shopware | recipe/shopware.php |
deploy, sw:shared:init, sw:publish, sw:transfer, migrate:*, fetch |
| TYPO3 (rsync) | recipe/typo3-rsync.php |
Legacy TYPO3 deploy + fetch/sync/transfer |
| Shopware (rsync, legacy) | recipe/shopware-rsync.php |
Older git+build+rsync Shopware deploy |
| generic | recipe/fetch.php, recipe/sync.php, recipe/transfer.php |
Bare DB/file primitives |
Shopware
Mental model
There are three places, and it helps to keep them apart:
LOCAL (control machine) SERVER (per host)
─ runs `vendor/bin/dep` ─ {{deploy_path}}/releases/… (each deploy)
─ .env.local → local dev DB ─ {{deploy_path}}/current (symlink → newest release)
─ .env.<alias> → target templates ─ {{deploy_path}}/shared (persists across releases)
─ bin/migrate.tables (whitelist) └─ shared/.env.local = the REAL secrets
└─ shared/files, public/media, … = media
The env files (this is the part people trip over):
| File | Where | Purpose |
|---|---|---|
.env |
committed, shipped in every release | base / dev defaults only — no real secrets |
.env.local |
local only, git-ignored | your local dev DB/secrets (used by sw_local_db) |
.env.<alias> |
local only, git-ignored | template for a target host → uploaded once as its shared/.env.local |
shared/.env.local |
on the server, in shared/ |
the host's real secrets, symlinked into every release |
The recipe reads real credentials from .env.local (server: the shared one;
local: your dev one) — never from the committed .env.
Prerequisites: a
shopware/production-style project withshopware/deployment-helperinstalled (composer require shopware/deployment-helper); the deploy runsvendor/bin/shopware-deployment-helper runon the target.
Configure deploy.yaml
Minimal, annotated — this is all you need for deploy, sw:shared:init,
sw:publish and migrate:*:
import: - vendor/wineworlds/deployer-recipes/recipe/shopware.php config: # chmod avoids needing ACL/sudo on shared hosting (recommended): writable_mode: chmod # ONLY needed for `sw:transfer` — the alias of the OLD/source server: # sync_from_host: old hosts: prod: hostname: server.example.de remote_user: deployer deploy_path: /var/www/prod # releases/, current, shared/ live here bin/php: /usr/bin/php83 # PHP binary on the server (for bin/console) keep_releases: 3 # optional (Deployer default: 10) # optional — clears opcache after the symlink: # cachetool: /run/php/php83-fpm.sock after: deploy:failed: deploy:unlock
Add more hosts as needed — each host <alias> maps to a local template
.env.<alias> (see sw_env_template to change the naming).
Host keys that matter
| Key | Required | Meaning |
|---|---|---|
hostname / remote_user |
yes | SSH target (also honors port: and your ~/.ssh/config) |
deploy_path |
yes | base dir on the server (holds releases/, current, shared/) |
bin/php |
recommended | PHP CLI on the server used for bin/console |
keep_releases |
no | how many releases to keep |
cachetool |
no | php-fpm socket/URL → opcache is cleared after each deploy |
Local files you must keep (git-ignored)
# .gitignore .env.local # your local dev secrets .env.* # per-host templates (.env.prod, .env.old, …) — REAL secrets!
Create a host template by copying .env and filling in the real values for
that server (DATABASE_URL, APP_URL, APP_SECRET, MAILER_DSN, APP_ENV=prod, …):
cp .env .env.prod # then edit .env.prod
Commands
deploy — release a new version
vendor/bin/dep deploy prod
Pipeline: deploy:prepare → deploy:clear_paths → sw:deployment:helper
(migrations, plugins, theme, cache via the deployment-helper) →
sw:touch_install_lock → sw:health_checks → deploy:publish.
- Needs:
sw:shared:initmust have run once before the first deploy (soshared/.env.localand the shared dirs exist). - Headless (no storefront): set
sw_health_check: falseinconfig:— thesystem:check --context=pre_rolloutstep only makes sense with a storefront.
sw:shared:init — one-time provisioning (run once per host)
vendor/bin/dep sw:shared:init prod
Creates shared/ + all shared dirs, uploads .env.prod → shared/.env.local,
uploads config files (auth.json, public/.htaccess, public/.user.ini) and
your local media, and fixes group permissions. Idempotent — existing env/config
on the host is never overwritten.
- Needs: local
.env.prod(the host template). Errors clearly if missing. - Sub-tasks if you want the pieces:
sw:shared:skeleton(no media) andsw:shared:media:upload.
sw:publish — bring a locally developed instance online
vendor/bin/dep sw:publish prod # aborts if the target is locked/exists vendor/bin/dep sw:publish prod --force # ignore lock + skip confirmations
Does it all in one go: sw:target:guard → sw:shared:init → sw:db:publish
(your complete local DB → the target DB) → deploy → sw:target:lock.
- Needs: local
.env.prod, local.env.local(your dev DB), andshopware/deployment-helper. - Protection: a lock file (
{{deploy_path}}/.sw-publish.lock) marks a published instance. A secondsw:publishaborts unless--force. If the target already has acurrent/without a lock, it asks for confirmation. sw:db:publish prodis also callable on its own (overwrites the target DB).
sw:transfer — move a shop to another server
# in config: sync_from_host: old cp .env .env.newserver # fill in the new server's values vendor/bin/dep sw:transfer newserver --force
Migrates an existing shop source → target: shared media and the complete
DB are copied from sync_from_host to the selected host, then a fresh release is
deployed. Media + DB are routed through your local machine, so no direct
source→target SSH is required (Deployer already has SSH to both).
- Needs:
config.sync_from_host(source alias), both hosts defined, and a local.env.newservertemplate for the target. - Call it on the TARGET host (
dep sw:transfer newserver); the source comes fromsync_from_host. - Same lock protection and
--forceassw:publish.
migrate:* — selective data migration (relaunch)
Copies selected business data (customers, orders, newsletter, number ranges,
promotions …) from your locally migrated DB into an existing target shop —
without touching the target's catalog/config. This runs local → target host
(no sync_from_host).
vendor/bin/dep migrate:backup prod # 1. rollback point (dumps target DB) vendor/bin/dep migrate:integrity-check prod # 2. schema / orphan / row-count report vendor/bin/dep migrate:tables prod # 3. push whitelisted tables (atomic) vendor/bin/dep migrate:postprocess prod # 4. fix refs + cache:clear + dal:refresh:index # if needed: vendor/bin/dep migrate:rollback prod -o backup=var/migrate-backups/<file>.sql.gz vendor/bin/dep migrate:switch:domain prod -o sc_from=old.de -o sc_to=new.de -o app_url=https://new.de vendor/bin/dep migrate:maintenance:enable prod # / :disable
- Needs: a whitelist file
bin/migrate.tables(one table per line,#= comment) and a local DB holding the migrated source data. - Configure the specifics in
config:(all optional, sensible defaults):
config: migrate_tables_file: bin/migrate.tables # whitelist location migrate_backup_dir: var/migrate-backups # where backups are stored (local) migrate_fallback_shipping_method: '' # 32-char hex id, for orphaned deliveries migrate_shipping_method_map: {} # { oldHexId: newHexId } (applied first)
Example bin/migrate.tables:
# Customers
customer
customer_address
# Orders
order
order_line_item
order_delivery
# Number ranges (order/customer counters!)
number_range_state
fetch — pull a remote state to local
vendor/bin/dep fetch:db prod # dump remote DB → import into local vendor/bin/dep fetch:files prod # download shared media → local vendor/bin/dep fetch prod # both
Reads the remote credentials from the host's .env.local.
Typical workflows
| Goal | Commands |
|---|---|
| First time online (local shop → new server) | cp .env .env.prod (edit) → dep sw:publish prod |
| Routine deploy (code only) | dep deploy prod |
| Server move (old → new) | set sync_from_host: old, cp .env .env.new (edit) → dep sw:transfer new |
| Relaunch (migrate live data into a rebuilt shop) | dep migrate:backup → migrate:integrity-check → migrate:tables → migrate:postprocess |
Settings reference
| Setting | Default | Purpose |
|---|---|---|
sw_env_template |
.env.<alias> |
local env template uploaded as shared/.env.local |
sw_shared_config_files |
auth.json, public/.htaccess, public/.user.ini |
config uploaded once by shared:init |
sw_shared_media_dirs |
files, public/media, public/thumbnail, public/sitemap, public/export |
media moved by publish/transfer |
sw_deploy_lock_file |
{{deploy_path}}/.sw-publish.lock |
publish/transfer protection lock |
sw_mysqldump_options |
--single-transaction --quick --no-tablespaces --skip-comments --default-character-set=utf8mb4 |
dump options for full dumps |
sw_health_check |
true |
run system:check during deploy (false for headless) |
dotenv |
{{current_path}}/.env.local |
file Deployer sources before each run() (real secrets). Migrating from an old hoster that keeps creds in .env? override with {{current_path}}/.env |
migrate_tables_file |
bin/migrate.tables |
whitelist for migrate:tables |
TYPO3 / generic
import: - vendor/wineworlds/deployer-recipes/recipe/typo3-rsync.php config: sync_from_host: live sync_to_host: preview
vendor/bin/dep sync preview # sync:db + sync:files (host → host) vendor/bin/dep fetch preview # fetch:db + fetch:files (host → local) vendor/bin/dep transfer preview # sync:db + full deploy_path rsync