Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,44 @@ ARG TERMUX__CACHE_DIR

# Install generated rootfs containing:
# - termux bootstrap
# - aosp-libs (bionic libc, linker, boringssl, zlib, libicuuc, debuggerd)
# - aosp-utils (toybox, mksh, iputils)
# - aosp-libs (bionic libc, linker, boringssl, zlib, libicuuc, debuggerd, depends on resolv-conf)
# - aosp-utils (toybox, grep, mksh, iputils)
# - libandroid-stub
# - dnsmasq
# Since /system is now a symbolic link to $PREFIX/opt/aosp,
# which has contents that can be updated by the system user via apt,
# the entire rootfs is now owned by the system user (1000:1000).
# the entire rootfs is now owned by the system user (1000:1000),
# except for /data and /data/data (see below)
COPY --chown=1000:1000 ${TERMUX_DOCKER__ROOTFS} /

# Docker uses /bin/sh by default, but we don't have it.
ENV PATH=/system/bin
ENV PATH=${TERMUX__PREFIX}/bin
SHELL ["sh", "-c"]

# Prevent the unprivileged user from having read access to
# /data and /data/data just like all real Android devices
# this will enable termux-docker to reproduce bugs (for debugging and development)
# like this one:
# https://github.com/termux/termux-packages/issues/28433
# but unfortunately it won't enable termux-docker to reproduce bugs that actually
# require / to be inaccessible to reproduce, like this one:
# https://github.com/termux-user-repository/tur/issues/1897
# it doesn't seem possible to set chmod 771 persistently on /
# within docker, since it reverts to 755 immediately when the container is run.
RUN chown root:root /data/ /data/data/
RUN chmod 771 /data/ /data/data/

