Skip to content

Instantly share code, notes, and snippets.

@nodefive
Last active May 17, 2026 16:20
Show Gist options
  • Select an option

  • Save nodefive/cda6573ebd85e27def3826c26532e869 to your computer and use it in GitHub Desktop.

Select an option

Save nodefive/cda6573ebd85e27def3826c26532e869 to your computer and use it in GitHub Desktop.

Migrating an OVH VPS to Arch Linux

Overview & Prerequisites

This guide walks through replacing a running Ubuntu installation on an OVH VPS with a fresh Arch Linux base system, using OVH's built-in Rescue Mode as the installation environment. No external ISO (not available for VPS) or boot image (again, arch it's not available for VPS) is used.

WARNING: This process will completely wipe your existing VPS disk. Back up all data, databases, configuration files, and SSH keys before proceeding.


Optional: Download and Mount a Snapshot of the Current System

Before wiping the VPS, you may want a full local copy of the current disk as a safety net. OVH allows you to create a snapshot via the control panel and download it as a QCOW2 image.

Create and Download the Snapshot

  1. Go to OVH Control Panel → Your VPS
  2. Create a snapshot from the control panel interface
  3. Once complete, OVH provides a direct download URL
  4. Download it to your local machine:
curl -o vps-snapshot.qcow2 "<OVH_SNAPSHOT_DOWNLOAD_URL>"

Convert QCOW2 to Raw Image

OVH snapshots are in QCOW2 format. Convert to a raw image for mounting:

qemu-img convert -f qcow2 -O raw vps-snapshot.qcow2 vps.img

Mount the Image Locally

Load the loop module if not already loaded:

sudo modprobe loop

Attach the image as a loop device:

sudo losetup -fP vps.img
sudo losetup -l   # confirm the assigned loop device, e.g. /dev/loop0

Check the partition layout:

sudo fdisk -l /dev/loop0

Mount the root partition (usually the largest one, e.g. p1 or p2):

sudo mkdir -p /mnt/vps
sudo mount /dev/loop0p1 /mnt/vps   # adjust partition number as needed

You can now browse and extract files from the old system at /mnt/vps.

Unmount When Done

sudo umount /mnt/vps
sudo losetup -d /dev/loop0

Before You Start Probe All Required Values

Run these commands on your currently running Ubuntu VPS before doing anything else. Record every value you will need them during the chroot configuration step and cannot retrieve them once the disk is wiped.

Network interface name:

ip -o link show | awk '$2 != "lo:" {print $2}' | tr -d ':'
# Example output: ens3
#  Save as <YOUR_INTERFACE>

Static IP address and prefix:

ip addr show | grep "inet " | grep -v "127.0.0.1" | awk '{print $2}'
# Example output: 15.204.86.133/32
#  Save IP part as <YOUR_VPS_IP>, prefix as <YOUR_PREFIX> (usually /32 on OVH)

Default gateway:

ip route show | grep default | awk '{print $3}'
# Example output: 15.204.86.1
#  Save as <YOUR_GATEWAY_IP>

DNS server:

resolvectl status 2>/dev/null | grep "DNS Servers" | awk '{print $3}' || \
grep nameserver /etc/resolv.conf | awk '{print $2}' | head -1
# Example output: 213.186.33.99
#  Save as <YOUR_DNS_IP>

Your SSH public key:

cat ~/.ssh/authorized_keys
#  Save the full key string as <YOUR_SSH_PUBLIC_KEY>

Your desired SSH port (pick a non-standard port, e.g. 25782):

#  Decide on a value and save as <YOUR_SSH_PORT>

Your admin username:

whoami
#  Save as <YOUR_USERNAME>

Your hostname:

hostname
#  Save as <YOUR_HOSTNAME> (or choose a new one)

Your timezone:

timedatectl | grep "Time zone" | awk '{print $3}'
# Example output: America/Sao_Paulo
#  Save as <YOUR_TIMEZONE> (or use UTC for servers)

Once collected, fill in this reference table before proceeding:

