I have a 64-bit (amd64 a.k.a. x86_64) Debian or Ubuntu installation. I need to run 32-bit (i386/i686) programs occasionally, or to compile programs for a 32-bit system. How can I do this with a minimum of fuss?

Bonus: what if I want to run or test with an older or newer release of the distribution?


4 Answers 4


For current releases

Current Debian and Ubuntu have multiarch support: You can mix x86_32 (i386) and x86_64 (amd64) packages on the same system in a straightforward way. This is known as multiarch support - see Ubuntu or Debian wiki more information.

See warl0ck's answer for a simple, up-to-date answer.

For old releases

In older releases, Debian and Ubuntu ship with a number of 32-bit libraries on amd64. Install the ia32-libs Install ia32-libs package to have a basic set of 32-bit libraries, and possibly other packages that depend on this one. Your 32-bit executables should simply run if you have all the required libraries. For development, install gcc-multilib Install gcc-multilib, and again possibly other packages that depend on it such as g++-multilib. You may find binutils-multiarch Install binutils-multiarch useful as well, and ia32-libs-dev on Debian. Pass the -m32 option to gcc to compile for ix86.

Note that uname -m will still show x64_64 if you're running a 64-bit kernel, regardless of what 32-bit user mode components you have installed. Schroot described below takes care of this.


This section is a guide to installing a Debian-like distribution “inside” another Linux distribution. It is worded in terms of installing a 32-bit Ubuntu inside a 64-bit Ubuntu, but should apply with minor modifications to other situations, such as installing Debian unstable inside Debian stable or vice versa.


The idea is to install an alternate distribution in a subtree and run from that. You can install a 32-bit system on a 64-bit system that way, or a different release of your distribution, or a testing environment with different sets of packages installed.

The chroot command and system call starts a process with a view of the filesystem that's restricted to a subtree of the directory tree. Debian and Ubuntu ship schroot, a utility that wraps around this feature to create a more usable sub-environment.

Install the schroot package Install schroot (Debian) and the debootstrap package Install debootstrap (Debian). Debootstrap is only needed for the installation of the alternate distribution and can be removed afterwards.

Set up schroot

This example describes how to set up a 32-bit Ubuntu 10.04LTS (lucid lynx) alternate environment. A similar setup should work with other releases of Debian and Ubuntu. Create a file /etc/schroot/chroot.d/lucid32 with the following contents:

description=Ubuntu 10.04LTS 32-bit

The line directory=/32 tells schroot where we'll put the files of the 32-bit installation. The line username=yourusername says the user yourusername will be allowed to use the schroot. The line groups=users,admin says that users in either group will be allowed to use the schroot; you can also put a users=… directive.

Install the new distribution

Create the directory and start populating it with debootstrap. Debootstrap downloads and installs a core set of packages for the specified distribution and architecture.

mkdir /32
debootstrap --arch i386 lucid /32 http://archive.ubuntu.com/ubuntu

You almost have a working system already; what follows is minor enhancements. Schroot automatically overwrites several files in /32/etc when you run it, in particular the DNS configuration in /etc/resolv.conf and the user database in /etc/passwd and other files (this can be overridden, see the documentation). There are a few more files you may want to copy manually once and for all:

cp -p /etc/apt/apt.conf /32/etc/apt/      # for proxy settings
cp -p /etc/apt/sources.list /32/etc/apt/  # for universe, security, etc
cp -p /etc/environment /32/etc/           # for proxy and locale settings
cp -p /etc/sudoers /32/etc/               # for custom sudo settings

There won't be a file /etc/mtab or /etc/fstab in the chroot. I don't recommend using the mount command manually in the chroot, do it from outside. But do create a good-enough /etc/mtab to make commands such as df work reasonably.

ln -s /proc/mounts /32/etc/mtab

With the directory type, schroot will perform bind mounts of a number of directories, i.e. those directories will be shared with the parent installation: /proc, /dev, /home, /tmp.

Services in the chroot

As described here, a schroot is not suitable for running daemons. Programs in the schroot will be killed when you exit the schroot. Use a “plain” schroot instead of a “directory” schroot if you want it to be more permanent, and set up permanent bind mounts in /etc/fstab on the parent installation.

On Debian and Ubuntu, services start automatically on installation. To avoid this (which could disrupt the services running outside the chroot, in particular because network ports are shared), establish a policy of not running services in the chroot. Put the following script as /32/usr/sbin/policy-rc.d and make it executable (chmod a+rx /32/usr/sbin/policy-rc.d).

## Don't start any service if running in a chroot.
## See /usr/share/doc/sysv-rc/README.policy-rc.d.gz
if [ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" ]; then
  exit 101

Populate the new system

Now we can start using the chroot. You'll want to install a few more packages at this point.

schroot -c lucid32
sudo apt-get update
apt-get install lsb-core nano

You may need to generate a few locales, e.g.

locale-gen en_US en_US.utf8

If the schroot is for an older release of Ubuntu such as 8.04 (hardy), note that the package ubuntu-standard pulls in an MTA. Select nullmailer instead of the default postfix (you may want your chroot to send mail but you definitely don't want it to receive any).

Going further

For more information, see the schroot manual, the schroot FAQ and the schroot.conf manual. Schroot is part of the Debian autobuilder (buildd) project. There may be additional useful tips on the Ubuntu community page about debootstrap.

Virtual machine

If you need complete isolation of the alternate environment, use a virtual machine such as KVM (qemu-kvm Install qemu-kvm) or VirtualBox.

  • 1
    @Gilles Can you add an explanation of what the [ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" ] condition does? Commented Jun 20, 2014 at 12:37
  • 2
    @FaheemMitha It tests whether the script is running in a chroot. It's there to support booting the chroot installation as a standalone system. Commented Jun 20, 2014 at 13:01
  • 1
    I suggest adding a line like users=faheem to your example. By default, for me at least, I'm not a member of the users group. I would have expected to be, however. Commented Jun 20, 2014 at 17:08

Since Ubuntu 11.04 (natty) and Debian 7.0 (wheezy) introduced multiarch support, 32-bit and 64-bit libraries can coexist on one system. To install a 32-bit library libXX, first add the necessary 32bit architecture to your system:

sudo dpkg --add-architecture i386
sudo apt-get update

Then install the 32bit library:

sudo apt-get install libXX:i386

The ia32-libs package. As of Ubuntu 12.04 precise, it no longer contains any libraries, it only pulls in libXX:i386 packages as dependencies.

The compile part is rather easy for C and C++ programs, add -m32 to CFLAG or CXXFLAG, which will make generated program 32bit, e.g

export CFLAGS="-m32"
gcc main.c -o main

This works for makefile-based projects as well.

  • 2
    apt-get install ia32-libs worked for me (13.04)!
    – Étienne
    Commented May 12, 2013 at 19:43
  • 1
    for ubuntu 14.04, it's "sudo dpkg --add-architecture i386"
    – diyism
    Commented Oct 21, 2015 at 2:39
  • For example, on Debian Jessie: dpkg --add-architecture i386; apt-get update; apt-get install libstdc++5:i386 libpam0g:i386 Commented Apr 28, 2016 at 14:16

If you just have a 32-bit binary you'd like to run on a modern 64-bit Debian/Ubuntu system, do the following:

dpkg --add-architecture i386
apt update
apt install libc6-i386

This has been tested on Debian 9.

  • 1
    I also needed sudo apt-get install lib32z1 for my particular 32-bit executable, if that helps. The error was: sqlite3: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory. Yes, it's 2019 and I can't find a precompiled 64-bit sqlite3 client supported by the sqlite project :( I'm rather limited in what I am permitted to install on this shared resource so I have to pull down precompiled sqlite packages. Thankfully I can install lib32z1. Commented Sep 19, 2019 at 22:19

I wrote about this as a Docker solution - https://bbosmith.medium.com/running-32-bit-i386-applications-on-64-bit-arm-using-docker-de7a198914a2. Anywhere Docker runs this solution will work - just change settings to suit.

I have an answer for running 32 bit apps on any platform that has a docker image available. Sorry this answer doesn't cover compiling. But it is so easy and powerful for running other platform's binaries that it is certainly worth posting about as it is.

Say you want to run date from a 32-bit Debian instance on your 64-bit instance. Obviously this is contrived - insert your binary of choice.

First prove that there is a 32-bit linux capable platform available:

host $ docker buildx imagetools inspect debian
Name:      docker.io/library/debian:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:e97ee92bf1e11a2de654e9f3da827d8dce32b54e0490ac83bfc65c8706568116

  Name:      docker.io/library/debian:latest@sha256:c2cedd7f80a4dd0f9f80d3699bd433ccf3de33ab63bfa2d4c4ba870c998222d6
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64

  Name:      docker.io/library/debian:latest@sha256:e2e12679dbb45a942e627756196cc8a87182c5de53000b021a051733422e1d45
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm/v5

  Name:      docker.io/library/debian:latest@sha256:2acb8966cd93f0928d5ed57116849afae60eb8603797ca9b34801620d35236d9
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm/v7

  Name:      docker.io/library/debian:latest@sha256:6496a3400e12fbff53da7318b304405a5cb2f185a74bdb4458dcc4b645839380
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm64/v8

  Name:      docker.io/library/debian:latest@sha256:0877f432f15595d9754cdd7c01e8afd53767db4b515b9a47d3e67e1e2f40f520
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/386

  Name:      docker.io/library/debian:latest@sha256:f464ff1c8422dce7ce2f9c179b7e39a680748b4d0e3e074ad705cf84d4372a0c
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/mips64le

  Name:      docker.io/library/debian:latest@sha256:5105fb4c3f037b0a6278c3ae1f0320ff1e169a3d7cfb0ca7a7b51f8657039c21
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/ppc64le

  Name:      docker.io/library/debian:latest@sha256:c5bfba100bac4c910c69710efe8a07b4b39d547f3c75722944eb741845742936
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/s390x

Yes! Platform: linux/386.

If not you'll need to try other images instead of Debian (used in this case) or do some duck-duck searching.

Next write a Dockerfile:

FROM debian


The exec form of ENTRYPOINT allows arguments to be passed, such as "+DATE: %D%nTIME: %T"

See https://docs.docker.com/reference/dockerfile/#shell-and-exec-form

Build it with:

host $ docker buildx build --platform <target for build> -t <dockeruser>/<dockerImageToBuild>:latest \
     --push .


host $ docker buildx build --platform linux/386 -t bbos/dateegforso:latest --push .

The tag name (bbos/dateegforso) must be all lowercase.

That pushes the build image to Bob's docker.io repository. Pushing isn't necessary but will persist the image globally.

You will need to login first to enable this push.

docker login

Run this using:

docker run --init --platform <platform to run if multiple> <dockeruser>/<dockerImageToBuild>:latest <args>


docker run --init --platform linux/386 bbos/dateegforso:latest "+DATE: %D%nTIME: %T"

(That image currently exists and that command should work as-is).

--init isn't necessary, but you'll need this if you have a long running task (obviously not date) and you need to kill it. Otherwise ctrl-c sends it in to la-la land.

You can run this ANYWHERE docker does. x64 Linux, AMD, AARM64, ..

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .