debootstrap – installing Ubuntu from Ubuntu

I run several Ubuntu servers, and although I usually keep them up to date via apt-get upgrade and do-release-upgrade, it is occasionally useful to start with a completely clean slate and re-install the operating system.

For example, if I want to upgrade my system from Ubuntu Natty to Ubuntu Oneiric, apt-get dist-upgrade (or its equivalent in aptitude) will upgrade all packages in the system to the versions for that release. At the end of the process, I should have the same result as if I did a completely fresh install of Ubuntu Oneiric (ignoring any new packages or configuration files changes), but as any seasoned Debian or Ubuntu user will know this isn’t always the case. Package maintainers do their best to ensure that their packages upgrade cleanly, preserving any customisation you may have made, but are not always successful.

For servers that receive a lot of regular use it’s also common to find installed packages that are no longer used, old configuration files in /etc that are not referred to, and so on.

It is possible to create a fresh Ubuntu installation from an existing system using debootstrap. This is useful if you want to install a fresh OS but want to minimise downtime on your server, or want to do the bulk of the installation remotely (most of the install time is spent downloading and installing packages).

Preparation
Ensure that you have a separate /boot partition. Typing mount at the terminal will show you if you do. Although this method of installing Ubuntu will probably work without this, I’ve only tested it with a separately mounted /boot partition.

For this, you’ll need a filesystem to install to (this will become the root filesystem when you boot into the new OS).

I recommend a dedicated partition for this, though you can use any directory you choose from your existing system. Depending on what you packages you install you can make the partition quite small (I typically use 5GB for console-only systems). See here for information on partitioning.

If you use Logical Volume Manager (LVM) this step is made easy – simply create a new LVM volume using lvcreate. If you don’t use LVM, now might be a good time to start using it, as it makes managing disks and filesystems much more flexible. Have a read of this Ubuntu wiki article to get an overview.

Once you have a partition ready, put an ext4 filesystem on it:

# mkfs.ext4 /dev/sda2

Replace /dev/sda2 with your target partition or LVM volume.

You will also need debootstrap installed:

# apt-get install debootstrap

Installation
Now that our filesystem is ready we can do the installation. This does not affect the running of your existing system, it only installs files into the new OS filesystem. This section can be done remotely (e.g. over SSH).

Mount your target filesystem to a local directory:

# mkdir /target
# mount /dev/sda2

Next, use debootstrap to install the basic Ubuntu system into your new filesystem:

# debootstrap oneiric /target

This process will take a few minutes while the base system is downloaded. You can replace oneiric with whichever distro you’re wanting to install.

Time to do basic configuration. You’ll need to update the following files to suit your system:

  • /target/etc/fstab
  • /target/etc/hostname
  • /target/etc/network/interfaces

For each of these I recommend copying the corresponding file from your existing system and then editing it as appropriate.

Remember that for /etc/fstab your new root filesystem will be whatever /target is mounted to, not your current root filesystem! If you want to use UUIDs in fstab you can use blkid to discover these (you must run this as root).

Now we configure APT – copy over sources.list from your existing system into the new system:

# cp /etc/apt/sources.list /target/etc/apt/sources.list

Go into the new sources.list and change the Ubuntu release name for each repository to oneiric. If you have any other custom settings for APT (e.g. proxy server, package pinning, etc) then also copy over those settings from your existing system.

In a minute you’ll chroot into the new system to complete the installation. First, though, we must mount some system directories inside our target environment so that they’ll be available under the new root:

# mount --bind /dev /target/dev
# mount -t proc /proc /target/proc

And now we chroot:

# chroot /target

A new instance of /bin/sh will run, from within your chroot environment. The root directory inside this shell has been changed to /target. This allows us to run programs as if they were executed on our new system. (Remember, your existing system is untouched, include all processes that it is currently running. You can exit the chroot by simply exiting this shell, and go back into it using chroot /target as before.)

# ls /
bin boot dev etc home lib lost+found media mnt opt proc root run sbin selinux srv sys tmp usr var

Inside the chroot, we run apt-get to upgrade packages in our system. This will take a while to download/install packages.

# apt-get update
# apt-get upgrade

We need to fix our locale manually (this will fix any errors you’ve already seen related to the locale not being set):

# apt-get install language-pack-en-base

Fix your timezone by configuring the tzdata package:

# dpkg-reconfigure tzdata

If you want to install further packages, you can do it at this stage. At the moment you will have a very barebones system, so I recommend at least installing ubuntu-standard so that basic utilities and packages are included. You may also want to install an SSH server and a decent editor. If you’re wanting to install a graphical system install ubuntu-desktop.

# apt-get install ubuntu-standard openssh-server vim

If you’re using LVM, make sure you install lvm2 also (you may have trouble booting otherwise):

# apt-get install lvm2

Finally, we add users to our new installation. Without this step you won’t be able to login.

# adduser jonny
# passwd jonny
# adduser fred
# passwd fred

Add at least one user to the admin group so that they can use sudo.

# adduser jonny admin # add jonny to the admin group

Congratulations, you now have a new Ubuntu installation ready to roll. The last bit we need to configure is the boot loader.

Boot loader configuration
For this step I recommend being at the console of the machine, as this can easily go wrong first time and if you do these steps remotely you may leave your machine stuck at the GRUB menu with no way to boot it back into Ubuntu!

Escape out of your chroot jail by exiting your shell – you should end up back at the shell you started with. You need to mount /boot inside your target filesystem: (use mount to help you if needed)

# mount | grep /boot
/dev/sda1 on /boot type ext2 (rw)
# mount /dev/sda1 /target/boot

Go back into the chroot:
# chroot /target

Install a kernel and GRUB into the new system.

# apt-get install grub-pc linux-image

This will install an autogenerated grub.cfg. If you want to customise your grub.cfg at this point, edit /etc/default/grub. I prefer to see GRUB loading, so set GRUB_TIMEOUT=10 and comment-out the GRUB_HIDDEN_TIMEOUT line. Remember to run update-grub after changing this file.

Reboot
Finally, reboot into your new system.

If you encounter issues booting, use GRUB’s edit mode to tweak your boot options. If you cannot boot into your new system you should be able to boot into the old one by selecting one of the older menu entries.

Once you are in your new system you may want to mount your old root filesystem so that you can copy any outstanding configuration files across (I usually mount it read-only in /old-root.)

Thanks for reading. This article was heavily inspired by Ubuntu’s install guide here.

Leave a comment