The shift toward ARM64 architecture in the consumer and enterprise electronics sectors has fundamentally altered the landscape of containerization. For decades, x86_64 (amd64) served as the monolithic standard for server and desktop computing. However, the emergence of ARM-based silicon—ranging from the NVIDIA Jetson series and Raspberry Pi to the high-performance ARM servers used in cloud environments—has necessitated a specialized approach to Docker implementation. Docker on ARM64 is not merely a port of the existing x86 software; it involves a complex interplay of instruction set architectures (ISA), kernel-level virtualization, and specific image manifests that allow a container designed for one CPU architecture to function, or be built, for another.
At its core, Docker provides an abstraction layer that automates the deployment of applications within software containers. This process involves operating-system-level virtualization on Linux, Windows, and macOS. When dealing with ARM64, the primary challenge is the binary incompatibility between the ARMv8 instruction set and the x86 instruction set. A binary compiled for an Intel or AMD processor cannot execute on an ARM processor without an emulation layer. Consequently, the Docker ecosystem has evolved to support "per-architecture" repositories and multi-architecture manifests, ensuring that the correct binary is pulled based on the host's hardware.
The technical complexity increases when users attempt advanced configurations, such as Docker-in-Docker (DinD) or cross-compilation. DinD allows a Docker daemon to run inside a container, which is essential for CI/CD pipelines (like GitHub Actions or GitLab CI) where the pipeline itself needs to build and push images. On ARM64, this requires specific image variants and privileged access to the host kernel to manage the container namespaces and cgroups. Furthermore, the introduction of rootless modes for ARM64 Docker images addresses critical security concerns by allowing the daemon to run without administrative privileges, although it still requires careful configuration of UID/GID mappings to ensure filesystem permissions are maintained.
ARM64 Docker Image Variants and Repository Structure
The Docker ecosystem manages architecture-specific builds through dedicated repositories. For ARM64, the arm64v8 namespace is the authoritative source for official images tailored for the ARMv8 architecture. These images are maintained by experts within the Docker Project, such as Tianon, ensuring that the binaries are optimized for the AArch64 instruction set.
The arm64v8/docker image provides several distinct variants depending on the intended use case:
arm64v8/docker:<version>: This variant is designed for users who need to interact with a remote Docker engine. It contains the Docker CLI and necessary plugins but does not run the Docker engine internally. This is the ideal choice for lightweight management containers.arm64v8/docker:<version>-dind: This is the "Docker in Docker" variant. It is the default variant and includes both the Docker CLI and the full Docker engine. This allows the container to act as a complete Docker host.arm64v8/docker:<version>-rootless: This experimental variant allows the Docker daemon to run without root privileges. This is a critical security feature for multi-tenant environments, although it still requires specific configurations to function.
The technical distinction between these variants is found in their entrypoints and installed packages. The dind variant is geared toward full virtualization, while the standard variant is a client-only tool. The rootless variant modifies the way the daemon interacts with the Linux kernel, specifically utilizing user namespaces to map a non-privileged user to a root-like identity within the container.
Implementing Docker-in-Docker (DinD) on ARM64
Running Docker inside Docker is a powerful but dangerous capability. It is primarily used for the development of Docker itself or within complex CI/CD orchestrations. Because the Docker daemon requires deep integration with the host's kernel for managing namespaces and networking, it cannot run in a standard restricted container.
To successfully deploy a DinD setup on ARM64, the --privileged flag is mandatory. The technical reason for this is that the inner Docker daemon needs to create its own child containers, which requires the ability to modify the host's kernel parameters and mount filesystems. Without the --privileged flag, the container is blocked by the default Seccomp profile and AppArmor policies of the host.
For versions 18.09 and later, the dind variants automatically generate TLS certificates. This is handled via the DOCKER_TLS_CERTDIR environment variable. In version 18.09 specifically, this behavior was disabled by default to maintain backward compatibility, but it is now a standard feature for securing communication between the Docker CLI and the daemon.
The following command demonstrates the deployment of an ARM64 DinD container:
bash
docker run --privileged --name some-docker -d \
--network some-network --network-alias docker \
-e DOCKER_TLS_CERTDIR=/certs \
-v some-docker-certs-ca:/certs/ca \
-v some-docker-certs-client:/certs/client \
arm64v8/docker:dind
In this configuration, the -v flags create volumes for CA and client certificates, ensuring that the communication remains encrypted. The --network-alias docker allows other containers on the same network to address this engine simply as docker.
Rootless Docker Configuration for ARM64
The arm64v8/docker:dind-rootless image is designed to mitigate the security risks associated with the --privileged flag. While it still requires --privileged for certain DinD functions to work properly, it runs the actual daemon as a non-root user. This limits the blast radius if the container is compromised, as the attacker does not have immediate root access to the host machine.
A basic execution flow for a rootless ARM64 container is as follows:
bash
docker run -d --name some-docker --privileged arm64v8/docker:dind-rootless
To verify that the daemon is running and listening, the logs should be inspected:
bash
docker logs --tail=3 some-docker
Expected output indicates the API is listening on the rootless socket:
text
time="xxx" level=info msg="Daemon has completed initialization"
time="xxx" level=info msg="API listen on /run/user/1000/docker.sock"
time="xxx" level=info msg="API listen on [::]:2376"
For advanced users who need to run the container with a specific User ID (UID) or Group ID (GID) other than the default 1000:1000, the /etc/passwd and /etc/group files must be modified during the build process. This can be achieved by creating a custom Dockerfile:
dockerfile
FROM arm64v8/docker:dind-rootless
USER root
RUN set -eux; \
sed -i -e 's/^rootless:x:1000:1000:/rootless:x:1234:5678:/' /etc/passwd; \
sed -i -e 's/^rootless:x:1000:/rootless:x:5678:/' /etc/group
This technical modification ensures that the internal rootless user matches the external UID/GID requirements of the host environment, preventing permission denied errors when mounting volumes.
Docker Desktop Installation on ARM Linux
Docker Desktop provides a GUI-driven experience and a streamlined installation process for ARM-based Linux distributions, such as Ubuntu 24.04. This is particularly useful for developers using hardware like the System76 Thelio Astra.
The installation process involves obtaining the .deb package specifically compiled for the arm64 architecture. Since the official release notes often highlight amd64 links, users must manually modify the URL to target the ARM64 binary.
The technical process for installation via the terminal is:
bash
wget https://desktop.docker.com/linux/main/arm64/187762/docker-desktop-arm64.deb
sudo apt install ./docker-desktop-arm64.deb
Once installed, the docker version command provides a detailed breakdown of the environment. A typical ARM64 installation output reveals the specific versions of the engine and the underlying components:
| Component | Version | Detail |
|---|---|---|
| Server | Docker Desktop 4.40.0 | Build 187762 |
| Engine | 28.0.4 | API v1.48 |
| Go Version | go1.23.7 | Runtime environment |
| OS/Arch | linux/arm64 | Target architecture |
| containerd | 1.7.26 | Container runtime |
| runc | 1.2.5 | Low-level runtime |
| docker-init | 0.19.0 | Init process |
This stack ensures that the Docker engine is optimized for the ARM64 instruction set, allowing for native performance without the overhead of emulation.
Cross-Compilation and Multi-Architecture Manifests
One of the most significant hurdles in the ARM ecosystem is the ability to build an image on an x86_64 machine and run it on an ARM64 machine (such as an NVIDIA Jetson). This is achieved through cross-compilation and the use of the buildx plugin.
To enable this on an x86_64 host, the system must have QEMU and binfmt-support installed. These tools allow the Linux kernel to recognize and execute binaries meant for different architectures.
The setup command on a Debian/Ubuntu system is:
bash
sudo apt install -y qemu binfmt-support qemu-user-static
Once these dependencies are met, Docker Buildx can be used to target the ARM64 platform:
bash
docker buildx build --platform linux/arm64 -t my-image:arm64 -f Dockerfile . --load
To verify that the resulting image is indeed ARM64, the uname -m command can be executed within the container:
bash
docker run --rm --platform linux/arm64 my-image:arm64 uname -m
The expected output is aarch64, confirming the binary is compatible with ARM64 hardware. However, users must be cautious: while a simple "Hello World" program will work, complex software (like NVIDIA DeepStream 6.3) may fail if the image relies on specific hardware drivers or GPU libraries that are not present in the emulated environment.
The Role of Manifest Lists
The Docker Hub uses "Manifest Lists" to manage multi-architecture images. A manifest list is a JSON object that tells the Docker client which image digest to pull based on the host's architecture.
If a user does not specifically request the application/vnd.docker.distribution.manifest.list.v2+json media type, they may default to an amd64 image, which will fail to execute on ARM hardware.
The structure of a manifest list typically looks like this:
json
{
"manifests": [
{
"digest": "sha256:2518e00b368b533389f0b0934c123d15b364bea00270c25855714afaf7948078",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 2218
},
{
"digest": "sha256:6eed89f4a82c64d6dbee2c4f56abd18932ccf12021fe824e1a1f6568caaf3651",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 2218
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}
This mechanism allows a single tag (e.g., docker:latest) to point to different binaries for different platforms. The Docker client checks the host's architecture, searches the manifest list for a compatible entry, and then queries the specific digest.
Conclusion
The integration of Docker on ARM64 is a sophisticated convergence of software virtualization and hardware specificity. The transition from x86_64 to ARM64 requires a deep understanding of how images are tagged, how manifests are parsed, and how the underlying Linux kernel handles privileged operations. The availability of arm64v8 repositories ensures that the core Docker toolset is optimized for AArch64, while tools like Buildx and QEMU provide the bridge necessary for cross-platform development.
The use of Docker-in-Docker (DinD) and rootless modes on ARM64 highlights the tension between functionality and security. While --privileged mode remains a necessity for the daemon's operation, the move toward rootless configurations and the careful management of UID/GID mappings demonstrate an industry-wide effort to secure containerized environments. Furthermore, the adoption of Docker Desktop for ARM Linux signifies the maturation of the platform, moving it from a niche tool for enthusiasts to a primary development environment for professional engineers.
Ultimately, the success of a Docker deployment on ARM64 depends on the precise selection of image variants and the correct configuration of the runtime environment. Whether deploying on a lightweight IoT device or a high-density ARM server, the reliance on multi-architecture manifests and a robust understanding of the arm64v8 ecosystem is paramount for achieving stability and performance.