Variable Command to probe Your value
<YOUR_INTERFACE> ip -o link show e.g. ens3
<YOUR_VPS_IP> ip addr show e.g. 1.2.3.4
<YOUR_PREFIX> ip addr show e.g. /32
<YOUR_GATEWAY_IP> ip route show e.g. 1.2.3.1
<YOUR_DNS_IP> resolvectl status e.g. 213.186.33.99
<YOUR_SSH_PUBLIC_KEY> cat ~/.ssh/authorized_keys ssh-ed25519 AAA...
<YOUR_SSH_PORT> your choice e.g. 25782
<YOUR_USERNAME> whoami e.g. admin
<YOUR_HOSTNAME> hostname e.g. myvps
<YOUR_TIMEZONE> timedatectl e.g. UTC

Step 1: Entering OVH Rescue Mode

  1. Log in to the OVH Control Panel
  2. Navigate to Bare Metal Cloud VPS Your VPS
  3. Click the ... menu next to Boot and select Reboot in rescue mode
  4. Confirm the reboot OVH will send the temporary root credentials to your account email
  5. Wait approximately 3 minutes, then SSH in using port 22:
ssh root@<YOUR_VPS_IP>

Step 2: Preparing the Target Disk

Identify the disk

lsblk

The VPS disk is typically /dev/sda or /dev/vda. The rescue environment boots from a separate smaller disk (usually /dev/sdb).

Wipe and partition the disk

wipefs -af /dev/sdX
gdisk /dev/sdX

Inside gdisk:

  • o create new GPT table confirm with y
  • n partition 1 Enter (default start) +1M type ef02 (BIOS boot for GRUB)
  • n partition 2 Enter Enter type 8300 (Linux filesystem)
  • w write and exit confirm with y

Verify the result:

lsblk /dev/sdX
# Expected: sdX1 = 1M, sdX2 = remainder

Format the root partition

mkfs.ext4 /dev/sdX2

Mount the root partition

mount /dev/sdX2 /mnt

Step 3: Deploying the Arch Linux Base Image

Download the official Arch Linux bootstrap tarball directly to the rescue environment:

cd /tmp
wget https://mirrors.kernel.org/archlinux/iso/latest/archlinux-bootstrap-x86_64.tar.zst

Extract the bootstrap directly into the mounted root partition:

tar xf /tmp/archlinux-bootstrap-x86_64.tar.zst -C /mnt --strip-components=1

The --strip-components=1 flag is required to extract contents of root.x86_64/ directly into /mnt rather than creating a subdirectory.


Step 4: Chrooting and Core Configuration

Set up bind mounts

mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /dev /mnt/dev
mount --bind /dev/pts /mnt/dev/pts

Configure a mirror and initialize pacman keys

sed -i 's/^#Server/Server/' /mnt/etc/pacman.d/mirrorlist

Enter the chroot

chroot /mnt /bin/bash

Fix DNS inside the chroot

echo "nameserver 8.8.8.8" > /etc/resolv.conf

Initialize pacman keys

pacman-key --init
pacman-key --populate archlinux

Install the base system and required packages

pacman -Sy base linux linux-firmware grub openssh sudo git curl wget

When prompted to choose an initramfs provider, select mkinitcpio (option 1).


Network Configuration

OVH VPS instances use a /32 static IP with a gateway that is outside the subnet. This requires the GatewayOnLink=yes directive to be reachable.

Create the network configuration file:

cat > /etc/systemd/network/20-wired.network << 'EOF'
[Match]
Name=<YOUR_INTERFACE>

[Network]
Address=<YOUR_VPS_IP>/32
Gateway=<YOUR_GATEWAY_IP>
DNS=<YOUR_DNS_IP>

[Route]
Gateway=<YOUR_GATEWAY_IP>
GatewayOnLink=yes
EOF

Replace:

  • <YOUR_INTERFACE> typically ens3 or eth0
  • <YOUR_VPS_IP> your VPS static IP
  • <YOUR_GATEWAY_IP> the gateway shown by ip route show
  • <YOUR_DNS_IP> the DNS server (OVH provides 213.186.33.99 for US/CA region)

Enable networking services:

systemctl enable systemd-networkd
systemctl enable systemd-resolved

