now part of Enventron

Turning an i386 (32 bit) installation of Debian into an amd64 (64 bit) installation is becoming popular and, with the new multi-arch capabilities available in Debian since the release of Debian 7 “Wheezy”, it’s possible to do this in place and without a lot of difficulty. This procedure works on a base install in a VM as well as on a desktop machine with KDE and lots of other things installed (roughly 1800 packages in total). YMMV!

Disclaimer: you should ensure that you have good backups of your system before you start a process like this, particularly one that is at best experimental.

There are several other recipes published on how to do this and I have obviously learnt from these recipes. I’ve tried to simplify and make the process more robust than what was undertaken (or at least what was documented) there.

Other recipes have advised stripping the machine back to bare bones prior to starting. While that certainly helps apt in its quest to navigate the dependency graph, it’s not strictly necessary. The recipe here has been tested with

a basic installation including the standard task from tasksel
a basic installation plus standard task plus xfce4 plus xorg
That this recipe worked with non-trivial installations and without additional hand tweaking perhaps indicates that this is a more general recipe for turning an i386 box into an amd64 one.

Planning
First, make sure that the i386 packages you have installed actually have downloadable amd64 counterparts. All packages listed by the command below are going to cause problems later — they can be:

leftover detritus from previous releases (you can probably remove them)
locally compiled packages that you will need to recompile for amd64; removing them now and then installing the amd64 version at the end might be the easiest approach
3rd party i386 packages (like skype or crossover) which will remain on the system as they are and continue to use the i386 libraries

aptitude search '?narrow(?not(?archive("^[^n][^o].*$")),?version(CURRENT))?architecture(i386)'
Every package listed by that command will require a plan.

For those not so familiar with aptitude search patterns, the search pattern selects package versions that are currently installed (?narrow(…, ?version(CURRENT))) and are available from archives other than now and are from the i386 architecture (~ri386) (there are alternative patterns and a discussion in the BTS about how better to do this).

Kernel space and preparations
The machine needs to be running a 64 bit kernel before any of the crossgrading can happen. Fortunately, Debian already provides amd64 kernels within the i386 architecture so this is trivial to do. The recipe below will use dctrl-tools and aptitude so make sure they are installed and, if everything starts going wrong, it’s important to have a statically linked (that is to say, indestructable) version of the busybox Swiss-army knife around.

apt-get install linux-image-amd64 busybox-static dctrl-tools aptitude
dpkg --add-architecture amd64
apt-get update
reboot

Make sure you boot into the amd64 kernel; unfortunately, it will sort lower than the i386 kernel in the grub menu so this won’t be automatic.

Pre-filling apt’s cache
It’s convenient to get apt to download as many packages right now so that we’re not reliant on apt later (in case it breaks as we change libraries underneath it) or we break networking during the crossgrade process. Aptitude can produce a list of all the packages currently installed on the system and then we can ask apt to download their amd64 counterparts.

cd /var/cache/apt/archives
apt-get clean
aptitude search --disable-columns -F%p '~ri386~i!~o' |
xargs -n1 apt-get -o APT::Architecture=amd64 download

Note that the xargs -n1 stage is somewhat inefficient because apt will keep rereading its cache for each invocation and that will slow the process down. This is being done so that any i386-specific packages (libc6-i686 for instance) that are installed won’t prevent apt from downloading all the packages it does know about. Alternatively, filter the list of packages generated by the aptitude search and remove the -n1.

For those not so familiar with aptitude search patterns, the search pattern selects packages that are from the i386 architecture (~ri386) and are installed (~i) and are not obsolete (~o that is they are not left over from a previous release or locally installed).

Getting as far as possible with multi-arch
A lot of progress can be made by just installing the amd64 version of every library that is present and is already marked as being Multi-Arch: same. This means that a good number of libraries are pre-installed before the drastic work begins and it means that as soon as we start installing applications packages, they will start working.

grep-status --field=Status "install ok installed" |
grep-dctrl --field=Multi-Arch same --show-field=Package --no-field-names |
sed 's/$/:amd64/' |
xargs -n1 apt-get -y install

Warning: The use of apt here is slightly problematic — installing the libraries can potentially lead to large swathes of packages being removed here. The packages will get reinstalled later on because they will match the *.deb used in a later loop, but it’s suboptimal. The reason this can go awry here looks like Multi-arch: same libraries depending on non-multiarch’d libraries (the details are hard to tease our after the fact). Adding a –no-remove to the apt-get call above should prevent that from happening (untested).

To explain the rest of this command line, grep-status selects all installed packages, the list is filtered down by grep-dctrl to include only those that are Multi-Arch: same and then we tell apt to install the amd64 versions of those packages. Once again, we do many (inefficient) apt runs to make sure this doesn’t fail because of packages like libc6-i686 which is Multi-Arch: same but doesn’t exist for amd64.

Note: there might be some file overwrite conflicts in this; they are from buggy packages. If it’s something boring like the changelog, then just delete the changelog and reinstall the package for amd64. Removing the i386 package is also an option if you know you’re not going to need it soon, but that can make apt stressed because the package list is no longer consistent.

Go mad and make it amd64
Now we’re up to the irreversible and much more dangerous step: actually converting the installation over to amd64. The approach here is to use busybox to unpack a few key binary packages for us and then to let dpkg install all the packages we’ve already downloaded into the cache using apt.

We crudely destroy the old i386 files from the packages we will unpack using busybox because busybox’s dpkg-deb applet refuses to overwrite existing files. Sending a list of files (and some notes about diversions) to xargs rm is going to generate lots of noisy but harmless errors about not being able to delete directories with rm.

dpkg -L dpkg dash findutils | xargs rm
busybox dpkg-deb -x dpkg* /
busybox dpkg-deb -x dash* /
busybox dpkg-deb -x findutils* /
for i in *.deb; do dpkg -i --force-depends $i; done
apt-get -f install

The binary packages will be unpacked in alphabetical order by the for loop which is silly since that will unpack them in the wrong order according to the dependencies, but since we don’t necessarily have a working Essential set yet, it doesn’t really matter. It’s sufficient to push dpkg through the problem with –force-depends and marvel at how good dpkg is at doing the right thing and continuing to configure packages when it can and how robust maintainer scripts are in being idempotent. The multiple calls to dpkg in this loop is once again going to be slower than one large call, particularly as many of the benefits of triggers are lost in doing this (man-db will be rebuilt a lot…). The reason for doing this rather than just “dpkg -i *.deb” is that others have observed dpkg struggling to handle this crazy step in one run and we don’t want small and expected failures to prevent dpkg from trying more packages. Trading a little time on dpkg doing this all for us automatically rather than requiring manual fixing is definitely preferable. In my experience, the apt-get -f install call at the end usually doesn’t actually have anything to do in any case, but it’s a good opportunity to make sure apt is happy about its new situation.

Note that after unpacking dpkg with busybox, dpkg –print-architecture will report amd64 although the new dpkg package hasn’t actually been installed yet; that comes later. By the time we get to apt-get -f install, we have a fully functioning amd64 apt and we should have a fully functioning amd64 system. Remember that for apt-get, the -f option stands for –fix-broken and has nothing to do with force.

Once more, you may see file overwrite bugs. It’s possible that apt-get -f install won’t know how to deal with this situation and you will have to dpkg -r foo:i386 to remove the i386 package and then apt-get -f install again).

The final apt-get -f install stage may well fail with lots of complaints and a generic “problem solver generated breaks” statement. To solve this (manually), all that is required is a little judicious removal of remaining i386 packages that apt is complaining about and perhaps some aptitude download packagename; dpkg -i packagename_*deb invocations followed by another attempt at apt-get -f install. Once again, a little imperfect that apt might need a little hand-holding to solve the problems here, depending on how complicated the original system was.

Cleaning up
The cross-grade can leave aptitude quite confused about what it should be doing, with lots of packages marked as being in an iB or pB state:

$ aptitude search ~b

iB zip - Archiver for .zip files
pB zip:i386 - Archiver for .zip files

Where i means “currently installed”, p means “currently purged” and B means “will be broken by other actions”. To cancel whatever current actions aptitude has in mind and revert to the current state that apt already has:
aptitude keep-all

The old i386 kernel isn’t going to be able to run userspace any more so it may as well be removed. Expect some harmless warnings about vmlinuz.old and initrd.img.old from the maintainer scripts during this process.

aptitude remove '~ri386~nlinux-~i'

Our final check is to look over the remaining i386 packages on the machine. Hopefully it’s only library packages but there’s a chance there are a couple of library runtime packages that have remained. Manually install the amd64 versions of any non-library packages to get the ones for your (new) native architecture.

aptitude search '~ri386~i'
aptitude search '~ri386~i!~slibs'

The first search shows all i386 packages still installed; the second searches for i386 packages that are installed that are not from the archive section “libs”.

If you know you don’t want to keep any i386 libraries around, then they can all be cleaned out in one swoop.

aptitude remove '~ri386~i'
dpkg --remove-architecture i386