Skip to content

Instantly share code, notes, and snippets.

@oofnikj
Last active September 25, 2025 05:43
Show Gist options
  • Save oofnikj/e79aef095cd08756f7f26ed244355d62 to your computer and use it in GitHub Desktop.
Save oofnikj/e79aef095cd08756f7f26ed244355d62 to your computer and use it in GitHub Desktop.
Install Docker on Termux
KEYMAPOPTS="us us"
HOSTNAMEOPTS="-n alpine"
INTERFACESOPTS="auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
hostname alpine
"
TIMEZONEOPTS="-z UTC"
PROXYOPTS="none"
APKREPOSOPTS="http://dl-cdn.alpinelinux.org/alpine/v3.12/main http://dl-cdn.alpinelinux.org/alpine/v3.12/community"
SSHDOPTS="-c openssh"
NTPOPTS="-c busybox"
DISKOPTS="-v -m sys -s 0 /dev/sda"

Docker on Termux [in a VM]

Create a Linux VM and install Docker in it so you can (slowly) run x86 Docker containers on your Android device.

Recommended to use SSH or external keyboard to execute the following commands unless you want sore thumbs. See https://wiki.termux.com/wiki/Remote_Access#SSH

  • Install QEMU

     pkg install qemu-utils qemu-common qemu-system-x86_64-headless
    
  • Download Alpine Linux 3.12 (virt optimized) ISO

     mkdir alpine && cd $_
     wget http://dl-cdn.alpinelinux.org/alpine/v3.12/releases/x86_64/alpine-virt-3.12.3-x86_64.iso
    
  • Create disk (note it won't actually take 4GB of space, more like 500MB)

     qemu-img create -f qcow2 alpine.img 4G
    
  • Boot it up

    qemu-system-x86_64 -machine q35 -m 1024 -smp cpus=2 -cpu qemu64 \
      -drive if=pflash,format=raw,read-only,file=$PREFIX/share/qemu/edk2-x86_64-code.fd \
      -netdev user,id=n1,hostfwd=tcp::2222-:22 -device virtio-net,netdev=n1 \
      -cdrom alpine-virt-3.12.3-x86_64.iso \
      -nographic alpine.img
    
  • Login with user root (no password)

  • Setup network (press Enter to use defaults):

     localhost:~# setup-interfaces
     Available interfaces are: eth0.
     Enter '?' for help on bridges, bonding and vlans.
     Which one do you want to initialize? (or '?' or 'done') [eth0] 
     Ip address for eth0? (or 'dhcp', 'none', '?') [dhcp] 
     Do you want to do any manual network configuration? [no] 
     localhost:~# ifup eth0
    
  • Create an answerfile to speed up installation:

    localhost:~# wget https://gist.githubusercontent.com/oofnikj/e79aef095cd08756f7f26ed244355d62/raw/answerfile
    
  • Patch setup-disk to enable serial console output on boot

    localhost:~# sed -i -E 's/(local kernel_opts)=.*/\1="console=ttyS0"/' /sbin/setup-disk
    
  • Run setup to install to disk

    localhost:~# setup-alpine -f answerfile
    
  • Once installation is complete, power off the VM (command poweroff) and boot again without cdrom:

    qemu-system-x86_64 -machine q35 -m 1024 -smp cpus=2 -cpu qemu64 \
      -drive if=pflash,format=raw,read-only,file=$PREFIX/share/qemu/edk2-x86_64-code.fd \
      -netdev user,id=n1,hostfwd=tcp::2222-:22 -device virtio-net,netdev=n1 \
      -nographic alpine.img
    
  • Install docker and enable on boot:

    alpine:~# apk update && apk add docker
    alpine:~# service docker start
    alpine:~# rc-update add docker
    
  • Useful keys:

    • Ctrl+a x: quit emulation
    • Ctrl+a h: toggle QEMU console
@egandro
Copy link

egandro commented Jun 3, 2024

I thought that the performance would be better because it operates on the same architecture as the phone's processor. My assumption was that it would opt for virtualization instead of emulation, leading to superior performance.

If you "need" arm 64 docker and can give me a good reason why, I might consider adding this to my repo. I really see no point at the moment. You might have. That's why I am asking you.

@egandro
Copy link

egandro commented Jun 3, 2024

Guys do you know about proot-distro and ubuntu distro? It's much lighter and faster than qemu and supports docker not sure about alpine

Not true: https://en.wikipedia.org/wiki/Alpine_Linux

Alpine Linux is a Linux distribution designed to be small, simple, and secure. It uses musl, BusyBox, and OpenRC instead of the more commonly used glibc, GNU Core Utilities, and systemd.This makes Alpine one of few Linux distributions not to be based on the GNU Core Utilities.

@egandro
Copy link

egandro commented Jun 28, 2024

I updated to alpine 3.20

@orangeed
Copy link

  Error: Could not retrieve NBP file size from HTTP server.

  Error: Server response timeout.
BdsDxe: failed to load Boot0004 "UEFI HTTPv4 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x2,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)/Uri(): Not Found

what this?

@orangeed
Copy link

_PXE-E16:未收到有效报价。BdsDxe :无法从 PciRoot(0x0)/Pci(0x2,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0.0 加载 Boot0003“UEFI PXEv4 (MAC:525400123456)”:未找到
Did you solve it?

@egandro
Copy link

egandro commented Jul 25, 2024

  Error: Could not retrieve NBP file size from HTTP server.

  Error: Server response timeout.
BdsDxe: failed to load Boot0004 "UEFI HTTPv4 (MAC:525400123456)" from PciRoot(0x0)/Pci(0x2,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)/Uri(): Not Found

what this?

I think the Alpine version that is refered in this post is a bit outdated. Try using 3.19 or 3.20

@orangeed
Copy link

orangeed commented Jul 25, 2024 via email

@jinde98
Copy link

jinde98 commented Nov 6, 2024

why install X86 VMs when phone‘s cup are now arm . Wouldn't installing an arm system run more efficiently?

@orangeed
Copy link

orangeed commented Nov 6, 2024 via email

@daguilera-stc
Copy link

why install X86 VMs when phone‘s cup are now arm . Wouldn't installing an arm system run more efficiently?

Maybe beacuse the most latest docker images are for x86, trying to do with arm docker images is a crazy headache in many cases, you will need build from sources of arm images.

@orangeed
Copy link

orangeed commented Nov 21, 2024 via email

@gardendecoratio
Copy link

Use this one https://github.com/sylirre/vmConsole

@Saikatsaha1996 Thanks, this works, but In this the VM is not getting ip address like 192.168.x.x so that I can ssh. How to do that?

@schnatterer
Copy link

Related: Android 15 QPR2 for Pixel phones now features a preview of a virtualized terminal running Debian that makes installing docker as easy as on a Debian desktop
https://floss.social/@schnatterer/114144395558936957

@orangeed
Copy link

orangeed commented Mar 11, 2025 via email

@snwfdhmp
Copy link

Thanks!

FYI its missing pkg install wget

@orangeed
Copy link

orangeed commented Apr 19, 2025 via email

@hbheiner
Copy link

hbheiner commented Jun 7, 2025

But how to do, if it run and I have to use docker node, in the termux where I setup this vm

@orangeed
Copy link

orangeed commented Jun 7, 2025 via email

@gemfield
Copy link

I am trying to install with the aarch64 qemu but keeps getting /dev/sda is not a block device suitable for partitioning.

All the step went fine but getting error at the Disk & Install step, looks like it is not able to find the disk. which I created using: qemu-img create -f qcow2 alpine.img 20G

I am using below command to boot: qemu-system-aarch64 -machine virt -m 2048 -smp cpus=2 -cpu max -drive if=pflash,format=raw,read-only,file=$PREFIX/share/qemu/edk2-aarch64-code.fd -netdev user,id=n1,hostfwd=tcp::2222-:22 -device virtio-net,netdev=n1 -cdrom alpine-virt-3.19.1-aarch64.iso -nographic alpine.img

@raj-sharma-git replace /dev/sda with /dev/vdb, you can verify "vdb" by the output of fdisk -l on guest os

 Disk & Install
----------------

WARNING: The following disk(s) will be erased:
  vdb	(17.2 GB 0x1af4 )

WARNING: Erase the above disk(s) and continue? (y/n) [n] y
Creating file systems...
mkfs.fat 4.2 (2021-01-31)
Root device:     /dev/vdb2
Root filesystem: ext4
Boot device:     /dev/vdb2
Boot filesystem: ext4
Installing system on /dev/vdb2:
Installing for arm64-efi platform.
Installation finished. No error reported.
100% ████████████████████████████████████████████==> initramfs: creating /boot/initramfs-virt for 6.12.40-0-virt
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-virt
Found initrd image: /boot/initramfs-virt
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done

Installation is complete. Please reboot.

@orangeed
Copy link

orangeed commented Jul 26, 2025 via email

@egandro
Copy link

egandro commented Sep 2, 2025

I updated the installer to 3.22 on my repo. Please give it a try.

(Generic hint. Termux gets outdated very quickly. It's very simple to backup, uninstall, reinstall - instead of trying to "fix" the installation).

https://github.com/egandro/docker-qemu-arm

@orangeed
Copy link

orangeed commented Sep 2, 2025 via email

@egandro
Copy link

egandro commented Sep 2, 2025

Related: Android 15 QPR2 for Pixel phones now features a preview of a virtualized terminal running Debian that makes installing docker as easy as on a Debian desktop https://floss.social/@schnatterer/114144395558936957

There is also udocker (as native - but limited - solution on termux)

$ pkg install udocker
$ udocker run hello-world
Info: creating repo: /data/data/com.termux/files/home/.udocker
Info: udocker command line interface 1.3.17
Info: searching for udockertools >= 1.2.11
Info: installing udockertools 1.2.11
Info: installation of udockertools successful
Info: downloading layer sha256:ca9905c726f06de3cb54aaa54d4d1eade5403594e3fbfb050ccc970fd0212983
Info: downloading layer sha256:198f93fd5094f85a71f793fb8d8f481294d75fb80e6190abb4c6fad2b052a6b6
Warning: check container content: fcb5dd31-fd9a-335a-82e1-1c93700cc3e6

 ******************************************************************************
 *                                                                            *
 *               STARTING fcb5dd31-fd9a-335a-82e1-1c93700cc3e6                *
 *                                                                            *
 ******************************************************************************
 executing: hello

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

@kqvanity
Copy link

@egandro most container don't support arm architectures

@orangeed
Copy link

orangeed commented Sep 19, 2025 via email

@geomlattice
Copy link

geomlattice commented Sep 20, 2025

There is also udocker (as native - but limited - solution on termux)

$ pkg install udocker
$ udocker run hello-world

@egandro Do you know how to properly publish ports? I have tried the following to no avail

udocker run --publish=3333:8888 $CONTAINER_ID

@egandro
Copy link

egandro commented Sep 20, 2025

@egandro most container don't support arm architectures

I agree for the old armv7. A lot support arm64.

@egandro
Copy link

egandro commented Sep 20, 2025

@geomlattice Yes this is a bit odd...

udocker run --publish=3333:8888 $CONTAINER_ID

There is no port forwarding as we have in docker ... Exposed ports > 1024 are directly used and any other are changed.

mkdir -p $HOME/nginx-logs
udocker run \
  --volume=$HOME/nginx-logs:/var/log/nginx \
  nginx

Error: this container exposes privileged TCP/IP ports

 ******************************************************************************
 *                                                                            *
 *               STARTING XXXXXXXXXXXXXXXXXXXXXXXX                *
 *                                                                            *
 ******************************************************************************
 executing: docker-entrypoint.sh
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: ipv6 not available
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up

ATTENTION: A bind system call was requested on port: 80
The port has been changed. If connecting from outside Termux, use: 2080

...

$ curl localhost:2080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

I have no idea if there is a (firewall type) port forwarding as we have in regular docker. But there is an easy work around with custom Dockerfiles.

@valorisa
Copy link

To work around this behavior with udocker, it is recommended to create a custom Dockerfile that exposes a port higher than 1024, or to configure nginx to listen natively on a non-privileged port (e.g., 8080 or 2080), which avoids the problem of forced re-binding of privileged ports.

@geomlattice
Copy link

I understand now. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment