Skip to content

Your first sync

A sync drops and recreates your local database. That is the point of it, and it is also why the first run against a new target should be the one that changes nothing.

Work through these three in order. Each tells you something the next one depends on.

This guide assumes ddev-sync is installed in your project. It reaches the remote over your SSH agent, so make sure DDEV knows your key:

Terminal window
ddev auth ssh

Define a target as described in Configuration, then check it is there:

Terminal window
ddev sync targets
Terminal window
ddev sync --target production --steps db,files,sanitize,setup --dry-run

This builds the whole plan and prints it without executing it, without connecting to the remote and without writing a file. One exception: a target whose connection comes from Deployer is resolved first, which runs dep config in the web container and so evaluates the project’s own deploy.php.

Plan for target 'production' typo3@13
acme - deploy@web01.example:22 /srv/app/
Nothing will be executed.
Features node-build detected: all of: file package.json, npm script build
1 preflight remote ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p 22 deploy@web01.example
2 db.dump remote mysqldump --single-transaction $TYPO3_DB_NAME
3 db.download - streamed as part of db.dump into .ddev/.downloads/dump.sql
4 db.import local mysql -h ddev-acme-db db < .ddev/.downloads/dump.sql
5 files.sync rsync rsync -a ... :/srv/app/public/fileadmin/ ...
7 sanitize sql 10 rules: clear-be-sessions, ...
8 setup web composer install --no-interaction

Read three things here:

  • The profile label in the header — typo3@13 above. If that is not the variant you expected, the rest of the plan is for the wrong framework version, and nothing else you read matters.
  • The detected features. A frontend build that you did not want adds minutes; --no-features turns detection off for a run.
  • Which rules the sanitize step will apply, by name.
Terminal window
ddev sync --target production doctor

The output below is illustrative — an all-OK run is what you are aiming at, not something that has been observed through the add-on. The ssh connection and deploy path rows have so far only been seen failing, because no successful connection to a real remote host has been made through the add-on yet:

Doctor for target 'production'
OK ssh connection web01.example
OK deploy path /srv/app/
OK disk space 184 GB free
OK sanitize tables 3 required tables present
OK web container ddev-acme-web answers
OK ssh agent /home/.ssh-agent/proxy-socket

Under provider: docker three of those rows read differently, because the far side is a container inside your production stack rather than the host you connected to:

OK sync endpoint prod.example
OK deploy path /data/ (in the endpoint)
OK path readable

sync endpoint says the endpoint answered — not that the Docker host did, which it may well have while the endpoint was down. deploy path spells out that the path was read inside the container, which is the one place intuition from a server target is wrong. And path readable is the row that catches the likeliest misconfiguration of that provider: a path that exists and that the endpoint’s own user cannot read, which would otherwise surface in the middle of the file transfer.

This is the step that catches the failures worth catching before gigabytes move: an unreachable host, a wrong deploy path, too little disk space, and — the one that only shows up here — a sanitize rule that requires a table the remote database does not have. The last three rows are about your own machine rather than the remote: your free disk space, whether commands can reach the project’s web container, and whether DDEV’s forwarded ssh agent socket is there. A rule that cannot run is a rule that leaves production data in place, so doctor refuses to call that fine.

Start with the database only, on a project you can afford to break:

Terminal window
ddev sync --target production --steps db

You will be asked to confirm before the local database is dropped. When that looks right, add the rest:

Terminal window
ddev sync --target production --steps db,files,sanitize

setup is deliberately not in that list. Add it when you want composer install, migrations and frontend builds to run too — it is usually the slowest part by far.

Check that sanitising did what you expect, in whatever way suits your project — a look at the user table is enough:

SELECT email FROM users LIMIT 5;