Important: After the system is running, set /etc/resolv.conf to use your DNS directly for best performance:

echo "nameserver <YOUR_DNS_IP>" > /etc/resolv.conf

OVH's local DNS server resolves significantly faster than external DNS when queried from within OVH's network.

Important: Mask systemd-networkd-wait-online to prevent a ~2 minute boot delay that blocks nginx and all other network-dependent services:

systemctl mask systemd-networkd-wait-online

System Essentials

# Hostname
echo "<YOUR_HOSTNAME>" > /etc/hostname

# Timezone (UTC recommended for servers)
ln -sf /usr/share/zoneinfo/UTC /etc/localtime

# Locale
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf

# Root password
passwd

SSH Access

Configure SSH to prevent lockout after reboot:

cat > /etc/ssh/sshd_config << 'EOF'
Port <YOUR_SSH_PORT>
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
AuthorizedKeysFile .ssh/authorized_keys
Subsystem sftp /usr/lib/ssh/sftp-server
UseDNS no
EOF

Create your admin user and inject your SSH key:

useradd -m -G wheel -s /bin/bash <YOUR_USERNAME>
passwd <YOUR_USERNAME>
mkdir -p /home/<YOUR_USERNAME>/.ssh
echo "<YOUR_SSH_PUBLIC_KEY>" > /home/<YOUR_USERNAME>/.ssh/authorized_keys
chown -R <YOUR_USERNAME>:<YOUR_USERNAME> /home/<YOUR_USERNAME>/.ssh
chmod 700 /home/<YOUR_USERNAME>/.ssh
chmod 600 /home/<YOUR_USERNAME>/.ssh/authorized_keys

Enable sudo for the wheel group:

echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers

Enable sshd:

systemctl enable sshd

Bootloader

Install GRUB to the disk (BIOS mode OVH VPS uses BIOS, not UEFI):

grub-install --target=i386-pc /dev/sdX
grub-mkconfig -o /boot/grub/grub.cfg

Step 5: Finalizing and First Boot

Exit the chroot and unmount

exit
umount -R /mnt

Switch boot mode back in OVH Control Panel

  1. Go back to OVH Control Panel > Boot
  2. Click ... and select Reboot my VPS
  3. Reboot the VPS

Verify SSH access

Wait about 30 seconds, then connect using your configured SSH port:

ssh -p <YOUR_SSH_PORT> <YOUR_USERNAME>@<YOUR_VPS_IP>

Verify network

ip addr show
ping -c 3 archlinux.org

Post-Installation Notes

Fix DNS for OVH Network Performance

systemd-resolved adds latency on OVH's network. Use OVH's regional DNS directly instead:

sudo rm /etc/resolv.conf
echo "nameserver <YOUR_DNS_IP>" | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf   # prevent overwrites on reboot

Verify response time:

curl -o /dev/null -s -w "DNS: %{time_namelookup}\nTotal: %{time_total}\n" https://archlinux.org

DNS lookup should be under 5ms from within OVH's network.


Fix Boot Time — Mask systemd-networkd-wait-online

On OVH VPS with a static IP, systemd-networkd-wait-online serves no purpose and causes a ~2 minute boot delay that blocks all network-dependent services. Mask it permanently:

sudo systemctl mask systemd-networkd-wait-online
sudo systemctl daemon-reload

Verify boot time after next reboot:

systemd-analyze
# Expected: under 10 seconds total

Install QEMU Guest Agent

Required for OVH snapshots to flush disk buffers correctly and for graceful shutdowns from the control panel:

sudo pacman -S qemu-guest-agent

The service is socket-activated and does not need to be manually enabled.


Install an AUR Helper

yay is required for packages not available in the official Arch repositories:

sudo pacman -S --needed git base-devel
cd /tmp
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si

Web Server User

On Arch Linux, the web server user is http, not www-data. Update ownership of all web files accordingly:

sudo chown -R http:http /var/www/

Any application config files that reference www-data must be updated to http.


Package Manager

Use pacman for all official repository packages and yay for AUR packages:

sudo pacman -S <package>    # official repos
yay -S <package>            # AUR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment