You move a Linux server from your previous provider to a seed without reinstalling. Operating system, services, accounts and configuration all come along. Left out are only the parts that keep the seed bootable and reachable: kernel, boot files, file system table and network configuration.
You work in the rescue system for this, a self-contained live system that runs in memory and leaves the seed's disk untouched. That is the only way to overwrite the root file system while nothing is running on it.
For most moves, Move a Linux server from another provider to the dataforest cloud is the better choice. There you set the services up freshly on the seed and bring over only your data. It takes longer, but it is low-risk and leaves you with a clean, current system.
Before you start. This route reaches deep into the target system and can leave the seed unusable. You are working with root privileges on your own systems, and carrying this out is your responsibility. Check each command before you run it, and make sure you have a backup beforehand.
When this route pays off
When your server has grown over the years and nobody can say exactly how it was set up. When many services interlock and rebuilding them costs more time than transferring them. Or when you need an exact copy, down to accounts and passwords.
Three things speak against it. It demands confidence on the command line, especially when mounting file systems. If something goes wrong, the seed may only be reachable through the console after the restart. And you carry over all the baggage of the old system instead of shedding it during the move.
Your data on the old server is never affected. If the attempt fails, you restore the snapshot you take beforehand and start again.
What you need
- an old server running Debian or Ubuntu
- a seed with Debian or Ubuntu and the same processor architecture as the old server, as a rule
amd64 rootaccess on both machines andrsyncon the old server- a maintenance window whose length depends on your amount of data
This guide does not apply to other distributions. AlmaLinux and RockyLinux keep their network configuration elsewhere, and our images of these distributions partition the disk differently.
The same distribution or version is not required. The seed's kernel also boots an older system. We tested Debian 12 and Ubuntu 22.04 on a Debian 13 seed and Ubuntu 24.04 on an Ubuntu 24.04 seed, each including updates and another restart afterwards.
There is one limitation for running it afterwards, though. If your distribution differs from the seed's, your package manager does not know the running kernel and delivers no security updates for it. For a server you intend to run long term, the other route is then the better choice. If distribution and major version match, the question does not arise.
Prepare
Create the seed and let it boot fully once, so that network, boot loader and file system table are set up. What to consider regarding size, IPv4 address and firewall is covered in the main guide.
Then take a snapshot in the Snapshots tab, named before_transfer for example. It is your way back if the seed does not boot after the transfer.
Next, generate a key for the move on the seed and add it on the old server, as described in the main guide under Connect the two servers:
ssh-keygen -t ed25519 -C "migration" -f /root/.ssh/migration -N ""
It then sits on the seed's disk and is available to you in the rescue system.
Finally, stop every service on the old server that writes data, databases above all. From here on your operations pause until the seed takes over.
Switch to the rescue system
Switch the seed to Rescue mode under Seed Rescue in the Manage tab and confirm with Save and restart. Your SSH keys apply there as in normal operation.
Give it a minute before you log in. Because the rescue system has its own host keys, your computer reports a mismatch. Remove the old entry:
ssh-keygen -R <SEED-IP>
ssh root@<SEED-IP>
There, lsblk -f gives you an overview. You see two partitions: a small one with vfat, which is the boot partition, and a large one with ext4, your root file system. Do not touch the small one, the boot loader lives there. On our Debian and Ubuntu images the large one is /dev/sda2.
Only for Debian 13 seeds and older systems. The file system of a Debian 13 seed uses the orphan_file feature, which Debian 11, Ubuntu 22.04 and older systems do not know. The transferred system boots at first. But as soon as an update rebuilds the boot environment, the next start aborts with a failed file system check. Remove the feature now, while the file system is not mounted:
e2fsck -f /dev/sda2
tune2fs -O ^orphan_file /dev/sda2
Then mount the root file system:
mkdir -p /mnt/target && mount /dev/sda2 /mnt/target
The exclusion list
It is the core of the procedure, because whether the seed still boots and stays reachable afterwards depends on it. Create it in the rescue system:
cat > /root/exclude.txt <<'END'
/dev/*
/proc/*
/sys/*
/run/*
/tmp/*
/mnt/*
/media/*
/lost+found
/boot/*
/lib/modules/*
/usr/lib/modules/*
/vmlinuz*
/initrd.img*
/etc/fstab
/etc/hostname
/etc/hosts
/etc/machine-id
/var/lib/dbus/machine-id
/etc/network/interfaces
/etc/network/interfaces.d/*
/etc/netplan/*
/etc/systemd/network/*
/etc/udev/rules.d/70-persistent-net.rules
/etc/cloud/*
/var/lib/cloud/*
/etc/ssh/ssh_host_*
/root/.ssh/*
/etc/swap
/swapfile
/swap.img
END
Read in groups: the first entries are directories the kernel fills itself at runtime. The next ones hold the seed's kernel and boot files in place. Then come the file system table, host name and network configuration. Without these lines your seed is no longer reachable after the restart, because it would take over the old server's network settings and there is no DHCP with us. This applies in particular to servers set up without cloud-init. They usually write their fixed IP address straight into /etc/network/interfaces. The last entries protect cloud-init, the machine ID, the host keys, your own access and the swap file.
The transfer
rsync -aAXHx --numeric-ids --delete --info=progress2 \
-e "ssh -i /mnt/target/root/.ssh/migration" \
--exclude-from=/root/exclude.txt \
root@<OLD-SERVER-IP>:/ /mnt/target/
Run it with --dry-run --stats first. Then nothing happens, and the statistics show you what would.
-x keeps rsync on the root file system, so network drives mounted on your old server do not come along. If it has separate partitions for /home or /var, fetch those individually afterwards. --numeric-ids is correct because you are transferring the old server's user database along with everything else. --delete is not optional. Without it, files from the seed image that do not exist on your old server stay behind, and you end up with a hybrid of two distributions whose services fail on start. The entries on your exclusion list are unaffected. Messages like cannot delete non-empty directory: etc/cloud are therefore expected and correct.
Keep cloud-init
The panel places a new root password and a removed IPv4 address on the seed's cloud-init drive, and cloud-init applies them on the next start. Growing the file system with a larger plan also runs through cloud-init. So check whether the required programs are present in the transferred system:
ls /mnt/target/usr/bin/cloud-init /mnt/target/usr/bin/growpart /mnt/target/usr/sbin/qemu-ga
The guest agent (qemu-ga) belongs here because the panel reads your seed's state through it. If any of them is missing because your old server ran without cloud-init, install it after the first start. The network comes up without cloud-init too, since the seed's network configuration was preserved.
apt install -y -o Dpkg::Options::=--force-confold cloud-init cloud-guest-utils qemu-guest-agent
--force-confold keeps the seed's existing /etc/cloud/cloud.cfg. Without it, apt asks whether the file should be replaced with the package version. Keep it in any case. The package version blocks logging in as root with an SSH key. The remaining configuration, which lets cloud-init find the seed's drive, is already in /etc/cloud/cloud.cfg.d/, because /etc/cloud was excluded from the transfer.
Finally, reset the instance state so that cloud-init runs through completely once on the first start:
rm -rf /mnt/target/var/lib/cloud/*
Check and unmount
Check that the network configuration shows your seed's address and not the old server's. On Debian it is in /mnt/target/etc/network/interfaces.d/50-cloud-init, on Ubuntu in /mnt/target/etc/netplan/50-cloud-init.yaml. A kernel must still be present under /mnt/target/boot/.
Also check name resolution. If /etc/resolv.conf points to systemd-resolved, as usual on Ubuntu, there is nothing to do:
readlink /mnt/target/etc/resolv.conf
If the output does not point to systemd/resolve, the file usually still holds your previous provider's name servers. Many of them only answer inside their own network. Replace the file:
rm -f /mnt/target/etc/resolv.conf
printf 'nameserver 1.1.1.1\nnameserver 8.8.8.8\n' > /mnt/target/etc/resolv.conf
If anything is off, fix it now while the disk is still mounted. Then unmount and check the file system:
sync && umount /mnt/target && e2fsck -n /dev/sda2
The output of e2fsck -n must contain clean. This only checks, it changes nothing.
Switch back
Set the panel back to Hard Drive Mode and restart. The host keys change again here too, so clear the entry once more with ssh-keygen -R <SEED-IP>.
Right after the start, name resolution may take a moment. Wait a minute before concluding there is an error. systemctl --failed then shows whether anything did not come up cleanly.
What is different afterwards
The seed keeps its own host name and network configuration. Everything else comes from the old server, including the user accounts and their passwords. The exception is root. On the first start with cloud-init, root receives the password you set when creating the seed. Your SSH key keeps working because /root/.ssh was excluded. You can now remove the migration key, on the seed under /root/.ssh/migration* and on the old server the matching entry in authorized_keys.
Do not remove the seed's kernel. Your package manager does not know it, since it does not come from your old system. That is intended. If an update installs a kernel of your previous distribution, the seed still boots its own as long as its version number is higher. With a system older than the seed's, that is the case.
If the seed does not boot
If the seed does not come up, you reach it through the console in the panel and can see where it hangs.
- No network connection: check with
ip awhether the seed's address is set, and whether/etc/network/interfacesstill contains an old address. - Name resolution fails: replace
/etc/resolv.confas described above. - Boot ends in an emergency shell with a failed file system check: switch to the rescue system, run
e2fsck -f /dev/sda2andtune2fs -O ^orphan_file /dev/sda2there and restart.
If you cannot find the error, restore the snapshot and start again.
Switch the domain
Your system now runs on the seed. What is left is switching the domain, and that works exactly as with the other route. You first test under your real domain without touching the DNS records, then switch over. The main guide covers both in its sections on testing and switching over.
Keep the old server running for another two to four weeks before you cancel it.
