Vyline DOCS

Android Docker Host

This configuration runs Docker Engine on a rooted arm64 Android device. It does not mean generic Android support: kernel, mount, storage, and networking requirements must be checked individually.

Scope

This requires more host work than a normal Linux server. Verify not only the Docker CLI, but container startup, outbound traffic, published ports, and persistence.

For the full procedure from start to finish, see Android Complete Guide. This page is the shorter entry point for identifying which case your device falls into.

Layout

Android arm64 device
├─ Android OS / vendor kernel
│  ├─ root (KernelSU / Magisk etc.)
│  ├─ container-oriented kernel config
│  ├─ Android netd / policy routing
│  └─ SELinux
├─ Termux
│  ├─ BusyBox / SSH / tmux
│  └─ fetch Ubuntu rootfs
├─ Ubuntu rootfs (real chroot)
│  ├─ Docker Engine / containerd / runc
│  └─ Compose
├─ ext4 loop image → /var/lib/docker
└─ containers
   └─ vyline

optional:
   Portainer / Tailscale / Cloudflare Tunnel etc.

proot-distro is useful for downloading the rootfs, but Docker Engine itself runs in a real chroot. Portainer and remote-access tools are not Vyline requirements.

Preflight

uname -m
id
getenforce
cat /proc/cgroups
mount | grep -E 'cgroup|cgroup2'
ls -l /dev/block/loop* 2>/dev/null

In addition to arm64 and root, check namespaces, cgroups, seccomp, OverlayFS, veth/bridge, netfilter, loop/ext4, and other Docker kernel requirements. See Android Kernel for details.

What happensWhere to go next
dockerd itself does not startCheck Kernel, storage, and cgroups
hello-world works but the container cannot reach the InternetNetworking
Internet works but LAN cannot reach :3000Check published-port and nft/legacy packet paths
Docker and networking both workProceed to Vyline Compose

Termux

pkg update -y
pkg install -y proot-distro busybox openssh tmux coreutils curl
termux-wake-lock
proot-distro install ubuntu

Typical Ubuntu rootfs location:

/data/data/com.termux/files/usr/var/lib/proot-distro/containers/ubuntu/rootfs

Mounts for the real chroot

PREFIX=/data/data/com.termux/files/usr
ROOT="$PREFIX/var/lib/proot-distro/containers/ubuntu/rootfs"
BB="$PREFIX/bin/busybox"

su
$BB mount --bind "$ROOT" "$ROOT"
$BB mount --make-rslave "$ROOT"
$BB mount --rbind /dev "$ROOT/dev"
$BB mount --make-rslave "$ROOT/dev"
$BB mount -t proc proc "$ROOT/proc"
$BB mount --rbind /sys "$ROOT/sys"
$BB mount --make-rslave "$ROOT/sys"
$BB mount -t tmpfs -o mode=755,nosuid,nodev tmpfs "$ROOT/run"

Self-binding the rootfs and making it rslave allows runc's recursive mount operations to work on configurations that otherwise fail with errors such as remount / ... invalid argument.

Move Docker data-root to ext4 only when needed

First test whether overlay2 works on the Android filesystem. Only devices that hit EINVAL with combinations such as F2FS, casefold, and the vendor kernel need to move /var/lib/docker to an ext4 loop image.

stat -f -c '%T' /data
docker info 2>/dev/null | grep -E 'Storage Driver|Backing Filesystem'
mkdir -p /data/local/docker-storage
truncate -s 64G /data/local/docker-storage/docker-ext4.img
/system/bin/mke2fs -t ext4 -F /data/local/docker-storage/docker-ext4.img
LOOP=$(/system/bin/losetup -f)
/system/bin/losetup "$LOOP" /data/local/docker-storage/docker-ext4.img
$BB mount -t ext4 -o rw,noatime "$LOOP" "$ROOT/var/lib/docker"

Docker inside the chroot

{
  "storage-driver": "overlay2",
  "exec-opts": ["native.cgroupdriver=cgroupfs"],
  "log-driver": "local"
}
rm -f /var/run/docker.pid
nohup dockerd >/var/log/dockerd.log 2>&1 </dev/null &
docker run --rm hello-world
docker run --rm alpine uname -m
docker run --rm alpine ping -c 1 1.1.1.1

Networking is a separate problem

Even with Docker running, Android netd policy routing or a legacy/nft mismatch can break container-to-Internet traffic or LAN access to published ports. Follow Android Networking for diagnosis.

Vyline Compose

services:
  vyline:
    image: ghcr.io/tqmane/vyline:latest
    pull_policy: always
    platform: linux/arm64
    ports:
      - "3000:3000"
    volumes:
      - /opt/vyline/data:/app/data
      - /opt/vyline/storage:/app/storage
    environment:
      VYLINE_HOST: 0.0.0.0
      PORT: 3000
      VYLINE_DATA_DIR: /app/data
      VYLINE_STORAGE_DIR: /app/storage
      VYLINE_LAN_ACCESS: "false"
      VYLINE_TRUST_REMOTE_OWNER: "false"
      TZ: Asia/Tokyo
    restart: unless-stopped

Because the image is multi-arch, platform: linux/arm64 is normally optional. Keep it only if you want to be explicit. Use the standard health check first; only on Android chroots where host-side HTTP works but the healthcheck/exec path is broken should you add:

healthcheck:
  disable: true

Even then, verify Vyline itself with curl http://127.0.0.1:3000/healthz before disabling it. See the complete guide for the case where only docker exec has a broken mount namespace.

Search by page, setting, or command