# Moving a site with Sync

> Copy content between your machine, staging and production in one click, with a preview first and an undo after.

Sync copies one site into another: its content, its media, and if you like its templates and settings. Pull the live site onto your laptop to work with real content, push a finished section from staging to production, or refresh staging before showing the client. You see what will change first, and can undo it afterwards.

![Sync, with production and staging connected.](https://docs.lettern.org/media/screens/operations-sync.webp)

Sync is a paid plugin from the store. It's free between local and staging sites; a public site needs a Sync licence to take part.

## Connect two sites

1. On the first site, open **Sync** and copy **This Site’s Key**. It starts with the site's address.
2. On the second site, open **Sync** and press **Connect a Site**.
3. Paste the key, give the site a name like _Production_, and press **Connect**.

That's all: no SSH, no database password, nothing to open in a firewall. Only the site you connect from has to be able to reach the other one.

> **Note:** Anyone with a site's key can read and replace its content. Keep it like a password. **Make a New Key** replaces it, and sites connected with the old one then need the new one.

Each site can also switch off **Other sites can pull from this one** or **Other sites can push to this one**.

## Pull or push

- **Pull** copies the other site into this one.
- **Push** copies this one into the other.

Press **Pull** or **Push** next to a connected site. Sync compares the two and shows you what changes, group by group. Tick what should move, then press **Pull Now** or **Push Now**.

| Group | What moves | Pushed by default | Pulled by default |
| --- | --- | --- | --- |
| Content | Entries and their history. You can choose collections. | Yes | Yes |
| Media | Uploaded files and their details | Yes | Yes |
| People | Accounts, roles and access | No | Yes |
| Each plugin's data | Form submissions, Commerce's orders and invoices, comments, analytics, assistant chats, logs | No | Yes |
| Site Files | Templates, components, collections kept in files, globals, forms, menus | No | No |
| Settings | Settings, except which plugins are on and the licence key | No | No |
| Other tables | Tables in the same database that no plugin claims | No | No |

Pushing is for what your team makes, so what visitors leave behind on a live site, like orders and form submissions, stays there unless you tick it. Pulling brings everything, the way you want production on your machine. Site files and settings usually travel by git; tick them when they don't.

Addresses are rewritten on the way: a page that links to `https://www.example.com` links to `http://localhost:8000` once it's on your machine. Media only moves what differs, so the second push of a big library takes seconds.

## What each site keeps

Whatever moves, the receiving site keeps:

- its passwords and API keys, its licence key and which plugins are on;
- its Sync key and connections;
- Commerce's mode and keys: a pulled shop stays in test mode on your machine, and a pushed one keeps selling live;
- everyone who is signed in.

## Undo a sync

Before Sync replaces anything, it makes a backup. **Came In Lately** lists what came into this site, and **Undo** puts back what was there before. To undo a push, open the other site's menu on the site you pushed from and choose **Undo Last Push**.

Undo also replaces whatever changed since the sync. The last three backups are kept.

## Big sites and slow hosts

Sync works a few seconds at a time, so no request comes near a host's time limit, and the admin shows how far it is and how long is left. If the connection drops or you close the tab, it carries on from where it was when you come back.

## When Sync says no

Sync checks before it starts, and tells you what to do:

- **Different versions.** If a group's tables differ between the two sites, that group can't move until both run the same versions of Lettern and the plugin. Other groups still can.
- **A plugin on one side only.** Its data stays where it is, and the preview says so.
- **The same site twice.** A site can't sync with its own key, with a copy of its own folder, or with a site sharing its database.
- **Busy.** One sync at a time per site. A second waits until the first ends.
- **Permissions and disk space.** The receiving site needs to write to the folders that change, and room for the incoming copy and the backup.
- **No licence.** A public site without a Sync licence takes no part. Add the key under **Licence** on that site.

Different databases (MySQL and SQLite) or table prefixes are fine: rows are converted on the way.

## For developers

### From the command line

```bash
php cms remote key                         # this site's key
php cms remote add production '<key>'      # connect the other site
php cms remote                             # the connected sites

php cms pull production                    # the usual set, after a preview and a question
php cms push staging content media         # just these
php cms pull production content --collections=posts,pages
php cms push production --dry-run          # the preview, and stop
php cms pull production --yes --replace='cdn.example.com=>localhost:8000/cdn'
php cms sync:undo --remote=staging         # undo the last push to staging
```

When a site's own address doesn't work from where you connect, such as two containers on one Docker network, tick **This server reaches it at another address**, or add `--url=http://web:8000` to `remote add`. For a copy of a site's folder, run `php cms remote key --new` on the copy to give it a key of its own.

### What moves, exactly

Media is `public/media` and its details in `content/media`, compared by size and time. Site Files is `content/`. Settings is `config/`. Folder paths are rewritten along with addresses.

The receiving site also keeps its `.env`, its secrets (from **Settings**, kept in `storage/`), its sessions, API and sign-in tokens, its queue and caches, and Commerce's outbox. When people are replaced, each session follows its person to their account with the same email, and someone who has no account on the other site keeps theirs.

For a group with different tables, run `php cms sync` on each site after updating, so both have the same columns.

### How it switches over

Nothing on the receiving site changes until everything has arrived. Rows wait in staging tables and files in a staging folder while the site runs as before. Then Sync backs up what is about to be replaced and switches over: all tables in one transaction, then the files. Backups are in `storage/sync/backups`.

Tables go in chunks in key order, and big files in pieces of 2 MB or half the host's `post_max_size`, whichever is smaller. If a request times out, the sync carries on from the last piece when you come back or run the same command again. The receiving site keeps its staged copy for ten minutes; after that it starts afresh. A second sync waits for the first, or for ten minutes if it stopped halfway.

### Security

Every request between two sites is signed with the key's secret, with its time and a one-time value, so a captured request can't be sent again and nobody without the key gets an answer. Paths that leave their folder, hidden files and links are refused on both sides. Syncing is kept away from the assistant and MCP: replacing a site is a person's decision. The permission **Push and pull this site’s content to and from other sites** decides who sees **Sync**.

### Plugins with their own tables

See [Sync for plugins](https://docs.lettern.org/plugins/sync).

---
Source: https://docs.lettern.org/operations/sync
