Chroot Ubuntu 14.04 on Android (Nexus 10)

I previously had this tutorial for Ubuntu 12.10 on a Padfone and a Nexus 10. I’ve updated it for Ubuntu 14.04.

In this guide, I used as the repository for the bootstrap. However, I ran into some problems and had to retry the installation several times, and was somewhat slow for me, so I mirrored the trusty armhf repository at I give no guaranties about its uptime or freshness, but if you’re near me (Singapore), you could give it a try to speed things up.

In general, Ubuntu 14.04 doesn’t install as cleanly as 12.10 did, but I believe it should still work. In future, I may have to switch to another distro. Perhaps Arch.

This tutorial documents the steps I went through to get a working Ubuntu system (with LXDE) on my Android device, a Nexus 10. I decided to use the ARM hard-float port (armhf) instead of armle in order to try to extract better performance from the processor.

The tutorial was written based on the notes I made during the process. This tutorial is not for the faint of heart, and it’s best if you have some familiarity with Linux and aren’t afraid of the terminal. I’m not sure if I missed our anything in my notes, so let me know in the comments if anything doesn’t work.

Also, I believe there’s an app that does exactly what I’m trying to do here, but I don’t know if it supports the Padfone yet. If you’re not so comfortable with the terminal, this is probably the way to go.

The following were webpages that I found useful. Some even have almost all the steps you need to do this.


  • A rooted tablet
  • Terminal Emulator – I like this app because the hardware keyboard on the Padfone dock functions like a PC keyboard, with the back button functioning as escape. Very useful, especially when using Vim.
  • At least 2GB of space on your SD card or internal storage. (Following this tutorial exactly will use 3.5GB)
  • An Linux environment (Might work even on a live CD)

You will also likely need busybox on your tablet

Part 1 – Creating the bootstrap image
We are going to create a filesystem image to contain the entire Ubuntu installation. Then we will copy it to the Padfone.

On your Linux desktop (or server!) ensure you have debootstrap installed:

sudo apt-get install debootstrap

Change directory to a partition with enough space. (3.5G in this tutorial, 2GB minimum) For example:

cd /media/BigDisk

Create the empty disk image file:

dd if=/dev/zero of=img.lubuntu-armhf.root bs=8k count=458752
sudo mkfs.ext4 -L chroot -c img.lubuntu-armhf.root
sudo tune2fs -c 0 img.lubuntu-armhf.root

This creates a 3.5GB filesystem. If you want something smaller, like 2GB, substitute 458752 with 262144. Bear in mind that if you make your filesystem only 2GB, you’ll only have about 200MB free after installing the Lubuntu GUI.

Now, loop mount the newly created filesystem:

mkdir chroot
sudo mount -o loop img.lubuntu-armhf.root chroot

Now begin downloading the core system files.

sudo debootstrap --arch armhf --foreign trusty chroot

Take a break while it downloads the core files. Once that is done, unmount the filesystem:

cd ../../
sudo umount chroot

Then copy img.lubuntu-armhf.root to your phone.

Part 2 – Setting up the system
Now that we have the bootstrapped filesystem on the phone, we’re going to set it up and install some software packages.

First, open Terminal Emulator, become root, and navigate to the where the image is. For me, my image was in /sdcard/lubuntu

cd /sdcard/lubuntu

Now, mount the filesystem, setup a minimal environment, and chroot into it. You will need busybox in order to chroot. In my tablet, busybox is installed in /system/bin-busybox.

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/system/bin-busybox"
export TERM=linux
export HOME=/root
export USER=root
export UID=0
mount -o loop img.lubuntu-armhf.root lubuntu
mount -t proc proc lubuntu/proc
mount -t sysfs sysfs lubuntu/sys
mkdir lubuntu/dev/pts
mount -t devpts devpts lubuntu/dev/pts
chroot lubuntu /bin/bash

Now that we’re in, let’s finish up the installation:

/debootstrap/debootstrap --second-stage

You’ll notice that a package failed to install – that’s udev. udev in Ubuntu 14.04 performs several checks and will fail, causing apt to fail to install the entire package. To neuter udev so that we can install the rest of the system, run the following command:

dpkg-divert --local --rename --add /etc/init.d/udev
cp /etc/init.d/udev.distrib /etc/init.d/udev

Now, open /etc/init.d/udev in vi, and right after “### END INIT INFO“, enter this line:

exit 0

We also need to neuter initctl so that the fact that upstart doesn’t work does not give us lots of warnings and failures.

dpkg-divert --local --rename --add /sbin/initctl
cp /bin/true /sbin/initctl

Now do some additional basic configuration. (These settings are for my locale, which is English, Singapore. Modify the commands as necessary to match your actual locale.)

locale-gen en_SG en_SG.UTF-8
dpkg-reconfigure tzdata

Set the hostname and DNS server:

echo localhost > /etc/hostname
echo 'nameserver' > /etc/resolv.conf

Next, open /etc/apt/sources.list in an editor and replace the contents with this:

deb trusty main universe multiverse restricted

deb trusty-updates main universe multiverse restricted

deb trusty-security main universe multiverse restricted

Now we’ll update the apt indexes, upgrade to the latest versions of all the packages, and finish installing the packages that failed to install during the debootstrap.

apt-get update && apt-get upgrade -y

Ok, let’s get the rest of the software. These are all optional, of course, but I like to have SSH, vim and tmux.

apt-get install ubuntu-standard vim tmux openssh-client openssh-server
apt-get clean
rm /var/run/reboot-required*

