Note trans. : The author of this material is the architect at Barclays and the Open Source-enthusiast from Great Britain Ian Miell. It aims to make a convenient Docker image (with a “sleeping” binary), which does not need to be downloaded, but rather simply copied via copy & paste. By the method of trial, error and experiments with the Assembler-code, it achieves the goal by preparing an image smaller than a kilobyte in size.
Here it is (base64 encoded)H4sICIa2A1sCA2IA7Vrrbts2FFYL7M9+7QUGGNyfDYhtkuJFFLAhWZOhBYJmaLMOWBAEFC+xVlkyJLpYEBjdY+0l+k6jfGvqtkEWp2qD8TMg8vAqnsNzDg9lQhhmEjHDhY4zgWJBBUQJ5ZnCGAubMUQMyhJqoRRMJxYbo7Q2CedYxlQO/myqMroeEEHICIngApspxohEKI4h5DHmGEUQQw7jqAejDjBtnKz9q2w7zubi7gkugazVKHdGuWltQArkWDMCdoCqSpufg/QSPK4aV8pxW+nL96uxzMu39G+NqRe5PeekGj13Oi9BamXRmCtl1dS9X2jqel147C7W+aOJKd8dZ04dlcqsSw7KVyA9Ab/uHT/+cTht6mFRKVkMmywv0yv0mnxbMc8sSP8Apzvg0ViDtJwWxQ54Mpbny5W9qIrp2DSrmt+r+mVenu/ny+UelK6+mFR56VYtjsqfp3mxHupQZqZYdp/NGeo850x99r9j7QloyWEz8kvpK//47vuymvzQ29vf79m8MKnIaIa8bUmwRdByw6TKREIoIzE3xBrjrY7MGDUilomQ3GrNrFaIKqSZ4lkvL3tD12sn/IQCrI10xtcC7C1kH9I+xseQpYilRAwoZ5AI9IcfWFfqpRfzK1M3eeUZDRAfQDGAfc/jHTDKG1fVXiInlzcfctnwLPP9Vszs9VXvUzFy5jlZV5WzTbtN3cWkZWkhL/yS2gXm1p7lumkl24wkpv51FbYcU0EZy7SV0ucEZowkiCjvLbAVikCaGUqhyjT0c0Lj/YrElmmSWANOZ7MooHPwRCiLRaJEzBXKFGTCy49lUHNKjEigVdD6H4uTzPj9wzDCSawU0TQT2ujhjVwjgZzSj/n/eX7D/xPm/T8N/v/Ll/+Lg2fPnxw93eL85xFvyB9Rn4TzXwdAAxiMYLD/t9f/7eM/xDja1P+YBf3vKP7L2+PnttsA/IfjcQiE7nkgdH18Ey4O7pjdH7ygmX0p9n8eFA5aG3pb+0/eP/9jzFmw/13AdTBHK3/OPx7/Ic4X8qecQ9K244QG/98JXh8c/vLwwYM1/TD6KWqpv6LdOb37gT67URKterTpVxu1V9PXq3lW1d8skn++9Y83f4cDeEBAQMBnwliWuTWNu8l33G38/3X3fzGk79wFQ4S4Lwr+vwOcXIJHy4ANkLv4L4APcJ6ZSXUsz+efh1xaSOf3VxstHS6+H/nSu4s6wOns9OugxrdG7WXV5K6qc9NEn0n/ESab+s9o0P+O7v9ce1WzVNI7uAiczYI6BgQEBNwD/AvqV/+XACoAAA==
How did I come to this?
Once a colleague showed a Docker image that he used to test Kubernetes clusters. He did nothing: just ran under and waited for you to kill him.
“Look, it takes only 700 kilobytes! Its really fast to download! ”
And then I was curious what kind of minimal Docker image I can create. I wanted to get one that could be encoded in base64 and sent literally anywhere by simple copy & paste. Since the Docker image is just a tar file, and a tar file is just a file, everything should work out.
Tiny Binary
First of all, I needed a very small Linux binary that doesn't do anything. It will take a bit of magic - and here are two wonderful, informative and worthy articles about creating small executable files:
I didn’t need “Hello World”, but a program that just sleeps and works on x86_64. I started with an example from the first article:
SECTION .data msg: db "Hi World",10 len: equ $-msg SECTION .text global _start _start: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov ebx,0 mov eax,1 int 0x80
Run:
nasm -f elf64 hw.asm -o hw.o ld hw.o -o hw strip -s hw
It turns out the binary is
504 bytes .
But still, not “Hello World” is needed ... First, I found out that the
.data
or
.text
sections are unnecessary and no data loading is required. In addition, the upper half of the
_start
section deals with text output. In the end, I tried the following code:
global _start _start: mov ebx,0 mov eax,1 int 0x80
And he compiled already in
352 bytes .
But this is not the desired result, because the program simply completes its work, but we need it to sleep
as well . As a result of additional research, it turned out that the
mov eax
command fills the processor register with the corresponding Linux system call number, and
int 0x80
makes the call itself. This is described in more detail
here .
And
here I found the desired list. Syscall 1 is
exit
, and the one we need is
syscall 29:pause
. The result was such a program:
global _start _start: mov eax, 29 int 0x80
We saved another 8 bytes: the compilation produced a result of
344 bytes , and now it is a suitable binary that does nothing and waits for a signal.
Delving into hexes
It is time to get a chainsaw and deal with the binary ... For this, I used a
hexer , which is essentially vim for binary files with the ability to directly edit hexes. After lengthy experiments, I got from this:

… this:

This code does the same thing, but notice how many lines and spaces are gone. In the course of my work, I was guided by
such a document , but by and large it was a way of trial and error.
So, the size has decreased to
136 bytes .
Less than 100 bytes?
I wanted to know if it was possible to go further. After reading
this , I assumed that it would be possible to reach 45 bytes, but - alas! - not. The tricks described there are designed only for 32-bit binaries, but for 64-bit ones they did not work.
The best that I managed to do was to take
this 64-bit version of the program and embed it in my system call:
BITS 64 org 0x400000 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq phdr - $$ ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf64_Phdr dd 1 ; p_type dd 5 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq filesize ; p_filesz dq filesize ; p_memsz dq 0x1000 ; p_align phdrsize equ $ - phdr _start: mov eax, 29 int 0x80 filesize equ $ - $$
The resulting image is
127 bytes . At this point, I stopped trying to reduce the size, but accept the offers.
Tiny Docker Image
Now, when there is a binary that implements endless waiting, it remains to put it in the Docker image.
To save every possible byte, I created a binary with a file name of one byte -
t
- and placed it in the
Dockerfile
, creating an almost empty image:
FROM scratch ADD t /t
Note that there is no
CMD
in the
Dockerfile
, as this would increase the size of the image. To run, you will need to pass a command through the arguments to the
docker run
.
Then the
docker save
created a tar file, and then compressed it with maximum gzip compression. The result is a ported Docker image file of less than 1000 bytes in size:
$ docker build -tt . $ docker save t | gzip -9 - | wc -c 976
I also tried to reduce the size of the tar file by experimenting with the Docker manifest file, but in vain: because of the specifics of the tar format and the gzip compression algorithm, such changes only increased the final gzip. I tried other compression algorithms, but gzip turned out to be the best for this small file.
PS from translator
Read also in our blog: