<h1>Server Migration / Transfer <span class="badge bg-secondary align-middle" style="font-size:.55em;vertical-align:middle">Owner only</span></h1>
<p>
    The <strong>Migrate / Transfer Server</strong> engine clones this entire platform — the
    central database, every project, all managed user databases, and your files — to a new
    server and cuts over to it. It works on any host: <strong>cPanel, DirectAdmin, Plesk or a
    plain VPS</strong>, with no command line. Your current server is read-only throughout and
    is never changed until you deliberately flip.
</p>
<p>
    Open it from <a href="<?= $baseUrl ?>/settings/migrate">Settings → Core Configuration →
    Migration console</a>. Every control on that page carries a <b>?</b> hint, and the
    in-page <b>Guide</b> button mirrors this article.
</p>

<h2>Contents</h2>
<ul>
    <li><a href="#how">How it works</a></li>
    <li><a href="#prepare">Before you start</a></li>
    <li><a href="#scope">1 · Choose what moves</a></li>
    <li><a href="#preflight">2 · Preflight</a></li>
    <li><a href="#rehearsal">3 · Start (rehearsal)</a></li>
    <li><a href="#commit">4 · Commit the cutover</a></li>
    <li><a href="#userdbs">5 · Restore user databases</a></li>
    <li><a href="#flip">6 · Flip &amp; go live</a></li>
    <li><a href="#security">Security &amp; safety</a></li>
    <li><a href="#troubleshoot">Troubleshooting</a></li>
</ul>

<!-- =========================================== -->
<h2 id="how">How it works</h2>
<p>
    Migration runs as a pipeline of clear, checksummed steps. The key idea is a self-contained
    <strong>agent</strong>: a small PHP file that travels inside the bundle and runs
    <em>on the new server</em>. Because it runs there, it can reach the new server's database
    (which is almost always <code>localhost</code>-only on shared hosting) — that is what lets
    the same flow work on every kind of host.
</p>
<ol>
    <li><strong>Preflight</strong> — connect to the destination and confirm it's reachable, writable and PHP/DB-ready. Changes nothing.</li>
    <li><strong>Snapshot</strong> — package the selected databases (SQL dumps) and files (zip archives) into a <em>bundle</em>, with a SHA-256 checksum for every artifact.</li>
    <li><strong>Transport</strong> — upload the bundle over SFTP/FTP. Resumable: a re-run only sends what's missing.</li>
    <li><strong>Verify</strong> — the agent re-hashes every uploaded file against the manifest, proving a byte-perfect copy.</li>
    <li><strong>Provision</strong> — the agent creates or connects the target database.</li>
    <li><strong>Restore</strong> — the agent loads the database, unpacks the files and rewrites the config on the new server.</li>
    <li><strong>Flip</strong> — once you've verified the new site, retire this old server (it shows a "moved" notice) and point DNS across.</li>
</ol>
<p>
    A <strong>rehearsal</strong> runs steps 1–5 in dry-run: the whole plan is exercised and
    verified without changing the destination, so you can prove readiness before committing.
</p>

<h2 id="prepare">Before you start</h2>
<p>Have these ready for the new server:</p>
<ul>
    <li><strong>SFTP/FTP access</strong> — host, username, password (SFTP over SSH is strongly preferred).</li>
    <li><strong>Remote base path</strong> — the absolute folder where the site lives, e.g. <code>/home/acme/public_html</code>.</li>
    <li><strong>New site URL</strong> — an address the new server answers on <em>during</em> migration. A temporary hosting URL, or your real domain pointed at the new IP via your computer's <code>hosts</code> file, both work.</li>
    <li><strong>A MySQL database + user</strong> on the new server. On shared hosting, create the database and user in the panel first (names are usually prefixed, e.g. <code>acme_ppphp</code>). On a VPS you can let the agent create it.</li>
</ul>
<p>Copy the PPPHP application files to the new server too — the migration moves your <em>data</em> and configuration; the platform code itself is deployed the same way you first installed it (or via the app bundle).</p>

<h2 id="scope">1 · Choose what moves</h2>
<p>
    In <strong>What will move</strong>, tick the platform database, the projects, each managed
    user database and the file sets to include. The running total shows the transfer size.
    Untick anything you don't need this time — you can migrate part of your estate and repeat
    later.
</p>

<h2 id="preflight">2 · Preflight</h2>
<p>
    Fill in the <strong>Destination server</strong> details and click <strong>Run preflight</strong>.
    It opens the connection, checks the base path is writable, and reports PHP/DB readiness —
    without transferring anything. Fix any red step before continuing.
</p>
<p>Three quick-check helpers sit right in the destination form:</p>
<ul>
    <li><strong>Browse</strong> — logs into the destination and lets you navigate its folders and pick the <em>Remote base path</em>, so you never mistype it.</li>
    <li><strong>Test connection</strong> — a fast check that the SFTP/FTP login works and the folder is writable.</li>
    <li><strong>Test database</strong> — briefly deploys the agent to the new server and confirms your <em>target database</em> credentials connect, telling apart a wrong password from a not-yet-created database. (Needs the destination host + new site URL set.)</li>
</ul>

<h2 id="rehearsal">3 · Start (rehearsal)</h2>
<p>
    With a destination set, <strong>Start migration</strong> packages the bundle, transports it,
    and the agent verifies every file and validates the restore plan. <strong>Nothing on the
    destination is changed.</strong> The live progress panel shows each phase and a full log.
</p>
<p>Repeat the rehearsal until it is clean — it's safe to run again, and transport resumes where it left off.</p>

<h2 id="commit">4 · Commit the cutover</h2>
<p>
    Open <strong>Target database credentials</strong> and enter the new server's DB host
    (usually <code>localhost</code>), the database name, user and password. Then click
    <strong>Commit cutover</strong>.
</p>
<p>The agent will:</p>
<ul>
    <li>provision / connect the target database,</li>
    <li>restore the <strong>platform database</strong> into it,</li>
    <li>unpack your files, and</li>
    <li>rewrite <code>config/database.php</code> (backing up any existing one) and record the new base URL.</li>
</ul>
<p>This changes the <em>destination</em> server only. Your current server keeps serving visitors until you flip.</p>

<h2 id="userdbs">5 · Restore user databases</h2>
<p>
    Managed user databases are restored individually, because each may live in its own database
    with its own credentials on the new server. In <strong>Cut over → Restore user databases</strong>,
    give each one a target database name, user and password, and click <strong>Restore</strong>.
    The row shows success or the exact error.
</p>

<h2 id="flip">6 · Flip &amp; go live</h2>
<ol>
    <li><strong>Verify</strong> — open the new site URL and confirm it works and your data is present.</li>
    <li><strong>Flip</strong> — click <strong>Flip to new server</strong>. This old server now shows a
        "this site has moved" notice pointing at the new URL. You (the owner) and logged-in staff keep
        full access, so you can still reach the console.</li>
    <li><strong>Point DNS</strong> — update your domain's records to the new server. Once traffic flows
        there, the old box can be retired.</li>
</ol>
<p>Changed your mind? <strong>Bring back online</strong> removes the notice instantly — the flip is fully reversible. When everything is settled, delete the <code>_ppmigrate/</code> folder on the new server (the agent's cleanup does this) so the bundle and agent are gone.</p>

<h2 id="security">Security &amp; safety</h2>
<ul>
    <li><strong>Owner-only.</strong> No other role can see or run migrations, or read this guide.</li>
    <li><strong>Secrets protected.</strong> The destination password is encrypted at rest; the agent's one-time token is kept on this server and never placed in the transported bundle.</li>
    <li><strong>Integrity proven.</strong> Every artifact is SHA-256 checksummed and re-verified on the destination before restore.</li>
    <li><strong>Confirmed &amp; reversible.</strong> Destructive actions require explicit confirmation and support a dry run first; the flip is one click to undo.</li>
    <li><strong>Source is read-only.</strong> Nothing on your current server is modified or deleted during snapshot, transport or commit.</li>
</ul>

<h2 id="troubleshoot">Troubleshooting</h2>
<ul>
    <li><strong>Preflight can't connect</strong> — check the protocol/port (SFTP 22, FTP/FTPS 21), the host (this is the SFTP host, not the website URL), and that the account is active.</li>
    <li><strong>"Agent unreachable"</strong> — the new site URL must serve the uploaded files. Confirm the base path is the web root and that the URL resolves to the new server (temporary URL or hosts-file entry during testing).</li>
    <li><strong>Checksum mismatch</strong> — a transfer was interrupted. Just run Start again; transport is resumable and will re-send the affected files.</li>
    <li><strong>Provision fails but connect succeeds</strong> — on shared hosting the panel owns database creation. Create the database in the panel and leave "Create the database" unticked; a successful connection is all that's needed.</li>
    <li><strong>Config not writable</strong> — ensure the <code>config/</code> folder on the new server is writable by the web user during migration.</li>
</ul>

<hr>
<h3>🔗 Related Help Topics</h3>
<p>
    <a href="database_manager" class="btn btn-sm btn-outline-primary me-1">Database Manager</a>
    <a href="updates" class="btn btn-sm btn-outline-primary me-1">Online Updates</a>
    <a href="licensing" class="btn btn-sm btn-outline-primary">Licensing</a>
</p>