Addresses should be unreachable ones, and password hashes should all be identical and useless. If they are not, the rules for your project type did not run or did not match your schema: see the profiles reference for what has been verified for your framework, and treat the copy as production data until you have sorted it out.

  • It hangs with no output. You are in a terminal-less context (a script, CI) and a prompt is waiting for an answer that cannot come. Pass --steps and --target explicitly. Recent versions fail with a message instead.

  • --steps refuses a step you named. The step is not available for this project, because it would contribute nothing to the plan, and the message lists what is. A project with no S3 configuration, for example, is not offered s3. A step name that is simply misspelled gets a different message, saying it is unknown. See which steps are offered.

  • no environment file found in the project. The profile expected a .env your project does not have. For TYPO3 that is the default and the step is off; see Configuration.

  • A setup command fails. The sync stops there, after the database has been imported and sanitised. Fix the command, then re-run with --steps setup only.

  • You pressed Ctrl-C. The sync ends with a sentence rather than a traceback, and exits 130. The sentence names where it stopped and the last step that finished (in the post_sync hooks no running step is named), and says what state that leaves things in. It does not undo or clean up anything, so read it before you do the next thing. What it tells you depends on where you were:

    • At a confirmation prompt, including “This will drop the local database … Continue?”. That step was waiting for an answer and so changed nothing. The message still reports what earlier, finished steps left behind.
    • Before anything touched the database. The message says where it stopped and makes no claim about your data.
    • During the import, once the drop has begun. The local database may be partly replaced with production data, and it is not sanitized. Re-run with --steps db,sanitize, or treat the copy as production data.
    • After the import, before sanitizing. The local database has been replaced with production data and was not sanitized. Either re-run with --steps sanitize, or treat the copy as production data until you have.
    • During sanitizing, in a run that imported the database too. It holds production data and was “not fully sanitized”: some rules may already have applied. Re-run with --steps sanitize, or treat the copy as production data.
    • During sanitizing, in a --steps sanitize re-run. The run has no import of its own to reason from, so it cannot see what an earlier one left and does not pretend to: “sanitizing did not finish, so if the local database holds production data it may be only partly sanitized”. The remedy is the same — re-run --steps sanitize, or treat the copy as production data. Only you can tell which of the two it is.
    • In the post_sync hooks. They “may not have finished”. The state of the database is still reported.
    • At any earlier step. The post_sync hooks do not run, and the message says so. If you keep cleanup in post_sync, it has not happened.

    Stopping between the import and the end of sanitizing leaves production data on your machine; that is what the message is there to tell you.

  • Host key verification failed. ddev-sync accepts a host it has never connected to without asking and remembers its key, so a first connection needs nothing from you, whether you run it from a terminal or from a script. The one exception is a jump host: its key is not covered, see below. doctor shows ssh’s own words on the ssh connection row, and a failed sync prints them in full, with the fingerprint, below “Run ‘ddev auth ssh’ and try again”. For this message that advice is not the fix: your key is not the problem. Which of two things it is shows in the words before it:

    • Host key for web01.example has changed and you have requested strict checking. The host presented a different key from the one remembered, and ssh refuses it. This is the one case that needs a person, because it has two very different explanations. Either the server was rebuilt or its keys were regenerated, which is harmless, or something else is answering in its place, which is not. Find out which before you make the message go away: ask whoever runs the server, or compare the fingerprint from the full output of a failed sync with one they give you. Once you know the new key is the right one, forget the old one, and the next run remembers the new one:

      Terminal window
      ddev exec -s sync ssh-keygen -R web01.example

      Use the host that ssh’s message names, which for a target with a jump_host is the bastion’s, not the target’s, and write it as '[web01.example]:2222' when that host does not use port 22, because ssh remembers a host on another port separately.

    • Host key verification failed. on its own, for a target with a jump_host. ddev-sync’s trust-on-first-use covers the host you are syncing from, not the bastion in front of it, so the bastion’s key is unknown and, with no terminal to ask on, refused. Tell ssh about the bastion once. Either connect to it from a terminal and answer the question:

      Terminal window
      ddev exec -s sync ssh -p 22 tim@bastion.example

      or give it an entry in .ddev/ddev-sync/home/.ssh/config, which ssh reads for the bastion too:

      Host bastion.example
      StrictHostKeyChecking accept-new
    • Host key for ddev-sync-endpoint-… has changed, for a target with provider: docker. A sync endpoint is remembered under a name of its own — ddev-sync-endpoint-<host>-<sync_port> — rather than under the address it answers on, so that two stacks published on one Docker host do not look like one host changing its key. The overwhelmingly likely cause is a redeploy that dropped the endpoint’s host-key volume, and the sync says so; whoever deploys the stack can confirm it. Then forget the old key by the alias, not by the host:

      Terminal window
      ddev exec -s sync ssh-keygen -R ddev-sync-endpoint-prod.example-2222

    Remembered keys are kept in .ddev/ddev-sync/home/.ssh/known_hosts and survive restarts, but ddev add-on remove ddev-sync deletes them, so after a reinstall each host is accepted again on first contact.