├── .dockerignore ├── .github └── dependabot.yml ├── .semaphore ├── deploy.yml └── semaphore.yml ├── Dockerfile ├── LICENSE ├── README.md ├── files ├── insecure_shared_adbkey ├── insecure_shared_adbkey.pub └── update-platform-tools.sh └── systemd ├── adb-image.service ├── adbd-container.service └── adbd.service /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !/files 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: docker 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.semaphore/deploy.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Deploy to DockerHub 3 | blocks: 4 | - name: Deploy to DockerHub 5 | task: 6 | jobs: 7 | - name: Deploy 8 | commands: 9 | - checkout 10 | - 'docker build . -t "devicefarmer/adb:${SEMAPHORE_GIT_TAG_NAME:-latest}"' 11 | - docker push devicefarmer/adb 12 | secrets: 13 | - name: dockerhub-secrets 14 | prologue: 15 | commands: 16 | - 'echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin' 17 | agent: 18 | machine: 19 | type: e1-standard-2 20 | os_image: ubuntu2004 21 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Docker 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu2004 7 | blocks: 8 | - name: Build 9 | task: 10 | jobs: 11 | - name: docker build 12 | commands: 13 | - checkout 14 | - docker build . 15 | promotions: 16 | - name: Deploy to DockerHub 17 | pipeline_file: deploy.yml 18 | auto_promote: 19 | when: (branch = 'master' OR tag =~ '.*') AND result = 'passed' 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.18.6 2 | 3 | # Set up insecure default key 4 | RUN mkdir -m 0750 /root/.android 5 | ADD files/insecure_shared_adbkey /root/.android/adbkey 6 | ADD files/insecure_shared_adbkey.pub /root/.android/adbkey.pub 7 | ADD files/update-platform-tools.sh /usr/local/bin/update-platform-tools.sh 8 | 9 | RUN set -xeo pipefail && \ 10 | apk update && \ 11 | apk add wget ca-certificates tini && \ 12 | wget -O "/etc/apk/keys/sgerrand.rsa.pub" \ 13 | "https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub" && \ 14 | wget -O "/tmp/glibc.apk" \ 15 | "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r1/glibc-2.35-r1.apk" && \ 16 | wget -O "/tmp/glibc-bin.apk" \ 17 | "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r1/glibc-bin-2.35-r1.apk" && \ 18 | apk add "/tmp/glibc.apk" "/tmp/glibc-bin.apk" && \ 19 | rm "/etc/apk/keys/sgerrand.rsa.pub" && \ 20 | rm "/root/.wget-hsts" && \ 21 | rm "/tmp/glibc.apk" "/tmp/glibc-bin.apk" && \ 22 | rm -r /var/cache/apk/APKINDEX.* && \ 23 | mkdir -p /lib64 && \ 24 | ln -sf /usr/glibc-compat/lib/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 && \ 25 | /usr/local/bin/update-platform-tools.sh 26 | 27 | # Expose default ADB port 28 | EXPOSE 5037 29 | 30 | # Set up PATH 31 | ENV PATH $PATH:/opt/platform-tools 32 | 33 | # Hook up tini as the default init system for proper signal handling 34 | ENTRYPOINT ["/sbin/tini", "--"] 35 | 36 | # Start the server by default 37 | CMD ["adb", "-a", "-P", "5037", "server", "nodaemon"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © Simo Kinnunen. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-adb 2 | 3 | This repository contains a [Dockerfile](https://www.docker.io/) for the [Android Debug Bridge](http://developer.android.com/tools/help/adb.html). It gives you access to platform tools such as `adb` and `fastboot`. 4 | 5 | ## Changes 6 | 7 | Android platform tools are automatically updated to the latest stable version on each docker image build. 8 | 9 | * _2025-03-28_ Platform tools updated to `36.0.0` 10 | * _2024-09-21_ Platform tools updated to `35.0.2` 11 | * _2024-03-26_ Platform tools updated to `35.0.1`, alpine to `3.18.6` 12 | * _2023-12-13_ Alpine updated to 3.18.5 13 | * _2023-10-20_ Platform tools updated to `34.0.5` 14 | * _2023-08-08_ Platform tools updated to `34.0.4`, glibc to `2.35-r1`, alpine to `3.18.3` 15 | * _2023-05-28_ Platform tools updated to `34.0.3` 16 | * _2023-03-04_ Platform tools updated to `34.0.1` 17 | * _2022-10-09_ Platform tools updated to `33.0.3` 18 | * _2022-08-10_ Alpine updated to `3.16.2` 19 | * _2022-08-10_ Alpine updated to `3.16.1` 20 | * _2022-05-24_ Alpine updated to `3.16.0` 21 | * _2022-04-06_ Alpine updated to `3.15.4` 22 | * _2022-04-06_ Alpine updated to `3.15.3` 23 | * _2022-03-23_ Alpine updated to `3.15.2` 24 | * _2022-03-17_ Alpine updated to `3.15.1` 25 | * _2021-11-24_ Alpine updated to `3.15.0` 26 | * _2021-11-13_ Alpine updated to `3.14.3` 27 | * _2021-10-02_ Glibc updated to `2.34-r0` 28 | * _2021-10-02_ Alpine updated to `3.14.2` 29 | * _2021-06-16_ Alpine updated to `3.14.0` 30 | * _2021-04-15_ Alpine updated to `3.13.5` 31 | * _2021-03-26_ Alpine updated to `3.13.3` 32 | * _2021-02-18_ Alpine updated to `3.13.2` 33 | * _2021-01-29_ Alpine updated to `3.13.1` 34 | * _2021-01-15_ Alpine updated to `3.13.0` 35 | * _2020-12-17_ Alpine updated to `3.12.3` 36 | * _2020-12-11_ Alpine updated to `3.12.2` 37 | * _2020-11-05_ Platform tools updated to `30.0.5`, glibc to `2.32-r0` 38 | * _2020-10-22_ Alpine updated to `3.12.1` 39 | * _2020-10-06_ Platform tools updated to `30.0.4` 40 | * _2020-07-01_ Alpine updated to `3.12.0`, platorm tools updated to `29.0.6` 41 | * _2016-07-02_ The image now uses [Alpine](https://hub.docker.com/_/alpine/) as the base image, making it way smaller. Furthermore, downloading the platform tools is now done in a more cunning way, further removing almost all dependencies and reducing image size. Only platform-tools are now included. 42 | * _2016-07-02_ Due to internal ADB changes our previous start command no longer works in the latest version. The command has been updated, but if you were specifying it yourself, make sure you're using `adb -a -P 5037 server nodaemon`. Do NOT use the `fork-server` argument anymore. 43 | * _2016-07-02_ The `.android` directory path has been fixed. Thanks to @alexislg2 for spotting it! 44 | 45 | ## Gotchas 46 | 47 | * The container needs extended privileges for USB access 48 | * The host's `/dev/bus/usb` must be mounted on the container 49 | 50 | ## Security 51 | 52 | The container is preloaded with an RSA key for authentication, so that you won't have to accept a new key on the device every time you run the container (normally the key is generated on-demand by the adb binary). While convenient, it means that your device will be accessible over ADB to others who possess the key. You can supply your own keys by using `-v /your/key_folder:/root/.android` with `docker run`. 53 | 54 | ## Updating the platform tools manually 55 | 56 | If you feel like the platform tools are out of date and can't wait for a new image, you can update the platform tools with the following command: 57 | 58 | ```sh 59 | update-platform-tools.sh 60 | ``` 61 | 62 | It's in `/usr/local/bin` and therefore already in `$PATH`. 63 | 64 | ## Usage 65 | 66 | There are various ways to use this image. Some of the possible usage patterns are listed below. It may sometimes be possible to mix them depending on the case. Also, you don't have to limit yourself to the patterns mentioned here. If you can find another way that works for you, go ahead. 67 | 68 | ### Pattern 1 - Shared network on the same machine (easy) 69 | 70 | This usage pattern shares the ADB server container's network with ADB client containers. 71 | 72 | Start the server: 73 | 74 | ``` 75 | docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb --name adbd devicefarmer/adb 76 | ``` 77 | 78 | Then on the same machine: 79 | 80 | ``` 81 | docker run --rm -ti --net container:adbd devicefarmer/adb adb devices 82 | docker run --rm -i --net container:adbd ubuntu nc localhost 5037 <<<000chost:devices 83 | ``` 84 | 85 | **Pros:** 86 | 87 | * No port redirection required 88 | * No need to look up IP addresses 89 | * `adb forward` works without any tricks (but forwards will only be accessible to the client containers) 90 | 91 | **Cons:** 92 | 93 | * Cannot use bridged (or any other) network on the client container 94 | * Only works if the server and client containers run on the same machine 95 | 96 | ### Pattern 2 - Host network (easy but feels wrong) 97 | 98 | This usage pattern binds the ADB server directly to the host. 99 | 100 | Start the server: 101 | 102 | ``` 103 | docker run -d --privileged --net host -v /dev/bus/usb:/dev/bus/usb --name adbd devicefarmer/adb 104 | ``` 105 | 106 | Then on the same machine: 107 | 108 | ``` 109 | docker run --rm -ti --net host devicefarmer/adb adb devices 110 | docker run --rm -i --net host ubuntu nc localhost 5037 <<<000chost:devices 111 | ``` 112 | 113 | Or on another machine: 114 | 115 | ``` 116 | docker run --rm -ti sorccu/adb adb -H x.x.x.x -P 5037 devices 117 | ``` 118 | 119 | **Pros:** 120 | 121 | * No port redirection required 122 | * No need to look up IP addresses 123 | * `adb forward` works without any tricks (and forwards are visible from other machines) 124 | * No docker network overhead 125 | * ADB server visible from other machines 126 | 127 | **Cons:** 128 | 129 | * ADB server visible from other machines unless the startup command is modified 130 | * Client containers must always use host networking or know the machine's IP address 131 | 132 | ### Pattern 3 - Linked containers on the same machine (can be annoying) 133 | 134 | This usage pattern shares the ADB server container's network with ADB client containers. 135 | 136 | Start the server: 137 | 138 | ``` 139 | docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb --name adbd sorccu/adb 140 | ``` 141 | 142 | Then on the same machine: 143 | 144 | ``` 145 | docker run --rm -ti --link adbd:adbd sorccu/adb \ 146 | sh -c 'adb -H $ADBD_PORT_5037_TCP_ADDR -P 5037 devices' 147 | ``` 148 | 149 | **Pros:** 150 | 151 | * No port redirection required 152 | * No need to manually look up IP addresses 153 | * `adb forward` works without any tricks (but forwards will only be accessible to the client containers over the designated IP) 154 | 155 | **Cons:** 156 | 157 | * Need to always pass the server IP to `adb` with `-H $ADBD_PORT_5037_TCP_ADDR` 158 | * Need to be careful when running the container so that variables get replaced inside the container and not in the calling shell 159 | * Only works if the server and client containers run on the same machine 160 | 161 | ### Pattern 4 - Remote client 162 | 163 | This usage pattern works best when you want to access the ADB server from a remote host. 164 | 165 | Start the server: 166 | 167 | ``` 168 | docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb --name adbd -p 5037:5037 sorccu/adb 169 | ``` 170 | 171 | Then on the client host: 172 | 173 | ``` 174 | docker run --rm -ti sorccu/adb adb -H x.x.x.x -P 5037 devices 175 | ``` 176 | 177 | Where `x.x.x.x` is the server host machine. 178 | 179 | **Pros:** 180 | 181 | * Scales better (can use any number of hosts/clients) 182 | * No network limitations 183 | 184 | **Cons:** 185 | 186 | * Need to be aware of IP addresses 187 | * Higher latency 188 | * You'll need to make other ports (e.g. from `adb forward`) accessible yourself 189 | 190 | ## Systemd units 191 | 192 | Sample [systemd](https://www.freedesktop.org/wiki/Software/systemd/) units are provided in the [systemd/](systemd/) folder. 193 | 194 | | Unit | Role | Purpose | 195 | |------|------|---------| 196 | | [adb-image.service](systemd/adb-image.service) | Support | Pulls the image from Docker Hub. | 197 | | [adbd-container.service](systemd/adbd-container.service) | Support | Creates a container for the ADB daemon based on the adb image, but doesn't run it. | 198 | | [adbd.service](systemd/adbd.service) | Primary | Runs the prepared ADB daemon container and makes sure it stays alive. | 199 | 200 | This 3-unit configuration, while slightly complex, offers superior benefits such as incredibly fast start time on failure since everything has already been prepared for `adbd.service` so that it doesn't have to do any extra work. The adb image will only get pulled once at boot time instead of at every launch (or manually by calling `systemctl restart adb-image`, which will also restart the other units). 201 | 202 | Copy the units to `/etc/systemd/system/` on your target machine. 203 | 204 | Then, enable `adbd.service` so that it starts automatically after booting the machine: 205 | 206 | ```sh 207 | systemctl enable adbd 208 | ``` 209 | 210 | Finally, either reboot or start the service manually: 211 | 212 | ```sh 213 | systemctl start adbd 214 | ``` 215 | 216 | If you change the units, don't forget to run `systemctl daemon-reload` or they won't get updated. 217 | 218 | ## Thanks 219 | 220 | * [Jérôme Petazzoni's post on the docker-user forum explaining USB device access](https://groups.google.com/d/msg/docker-user/UsekCwA1CSI/RtgmyJOsRtIJ) 221 | * @sgerrand for [sgerrand/alpine-pkg-glibc](https://github.com/sgerrand/alpine-pkg-glibc) 222 | * @frol for [frol/docker-alpine-glibc](https://github.com/frol/docker-alpine-glibc) 223 | 224 | ## License 225 | 226 | See [LICENSE](LICENSE). 227 | -------------------------------------------------------------------------------- /files/insecure_shared_adbkey: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCppPEDWVQFScXE 3 | A/Z5KkDP62pbSSYiFOvpK4ixvAGOoeFD/Jw8m0VtxCsjiXylNaFXA10smHi1Hw3n 4 | vMruEYpQVv3kB4ij0NeZXZ+7jg+lkkQErWiWhACxerOjuOTbnoMtqdDZWwyl1Cd8 5 | ob2AWrVPZmTWoHG8Rw5yrUgDDYMgFMZpmywbXBqpSgw9NdzfLPGuH89LfzpE0hIN 6 | tOsIDjC2GG52h7hTBg6YwtSmFIMO/dk3jEob8eBKVaa5sB19m0POd+9E4Hlo2fEs 7 | +JbnluR7y9YKiTuXZDXTtcvZyg+aIreVtfOzpLbPcBUzBVSIYdc2z+Vq74r/f8pk 8 | QfiCwnHNAgMBAAECggEAZtD44ba6HZpgqjRcpYLeVSWxCDKFUhKsCF3CMzZnGzMx 9 | fCsV5gWVRrmmC5vAV8DxT/NR/T1LqzpvCwx5UWCAG8Edj56hSefBQ8pijSHEiezk 10 | HJGc2dyXLvnW9luRGSoxBvPtCE8Ok1LJu9erKqfPS5gbdZk4VYwbTZWIF5GQ71er 11 | bVS3jwKvqkUgGiZvfVTHmCCx7LGg6vpSS7dfAIPF2lBQfzfIN+epvXhSN8v2w8Lm 12 | 6IYqEI9GVBF7uflk+ogn/2gsZRx2nZzKrPJV1XbHebPOtWNOkDlK+zF8IYAz8e8k 13 | eHBD9DrWX/SlKbklMdGxRqWxayvASSgBjQqjykqKAQKBgQDQkhLkZdpHtLIhZeeB 14 | lwNTrLPiGzg4xMWgeFIMkAxb7ZRWAmbD1OL4OGC+brpb0aQqE2xdlvNcgCq84lof 15 | guLcpu6KJ7qdUzERZicdDtph4NGAKS6djhurHAdQsLaTs6wq57zPWOBYymBAV5TE 16 | 7m5d6/JZsNNEPbaPC8pY8ASsHQKBgQDQOMZ7jibT2gI4fxhCMvGJ6q5tSGKOBozZ 17 | FDcgua2KIqgD73XuW+bAIi/4uPzwvdRUk/quI5ZECfMgNzTLKqOOV/OIz7/ZIduO 18 | gWKM7OskF1JY6ihsTwvVgOUj2TtbZzDZdN1fuUk5FYq4QFr2wXQzlHMSczuAAnEL 19 | kU/dT7kNcQKBgQCLAtDUqY3yfNy8pc7G8H+nJVQ/PyUZsQyHB6qn9NpH6vES4kbb 20 | /ufHyMuyINrUl8Vyxb9UIWfSHxpdCgBHQFUz+47BRfl7IhdyIUOwelXTJqR7ZvdK 21 | y4xlXykA/saxau81KX8OM45Tn47HU5g0KTYmIzxDyzcEJJ2oeZND87UpgQKBgQCR 22 | 3eQ19CyBJu19VJPS9Es/Obd9+UKJik8rV70S4OCQr5ySPTOZiqoJGSoQDM+tet5/ 23 | bbckPOvsuCeo/uOuHC297yE9S4RzgQOFPmCipupHO0tF3Kv6zBlXNVfQmEK70ntn 24 | KzZV88A3DD9Euli/GmDkLW+7khwxngRBfUe8mzfhEQKBgQCSZDQj+luSV3NYoQN8 25 | Uhoz/dpWA1mFYSsRj61K/o0kfscPGoEEhpwJQFIJZBtVaUQlR74nhPBqfk2pHzl/ 26 | LYiHVMKzJ4LZ+60GYay6v2oaL0gXDSc/OiPV1HbvZ6RU4GuuI+5UyeBCyADxN9yE 27 | Ql9mSkxC8xWUlCBzS8Nojdsybw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /files/insecure_shared_adbkey.pub: -------------------------------------------------------------------------------- 1 | QAAAAPsciFrNccKC+EFkyn//iu9q5c8212GIVAUzFXDPtqSz87WVtyKaD8rZy7XTNWSXO4kK1st75Jbnlvgs8dloeeBE73fOQ5t9HbC5plVK4PEbSow32f0OgxSm1MKYDgZTuId2bhi2MA4I67QNEtJEOn9Lzx+u8Szf3DU9DEqpGlwbLJtpxhQggw0DSK1yDke8caDWZGZPtVqAvaF8J9SlDFvZ0Kktg57b5Lijs3qxAISWaK0ERJKlD467n12Z19CjiAfk/VZQihHuyrznDR+1eJgsXQNXoTWlfIkjK8RtRZs8nPxD4aGOAbyxiCvp6xQiJklbauvPQCp59gPExUkFVFkD8aSp84VBRBR0aoMw7zpCFsWQeOzuXAdHpWhJDRmaY3+Z835gOd4g4Qo39noyy0bsMEQOlDZNnXE633J5kdm7cqA7R5gFGI421i1wOKzMuHilolYZf0chRDFZXxPgDNunlQecGWHW1upm+O+/6GcXAsiOZcjthcE95FW3G9Psu/c/RhAPCB1dy8yyJGKkNk7n8yXAJbIYsSnhoM39eIcSGbtxxXB27V22n+KuzQRq7FubtYbqO/I+W0YzojuoWLrflwtmtcDPGZgI+pcAhXHpF4zvxOsi3HYemZ4dre3ELjQQ3vGwYRCJ23pqya8ifQxMrzZs7YFT91I/0V03BEquRpYYNgEAAQA= insecure_shared_adbkey -------------------------------------------------------------------------------- /files/update-platform-tools.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -euo pipefail 3 | 4 | install_platform_tools() { 5 | local URL="https://dl.google.com/android/repository/platform-tools-latest-linux.zip" 6 | local TMPFILE=$(mktemp) 7 | 8 | mkdir -p /opt 9 | echo "Fetching ${URL}" >&2 10 | wget -O "${TMPFILE}" "${URL}" 11 | 12 | echo "Removing previous version of platform tools if any" >&2 13 | rm -rf /opt/platform-tools 14 | 15 | echo "Unpacking platform tools" >&2 16 | unzip -d /opt "${TMPFILE}" 17 | rm "${TMPFILE}" 18 | 19 | local VERSION=$(grep Revision /opt/platform-tools/source.properties |cut -d '=' -f 2) 20 | echo "Platform tools version: ${VERSION} installed!" >&2 21 | } 22 | 23 | install_platform_tools 24 | -------------------------------------------------------------------------------- /systemd/adb-image.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Pull ADB docker image 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | ExecStart=/usr/bin/docker pull sorccu/adb:latest 10 | -------------------------------------------------------------------------------- /systemd/adbd-container.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Create ADB daemon docker container 3 | Requires=docker.service adb-image.service 4 | After=docker.service adb-image.service 5 | 6 | [Service] 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | ExecStartPre=-/usr/bin/docker kill adbd 10 | ExecStartPre=-/usr/bin/docker rm adbd 11 | ExecStart=/usr/bin/docker create \ 12 | --name adbd \ 13 | --privileged \ 14 | --net host \ 15 | -v /dev/bus/usb:/dev/bus/usb \ 16 | sorccu/adb:latest 17 | -------------------------------------------------------------------------------- /systemd/adbd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=ADB daemon 3 | Requires=docker.service adbd-container.service 4 | After=docker.service adbd-container.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStart=/usr/bin/docker start -a adbd 9 | ExecStop=/usr/bin/docker exec adbd adb kill-server 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | --------------------------------------------------------------------------------