# Install updates and cleanup
# Start dnsmasq to resolve hostnames, and,
# for some reason the -c argument of toybox-su is not working,
# so this odd-looking script forces the update process
# to work using the -s argument of toybox-su instead, which is working.
RUN sh -T /dev/ptmx -c "$TERMUX__PREFIX/bin/dnsmasq -u root -g root --pid-file=/dnsmasq.pid" && \
sleep 1 && \
echo '#!/system/bin/sh' > /update.sh && \
echo "PATH=$TERMUX__PREFIX/bin" >> /update.sh && \
echo "source $TERMUX__PREFIX/bin/termux-setup-package-manager" >> /update.sh && \
echo 'if [ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" ]; then' >> /update.sh && \
echo 'apt update' >> /update.sh && \
echo 'apt upgrade -o Dpkg::Options::=--force-confnew -y' >> /update.sh && \
echo 'elif [ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" ]; then' >> /update.sh && \
echo 'pacman-key --init' >> /update.sh && \
echo 'pacman-key --populate' >> /update.sh && \
echo 'pacman -Syyu --noconfirm' >> /update.sh && \
echo 'fi' >> /update.sh && \
chmod +x /update.sh && \
su system -s /update.sh && \
rm -rf /update.sh \
"${TERMUX__PREFIX}"/var/lib/apt/* \
USER 1000:1000
RUN . $TERMUX__PREFIX/bin/termux-setup-package-manager && \
if [ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" ]; then \
apt update && \
apt upgrade -o Dpkg::Options::=--force-confnew -y; \
elif [ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" ]; then \
pacman-key --init && \
pacman-key --populate && \
pacman -Syyu --noconfirm; \
fi && \
rm -rf "${TERMUX__PREFIX}"/var/lib/apt/* \
"${TERMUX__PREFIX}"/var/log/apt/* \
"${TERMUX__CACHE_DIR}"/apt/* \
"${TERMUX__PREFIX}"/var/cache/pacman/pkg/* \
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,16 @@ Available tags:
- `x86_64`
- `latest` (multiplatform)

The other package manager for Termux, pacman, is available in a separate image:

```.sh
docker run -it termux/termux-docker-pacman
```

If architecture is not compatible with host, the additional setup will
be needed. Read this document further to learn how you can run containers
of incompatible CPU architecture.

**Important note**: do not pass `--user` option to Docker command line.
The initial user of container must be root. Otherwise DNS will be broken
because of `dnsmasq` server failure.

### Running ARM containers

In order to run AArch64 container on x86(64) host, you need to setup
Expand Down Expand Up @@ -133,6 +135,13 @@ Podman:
./generate.sh --podman
```

Pacman:

```.sh
export TERMUX_PACKAGE_MANAGER=pacman
./generate.sh
```

## Known issues

There a number of known issues which may not be resolved:
Expand Down
9 changes: 0 additions & 9 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,9 @@ if [ $# -lt 1 ]; then
fi

if [ "$(id -u)" != "0" ]; then
echo "[!] Container is running as non-root, unable to start dnsmasq. DNS will be unavailable." >&2
exec "$@"
fi

if [ -z "$(pidof dnsmasq)" ]; then
/system/bin/sh -T /dev/ptmx -c "dnsmasq -u root -g root --pid-file=/dnsmasq.pid" >/dev/null 2>&1
sleep 1
if [ -z "$(pidof dnsmasq)" ]; then
echo "[!] Failed to start dnsmasq, host name resolution may fail." >&2
fi
fi

exec /system/bin/su -s "$PREFIX/bin/env" system -- \
-i \
ANDROID_DATA="$ANDROID_DATA" \
Expand Down
8 changes: 0 additions & 8 deletions entrypoint_root.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,4 @@ if [ "$(id -u)" != "0" ]; then
exit 1
fi

if [ -z "$(pidof dnsmasq)" ]; then
/system/bin/sh -T /dev/ptmx -c "dnsmasq -u root -g root --pid-file=/dnsmasq.pid" >/dev/null 2>&1
sleep 1
if [ -z "$(pidof dnsmasq)" ]; then
echo "[!] Failed to start dnsmasq, host name resolution may fail." >&2
fi
fi

exec "$@"
27 changes: 14 additions & 13 deletions generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,38 +52,34 @@ fi
case "${TERMUX_PACKAGE_MANAGER}" in
apt)
TERMUX_DOCKER__IMAGE_NAME="termux/termux-docker"
TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.04.26-r1%2Bapt.android-7"
TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.06.07-r1%2Bapt.android-7"
TERMUX_DOCKER__BOOTSTRAP_SRCURL="https://github.com/termux/termux-packages/releases/download/bootstrap-${TERMUX_DOCKER__BOOTSTRAP_VERSION}/bootstrap-${TERMUX_ARCH}.zip"
declare -A REPO_BASE_URLS=(
["main"]="https://packages-cf.termux.dev/apt/termux-main/dists/stable/main"
["root"]="https://packages-cf.termux.dev/apt/termux-root/dists/root/stable"
# ["root"]="https://packages-cf.termux.dev/apt/termux-root/dists/root/stable"
)
;;
pacman)
TERMUX_DOCKER__IMAGE_NAME="termux/termux-docker-pacman"
TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.04.26-r1%2Bpacman.android-7"
TERMUX_DOCKER__BOOTSTRAP_VERSION="2026.06.07-r1%2Bpacman.android-7"
TERMUX_DOCKER__BOOTSTRAP_SRCURL="https://github.com/termux-pacman/termux-packages/releases/download/bootstrap-${TERMUX_DOCKER__BOOTSTRAP_VERSION}/bootstrap-${TERMUX_ARCH}.zip"
declare -A REPO_BASE_URLS=(
["main"]="https://sync.termux-pacman.dev/main"
["root"]="https://sync.termux-pacman.dev/root"
# ["root"]="https://sync.termux-pacman.dev/root"
)
;;
*)
echo "Unsupported package manager \"${TERMUX_PACKAGE_MANAGER}\". Only 'apt' and 'pacman' are supported."
exit 1
;;
esac
echo "[*] Package manager that will be installed in termux-docker image: ${TERMUX_PACKAGE_MANAGER}"

# packages that are extracted, along with their dependencies,
# on top of the bootstrap to form the termux-docker rootfs.
# libandroid-stub is described in multiple places as existing explicitly
# for use with termux-docker, so pulling it in here.
# dnsmasq will not get automatically updated during 'pkg upgrade' by the user
# after termux-docker has been installed, since root-repo is not installed for now
# to imply that other root-packages are not directly supported,
# but aosp-utils, aosp-libs and libandroid-stub will get automatically updated
# by user-invoked instances of 'pkg upgrade' since they are in the main repository.
TERMUX_DOCKER__DEPENDS="aosp-utils, libandroid-stub, dnsmasq"
TERMUX_DOCKER__DEPENDS="aosp-utils, libandroid-stub"
TERMUX_DOCKER__BUILD_DEPENDS="ar, awk, curl, docker, grep, gzip, find, sed, tar, xargs, xz, unzip, jq"
TERMUX_APP__PACKAGE_NAME="com.termux"
TERMUX_APP__DATA_DIR="/data/data/$TERMUX_APP__PACKAGE_NAME"
Expand Down Expand Up @@ -198,7 +194,12 @@ pull_package_apt() {
if [ -n "$package_dependencies" ]; then
local dep
for dep in $package_dependencies; do
if [ ! -e "${TERMUX_DOCKER__PKGDIR}/${dep}" ]; then
# Since termux-docker builds on top of an existing bootstrap,
# the preinstalled packages from the bootstrap also need
# to be checked for duplicate dependencies
if [ ! -e "${TERMUX_DOCKER__PKGDIR}/${dep}" ] && \
! grep -q "^Package: $dep$" \
"${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/dpkg/status"; then
pull_package_apt "$dep"
fi
done
Expand Down Expand Up @@ -321,7 +322,7 @@ pull_package_pacman() {
{
echo "%FILES%"
$TAR xvf package.pkg.tar.xz -C "$TERMUX_DOCKER__ROOTFS" .INSTALL .MTREE data 2> /dev/null | grep '^data/' || true
} >> "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/files"
} > "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/files"
mv "${TERMUX_DOCKER__ROOTFS}/.MTREE" "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/mtree"
if [ -f "${TERMUX_DOCKER__ROOTFS}/.INSTALL" ]; then
mv "${TERMUX_DOCKER__ROOTFS}/.INSTALL" "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/install"
Expand All @@ -335,7 +336,7 @@ pull_package_pacman() {
done
jq -r -j '."'${package_name}'" | to_entries | .[] | select(.key | contains('$(sed 's/^/"/; s/ /","/g; s/$/"/' <<< ${keys_desc})')) | "%",(if .key == "ISIZE" then "SIZE" else .key end),"%\n",.value,"\n\n" | if type == "array" then (.| join("\n")) else . end' \
"${PATH_DB_PACKAGES}"
} >> "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/desc"
} > "${TERMUX_DOCKER__ROOTFS}${TERMUX__PREFIX}/var/lib/pacman/local/${package_desc}/desc"
)
fi
}
Expand Down
Loading