If you followed me and installed ubuntu-standard, you’d get another failed package: libpam-systemd. To solve this problem, we’re going to create a dummy init.d script. Create a file at /etc/init.d/systemd-logind with 0755 permissions (executable by all). In this file, enter the following:

exit 0

Then, let apt complete the installation by running “apt-get upgrade -y”.

Ok, now let’s create a non-root user and disable the root password. Of course, replace jack with a user name of your choosing.

passwd -l root
adduser jack
usermod -a -G sudo jack

On Android, users which aren’t part of the AID_INET group (Group ID 3003) will not be able to use IP sockets at all. To get around this problem, we’ll create a group called aid_inet in the chroot with the correct group ID and make the non-root user (“jack”) a member of this group.

groupadd --gid 3003 aid_inet
usermod -a -G aid_inet jack

One last thing, it’s convenient to have a script to be able to get into the chroot quickly. Create a file called enter_chrootand place the following script in it:



if [ ! -d $ROOTFS/etc ]; then
    echo "Mounting lubuntu chroot..."
    mount -o loop $PREFIX/img.lubuntu-armhf.root $ROOTFS
    mount -t proc proc $ROOTFS/proc
    mount -t sysfs sysfs $ROOTFS/sys
    mount -t devpts devpts $ROOTFS/dev/pts
    mount --bind /sdcard $ROOTFS/mnt/sdcard
    mount --bind $PREFIX $ROOTFS/mnt/MicroSD

echo "Setting environment vars"
export TERM=linux
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export USER=jack
export SHELL=/bin/bash
export HOME=/root
export LANG=C

echo "Starting shell as user $USER"
/system/bin/chroot $ROOTFS sudo -u $USER -i

This script mounts the filesystems (if they haven’t been mounted) sets up the environment, then enters the chroot properly. Now, to enter the chroot:

sh enter_chroot

The “sh” is necessary because files are not allowed to be executable on the sdcard.

Ok, the command line environment is all done!

Part 3 – Setting up a desktop environment
There are two ways to run a GUI from inside the chroot. Previously, I ran an X11 server in a framebuffer and then used VNC to access it. However, there is now a pretty good X11 server available for Android. I generally use this app. Then all you need to do is install the app, install the “lxde” metapackage, and run “DISPLAY=:0 startlxde”, assuming the app started on display :0.

Note: When I installed lxde, it re-installed libpam-systemd, and two more packages failed. When I purged it (again), those two packages were uninstalled along with it.

That said, if you’re still interested in the VNC on X framebuffer method, take a look at the old article.

8 thoughts on “Chroot Ubuntu 14.04 on Android (Nexus 10)

  1. Berend de Boer

    Very complete guide, great script at the end, best of the approaches I’ve seen so far. I’ve got a problem however: when I do “apt-get update” the system hangs at some point when downloading. The exact location various a bit, but basically tablet (tf300t) requires a hard reboot. This is using an Android 5.1 ROM (KatKiss 5.21).

    Wondering if people have seen similar things. Could be latest Android, could be that the Ubuntu arm packages don’t work so well on my tablet.

    1. tan-ce Post author

      Thanks. When you say the system hangs, do you mean the chroot hangs, or the entire tablet hangs?

      1. Berend de Boer

        Entire tablet at some point. I think the graphics stuff dies first, but can still ssh in (using an Android ssh server). With graphics I mean I can’t wake the screen out of sleep mode, or if it isn’t in sleep mode there are no longer screen updates, nor does screen react to touches.

        With ps I see these processes:

        3654 root 1524 S sh -
        3668 root 3252 S sudo apt-get update
        3669 root 4332 S apt-get update
        3672 root 4200 D /usr/lib/apt/methods/http
        3674 root 3944 S /usr/lib/apt/methods/gpgv
        3681 root 7460 D /usr/lib/apt/methods/bzip2

        sigterm to bzip2 won’t work, neither will sigkill, but either because of the sigkill or some other condition the tablet now will freeze and requires a hard reboot.

      2. Berend de Boer

        BTW, “apt-get update” has worked once for me, when I had just “main” in it. It just seems to lock up randomly, and if you query more repositories, the change becomes 100% I think. Have also installed a package successfully, so bits are working, but not reliably.

  2. Berend de Boer

    One more example, went back to only having the main repository, and didn’t complete this time:

    berend@localhost:~$ sudo apt-get update
    Ign trusty InRelease
    Get:1 trusty Release.gpg [933 B]
    Get:2 trusty Release [58.5 kB]
    Get:3 trusty/main armhf Packages [1295 kB]
    Get:4 trusty/main Translation-en [762 kB]
    Fetched 2117 kB in 15s (137 kB/s)
    Reading package lists... 1%

    With a bit of luck my do so next time.

    1. tan-ce Post author

      That’s really strange… “apt-get update” doesn’t even touch the system, it just downloads the indexes. I’ve never seen this type of issue when I was using my chroot back in KitKat.

      Does it always freeze at “Reading package lists”?

      1. Berend de Boer

        No, the point where it freezes is somewhat random. Could be a kernel issue. I’ll need to see if I can get a different Android 5.1 for my setup. Or perhaps the ARM binaries from Ubuntu 14.04 are “too advanced” (???) for my kernel.

        1. tan-ce Post author

          That’s strange, there shouldn’t be anything you can do to freeze the kernel… unless you’re running out of memory. Before and during the apt-get, perhaps you can run “free -m” to see much memory you’re consuming.


Leave a Reply

Your email address will not be published. Required fields are marked *