Move off Laravel Forge
Provisore reads your Forge server through Forge's API and a read-only SSH scan, then recreates its sites, databases, database users, scheduled commands and daemons on a new server, with repositories, branches and deploy scripts filled in. The Forge box is only read and keeps serving traffic until you change DNS. Moving the data and the uploaded files is yours.
A migration is not one button
Provisore recreates the records. You move the data, the uploaded files, and the DNS, in that order. The second half is the long one.
Your Forge account, through Forge's API
The facts a server cannot tell you about itself: which repository each site is built from, which branch it tracks, its deploy script and its domain aliases. The new sites arrive with those filled in, ready to deploy. If you switch it on for a site, Provisore also copies that site's environment file from Forge and stores it encrypted on the new site. Link the site to its database and the DB_* values are rewritten to point at the new server.
The machine itself, over SSH, read-only
Sites, databases, database users and their grants, scheduled commands, and long-running processes. The scan reads configuration and never your application data, so it cannot tell you whether a database has rows in it.
What connecting actually puts on your server
Connecting is a one-liner you run as root on the Forge box. It adds three things, and removing those three things removes everything.
What it adds
- provisore-migrate, a login account, and its home directory
- /etc/sudoers.d/90-provisore-migrate, one sudoers file
- ~provisore-migrate/.ssh/authorized_keys, one key in that account's own file
What it does not touch
- No package is installed and no service is restarted
- Nothing is written under /opt
- sshd is not reconfigured
- Nothing goes into root's authorized_keys
The account has passwordless sudo, because the scan reads root-owned configuration across the whole machine. It expires by itself after 14 days, or you end it sooner from the last step of the migration.
What does not come with you
Applying a plan creates records: site rows, database rows, users, cron entries, daemons. Apart from an environment file you opt in to, it copies nothing.
Your data
Databases are created empty. Every row stays on the Forge box until you dump it and load it here yourself, and there is no button for that. Do the dump last, or do it twice. Anything written to the old server after your dump stays on the old server.
Your files
Nothing under a site's storage directory, and nothing else the application has written to disk. Avatars, invoices, generated PDFs and imports are yours to move, and they are the part people forget until a customer opens an old receipt.
Certificates and keys
A private key is never taken off a machine. The new server issues its own Let's Encrypt certificates. Over HTTP that waits until DNS resolves to the new server. With a Cloudflare token connected, DNS validation can issue them before the cutover.
Passwords
The old machine's passwords cannot be read back, so they are not migrated. New database accounts get newly generated passwords, revealable once.
Anything in memory
Redis and Memcached contents, queues and sessions stay where they are. Jobs already queued on the old machine run there, against the old database.
Anything installed by hand
Extra PHP extensions, system packages, tuned configuration, a binary somebody dropped into /usr/local/bin two years ago. The scan reads sites, databases, scheduled commands and processes, and nothing else.
The order to do it in
Everything up to the DNS change is additive. The new server is built beside the old one, and until you move the record nothing your users touch has changed.
- 01
Pick the Forge server
In the app, choose Import from Forge from the New menu or ⌘K. Paste a Forge API token, then pick the organization and the server. A server Provisore cannot read is listed with the reason rather than hidden.
- 02
Run one command on it
Provisore gives you a one-liner to run as root on the Forge box. It adds the three things listed above and changes nothing else.
- 03
Read the machine
The scan produces a plan of what it found and a separate list of what it is leaving behind, each with the reason. You can read it again at any point before you apply.
- 04
Choose the new server
Create one on Hetzner, register a fresh Ubuntu machine from any provider, or use a server you already run on Provisore. The Ubuntu release and the database engine come pre-filled from the scan.
- 05
Review and apply
Tick the rows you want. Provisore creates them in dependency order: databases, then database users, then sites, then daemons and cron. An apply is not a transaction. Each row ends created, failed with a reason, blocked naming the row it waited for, or skipped. Fix a failure and apply again, and nothing that succeeded is undone.
- 06
Deploy, move the data, then point DNS
A checklist takes it from there. Deploy each site, load each database and copy the uploaded files while the old server still serves every request. Changing DNS moves your traffic, and it is the first step you cannot undo. Then check the sites on the new server.
- 07
End the access
Once the new server is live, end Provisore's access from the checklist. It removes the three items from the Forge box, then reconnects to confirm they are gone.
Questions people ask before they start
Does anything change on my Forge server?
No. Provisore only reads it. It never provisions it, never deploys to it, and never counts it against your plan. It keeps serving its sites for as long as DNS points at it, so you can stop halfway through for a week and lose nothing.
What about the PostgreSQL version?
Forge installs PostgreSQL from the PostgreSQL project's own archive, which runs ahead of Ubuntu's, and data does not restore into an older major. A new PostgreSQL on Provisore is 18 unless you pick an older major, back to 14, so a server created for the migration is usually level with the Forge box or ahead of it. The scan still compares both machines and reports a mismatch on the review screen, which matters when you migrate onto a server that already runs an older engine. It warns and does not block, because applying moves no data and there is more than one way through. A version it could not read is reported as unknown, never as compatible.
What happens to the cron entries Forge wrote?
Forge writes every job it manages into /etc/crontab, the Laravel scheduler included, so the scan judges that file line by line. Your jobs come across. Only its run-parts dispatch lines count as the distribution's and stay behind. A user crontab is never excluded, for any reason. Certbot's renewal entries stay behind, because Provisore issues and renews its own certificates and two renewal mechanisms on one server break each other.
What happens to the forge database and role?
Left behind, but only when all three hold: the source is a Forge account, nothing on the machine holds a grant on it except its own forge account, and no site being migrated is named for it. The scan reads configuration, so it cannot tell whether that database is empty.
Can I migrate a machine that is not on Forge?
Yes. Choose Any Ubuntu machine instead and it is read over SSH only. Without Forge's API, repository, branch and deploy script come back empty, you fill them in before the first deploy, and there is no environment file to copy. Everything else works the same.
How many servers can I move at once?
Each migration is one old machine onto one Provisore server, and you can run several side by side. The old machines never count against your plan. The new servers count like any other, so an estate moves box by box at whatever pace your DNS changes allow.
The full procedure, including the exclusion rules and what the review screen shows you, is in the migration guide. If you are moving more than a couple of servers, tell us what the estate looks like and we will tell you where it gets awkward.
Build the new server beside the old one.
Start with one Forge server. 14 days free on any plan, card required.