
You're leaving your VPS provider, and the server does more than one job: a website, a bot, a game server, cron jobs nobody remembers adding. The new provider gives you an empty VPS with a new IP address. The old server's users, keys, schedules and settings have to come along, and everything that trusts its IP has to learn the new one. This plan keeps the old server as your way back. Commands are for Ubuntu 24.04 and Debian 12.
Quick answer: Inventory the old server first: ports, services, timers, crontabs, users, packages, changed config and secrets. List everything tied to the old IP (DNS records, allowlists, SPF, reverse DNS) and lower DNS TTLs early. Rebuild on a fresh OS rather than copying the disk image, copy data with rsync while the old server runs, move databases with dumps, and test the new server by IP, reboot included. Switch in a quiet hour after a final sync, and cancel the old server only once its logs stay empty.
Step 1: Write Down What the Old Server Runs
Whatever you miss here, you find out after the old server is gone. Run these on it and save the output on your own computer (as root, drop the sudo):
sudo ss -tulpn
systemctl list-units --type=service --state=running
systemctl list-timers --all
ls /etc/systemd/system /etc/systemd/system/multi-user.target.wants
sudo ls /var/spool/cron/crontabs
ls /etc/cron.d
timedatectl
ss -tulpn lists listening TCP and UDP ports with their processes. Units you wrote yourself live in /etc/systemd/system, the place the systemd docs give units "created by the administrator", and multi-user.target.wants shows which are enabled at boot. Each user with a crontab has a file in /var/spool/cron/crontabs; print one with sudo crontab -u alice -l. Debian's cron runs all jobs in the system's time zone (cron(8)), so note what timedatectl shows.
Then the rest:
awk -F: '$3 >= 1000 && $3 <= 60000 {print $1, $6, $7}' /etc/passwd
apt-mark showmanual > manual-packages.txt
ls /etc/apt/sources.list.d
sudo dpkg --verify | grep ' c /'
sudo find /home /root /opt /srv /var/www -name '.env*' -not -path '*/node_modules/*' 2>/dev/null
sudo du -xh -d1 / 2>/dev/null | sort -h
- Users: the
awkline prints regular users (UIDs 1000 to 60000, the login.defs defaults);id aliceshows each one's groups. - Packages: the manually installed ones, plus third-party repositories such as Docker's in
sources.list.d. - Config:
dpkg --verifymarks changed config files with ac(dpkg). Files you created, such as your nginx site, don't appear, so check/etc/nginxtoo. - Secrets:
.envfiles, TLS keys in/etc/letsencrypt, DKIM keys, API tokens and app keys such as n8n's encryption key. - Size:
dushows the biggest folders, so the new disk fits.
Step 2: Find Everything Tied to the Old IP
The old IP hides in more places than DNS. Search your files for it, then for the old IPv6 address:
OLD=203.0.113.10
sudo grep -rIlF --exclude-dir=node_modules "$OLD" /etc /home /root /opt /srv /var/www 2>/dev/null
-F takes the dots literally, -I skips binary files and -l prints only file names (grep manual). A config that listens on the old address, like nginx's listen 203.0.113.10:443;, fails on the new server.
| Where the old IP is | Before the switch | At or after the switch |
|---|---|---|
A and AAAA records: domain, www, subdomains, hosts named in MX and SRV records | Lower the TTL | Point them at the new IP |
| Files the grep found | Note each one | Change the address |
| Allowlists: database clusters, API tokens, other servers' firewalls | Add the new IP | Remove the old IP |
SPF ip4: and ip6: entries (RFC 7208) | Add the new IP | Remove the old IP |
| Reverse DNS (PTR) of a mail-sending IP | Ask the new provider to set it | Check it |
| Players or partners who connect by IP | Announce the new address |
Lower the TTLs at least one old TTL ahead; Google's hosting move guide suggests at least a week, and our website move guide shows how to read the current value.
Allowlists fail somewhere else, so they're easy to miss: MongoDB Atlas only accepts connections from the project's IP access list, and a Cloudflare API token with an "Is in" IP filter only works from those addresses. Also check sudo ufw status on servers that only let your old IP in.
For a server that sends mail, Gmail's sender guidelines require a PTR record for the sending IP whose host name resolves back to that IP. Outbound port 25 may be blocked too: Google Cloud blocks it to external destinations, though some projects aren't restricted (checked 31 October 2026). Ask the new provider about both before you order.
Step 3: Rebuild or Copy the Disk Image
| Rebuild on a fresh OS | Copy the disk image | |
|---|---|---|
| What moves | What you pick from your inventory | Everything, leftovers included |
| Needs from the new provider | An OS template | A rescue system or image import, and a disk at least as large |
| OS release | Your choice | The old one |
| Network setup | Right from the first boot | The old server's addresses, fixed through a console |
| Downtime | Only the final sync | The whole disk copy, with the old server offline |
Rebuild unless you can't reinstall the software. A rebuild also leaves behind anything you didn't pick, which matters if you aren't sure the old server is clean. Keep the OS release while it's supported, so configs match; otherwise change now: Ubuntu 20.04's standard security maintenance ended in May 2025 (Ubuntu) and Debian 11's LTS on 31 August 2026 (Debian).
An image keeps the old network setup, in /etc/netplan/*.yaml on Ubuntu (netplan) and usually /etc/network/interfaces on Debian (interfaces(5)). If it runs cloud-init and the new platform gives a different instance ID, cloud-init treats the boot as a first boot: it renders the network configuration it selects and rotates the SSH host keys (first boot, boot stages).
A Space-Node VPS is KVM with full root access, and the order form offers Ubuntu 22.04, 24.04 and 26.04, Debian, Rocky Linux, AlmaLinux and CentOS Stream to match the old release; another OS or your own ISO goes through a ticket after ordering.
Step 4: Set Up the New Server and Copy the Data
Create each user from Step 1 under the same name before copying: rsync matches owners by name and falls back to the numeric ID when a name has no match (rsync). Use the groups id showed, and set the old time zone:
sudo useradd -m -s /bin/bash -G sudo alice
sudo passwd alice
sudo timedatectl set-timezone Europe/Amsterdam
Install what your package list needs. For the firewall, sudo ufw show added on the old server prints its rules as commands (ufw): run them on the new one, check that SSH is allowed, then sudo ufw enable.
Give the old server's root an SSH key for the new one (Step 3 of our website move guide), then copy from the old server while everything runs:
NEW=203.0.113.20
SSH='ssh -i /root/.ssh/migrate'
sudo rsync -aHAXR --info=progress2 -e "$SSH" /home /srv /opt /var/www root@$NEW:/
sudo rsync -aHAXR -e "$SSH" /etc/letsencrypt /etc/nginx/sites-available/example.com \
/etc/systemd/system/yourapp.service root@$NEW:/
-R keeps full paths, so /srv lands in /srv; later runs only send changes. Name only folders that exist and the /etc files from your inventory, never /etc/passwd, /etc/shadow, /etc/fstab or /etc/netplan, which describe the old machine's accounts, disks and network. From /root, copy single files and leave .ssh alone: the new server's copy holds this rsync's key.
On the new server, sudo systemctl daemon-reload loads the copied units. Don't enable or start the ones that write data or contact the outside world yet: two copies of a bot or a backup job do the work twice.
Move databases with dumps, not their files: PostgreSQL's manual says the server must be shut down for a usable file-level copy. Our MariaDB and MySQL and PostgreSQL guides cover dumps, users and grants, and apps have their own guides for Docker volumes, Pterodactyl Wings and Minecraft.
Step 5: Test Before You Switch
Test every service by IP while DNS still points at the old server: websites with curl --resolve as in the website guide, other TCP ports from your own computer:
nc -zv -w 5 203.0.113.20 22 80 443 25565
Each open port reports succeeded (nc). For UDP, join the game server by IP and port. Then reboot and compare the ports with Step 1; a service started by hand but never enabled stays down:
sudo systemctl reboot
# log in again, then:
systemctl --failed
sudo ss -tulpn
For mail, check outbound port 25 (if it times out, ask the provider) and the PTR record, which should print your mail host name:
nc -zv -w 5 gmail-smtp-in.l.google.com 25
dig +short -x 203.0.113.20
Step 6: Switch Over
In a quiet hour, in this order:
- Old server: stop everything that writes (
sudo systemctl stop cron yourapp.service, plus the website guide's 503 page) and send each crontab to the new server:sudo crontab -u alice -l | sudo ssh -i /root/.ssh/migrate root@$NEW 'cat > /root/crontab-alice'. - Dump and import the databases again. Rerun the first rsync line with
-i --delete --dry-run, read every*deletingline (a home folder only the new server has would go too), then run it with--delete. - Point the DNS records from Step 2 at the new IP.
- New server:
sudo crontab -u alice /root/crontab-aliceandsudo systemctl enable --now yourapp.service. - Old server:
sudo systemctl disable cron yourapp.service, so a reboot can't start a second copy.
To keep the SSH identity, so users and deploy jobs that connect by host name don't get a REMOTE HOST IDENTIFICATION HAS CHANGED warning, copy the host keys (sshd(8)) after the final sync:
sudo rsync -a -e "$SSH" /etc/ssh/ssh_host_* root@$NEW:/etc/ssh/
# then on the new server
sudo systemctl restart ssh.service
Then run ssh-keygen -R 203.0.113.20 on your computer and sudo ssh-keygen -R 203.0.113.20 on the old server, or their next connection to the new IP fails on the changed key.
Step 7: Wait, Then Cancel
Watch the old server: Google's hosting guide says to shut old hosting down once its logged traffic reaches zero:
sudo ss -tn state established
sudo tail -f /var/log/nginx/access.log
Apart from your SSH session, connections should die out as cached DNS answers expire. Also wait until your rarest scheduled job, such as a weekly report, has run on the new server. Then copy a last archive to the new server in case you missed something (- makes tar write to standard output, says the GNU tar manual):
sudo tar czf - /etc /home /opt /srv /var/www \
| sudo ssh -i /root/.ssh/migrate root@203.0.113.20 'cat > /root/old-server-final.tar.gz'
Set up backups on the new server, such as restic over SSH: the old server was your fallback until now. Remove the old IP from DNS, allowlists and SPF, and delete the migration key as in Step 8 of the website guide. After you cancel, the address isn't yours, and OWASP describes records pointing at deprovisioned resources as dangling, a route to subdomain takeover.
If It Still Fails
Cannot assign requested address. That's errno 99 (errno.h), in nginxbind() to 203.0.113.10:443 failed (99: Cannot assign requested address): a config still names the old IP, and the Step 2 grep finds it.Host key verification failed.in a deploy job. It stored the old server's host key: update it, or copy the host keys (Step 6).- Files show a number instead of an owner. The user didn't exist when rsync ran. Create it and rerun rsync, which then corrects the owners.
- Cron runs jobs at the wrong hour. Set the old zone with
sudo timedatectl set-timezone, thensudo systemctl restart cron. - Mail bounces, or an API refuses the new server. Check PTR, SPF and port 25 (Step 5), and the allowlists from Step 2.
FAQ
Can I take my IP address to the new provider? Usually not. A VPS address comes from the provider's address space, and RIPE NCC's IPv4 policy says that when an End User changes provider, such space "must be returned and the network renumbered". Addresses registered to you, such as Provider Independent space, can move.
Should I upgrade the OS during the move? Only if the old release is out of support. Otherwise move first and upgrade later, so you know which change broke something.
Can both servers run at the same time? Yes, until the switch; testing by IP depends on it. Keep anything that writes data, sends mail or runs on a schedule off on the new server until Step 6.
How early should I order the new VPS? Early enough to copy and test without hurry. Space-Node sets up a Standard VPS manually within 24 hours after payment, so count that day in.
Sources
All opened on 31 October 2026:
- Google: Changing your hosting, Email sender guidelines, Google Cloud: Sending email from an instance
- DNS, mail and IP addresses: RFC 7208 (SPF), BIND 9 dig manual, RIPE NCC IPv4 policy (ripe-826), RIPE NCC FAQ on keeping IP addresses
- Allowlists and takeovers: MongoDB Atlas IP access list, Cloudflare: Restrict tokens, OWASP WSTG: Subdomain takeover
- Release support: Ubuntu release cycle, Debian releases
- Networking and cloud-init: netplan generate, interfaces(5), cloud-init first boot, cloud-init boot stages
- systemd and cron: systemctl, systemd.unit, timedatectl, crontab(1), cron(8), Ubuntu 24.04 cron.service
- Users and packages (Debian 12): passwd(5), login.defs(5), useradd(8), passwd(1), id(1), apt-mark(8), sources.list(5), dpkg(1)
- Tools: ss(8), rsync(1), GNU grep manual, find(1), du(1), sort(1), mawk(1), nc(1), tar(1), GNU tar: archive file names, ufw(8)
- OpenSSH: sshd(8), ssh-keygen(1), sshconnect.c, sshconnect2.c, Ubuntu Server: OpenSSH server
- nginx and Certbot: ngx_connection.c, ngx_log.c, Ubuntu 24.04 nginx.conf and sites-available, Certbot user guide
- PostgreSQL: File system level backup, Linux errno.h