├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── alpine ├── Dockerfile └── syslinux.cfg ├── create_image.sh ├── debian ├── Dockerfile └── syslinux.cfg └── ubuntu ├── Dockerfile └── syslinux.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_store 3 | linux.* 4 | alpine.* 5 | debian.* 6 | ubuntu.* 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amd64/debian:bullseye 2 | LABEL com.iximiuz-project="docker-to-linux" 3 | RUN apt-get -y update 4 | RUN apt-get -y install extlinux fdisk qemu-utils 5 | 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COL_RED="\033[0;31m" 2 | COL_GRN="\033[0;32m" 3 | COL_END="\033[0m" 4 | 5 | UID=$(shell id -u) 6 | GID=$(shell id -g) 7 | VM_DISK_SIZE_MB?=1024 8 | 9 | REPO=docker-to-linux 10 | 11 | .PHONY: 12 | debian: debian.img 13 | 14 | .PHONY: 15 | ubuntu: ubuntu.img 16 | 17 | .PHONY: 18 | alpine: alpine.img 19 | 20 | %.tar: 21 | @echo ${COL_GRN}"[Dump $* directory structure to tar archive]"${COL_END} 22 | docker build -f $*/Dockerfile -t ${REPO}/$* . 23 | docker export -o $*.tar `docker run -d ${REPO}/$* /bin/true` 24 | 25 | %.dir: %.tar 26 | @echo ${COL_GRN}"[Extract $* tar archive]"${COL_END} 27 | docker run -it \ 28 | -v `pwd`:/os:rw \ 29 | ${REPO}/builder bash -c 'mkdir -p /os/$*.dir && tar -C /os/$*.dir --numeric-owner -xf /os/$*.tar' 30 | 31 | %.img: builder %.dir 32 | @echo ${COL_GRN}"[Create $* disk image]"${COL_END} 33 | docker run -it \ 34 | -v `pwd`:/os:rw \ 35 | -e DISTR=$* \ 36 | --privileged \ 37 | --cap-add SYS_ADMIN \ 38 | ${REPO}/builder bash /os/create_image.sh ${UID} ${GID} ${VM_DISK_SIZE_MB} 39 | 40 | .PHONY: 41 | builder: 42 | @echo ${COL_GRN}"[Ensure builder is ready]"${COL_END} 43 | @if [ "`docker images -q ${REPO}/builder`" = '' ]; then\ 44 | docker build -f Dockerfile -t ${REPO}/builder .;\ 45 | fi 46 | 47 | .PHONY: 48 | builder-interactive: 49 | docker run -it \ 50 | -v `pwd`:/os:rw \ 51 | --cap-add SYS_ADMIN \ 52 | ${REPO}/builder bash 53 | 54 | .PHONY: 55 | clean: clean-docker-procs clean-docker-images 56 | @echo ${COL_GRN}"[Remove leftovers]"${COL_END} 57 | rm -rf mnt debian.* alpine.* ubuntu.* 58 | 59 | .PHONY: 60 | clean-docker-procs: 61 | @echo ${COL_GRN}"[Remove Docker Processes]"${COL_END} 62 | @if [ "`docker ps -qa -f=label=com.iximiuz-project=${REPO}`" != '' ]; then\ 63 | docker rm `docker ps -qa -f=label=com.iximiuz-project=${REPO}`;\ 64 | else\ 65 | echo "";\ 66 | fi 67 | 68 | .PHONY: 69 | clean-docker-images: 70 | @echo ${COL_GRN}"[Remove Docker Images]"${COL_END} 71 | @if [ "`docker images -q ${REPO}/*`" != '' ]; then\ 72 | docker rmi `docker images -q ${REPO}/*`;\ 73 | else\ 74 | echo "";\ 75 | fi 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-to-linux - make bootable Linux disk image abusing Docker 2 | 3 | ```diff 4 | ! Support development of this project > patreon.com/iximiuz 5 | ``` 6 | 7 | There is no real goal behind this project. Just out of my curiosity what if: 8 | 9 | - launch a base Linux container (debian, alpine, etc) 10 | - pull in Linux kernel & init system (systemd, OpenRC, etc) 11 | - dump container's filesystem to a disk image 12 | - install bootloader (syslinux) to this image... 13 | 14 | Then it should be probably possible to launch a ~~real~~ virtual machine with such an image! 15 | 16 | Try it out: 17 | 18 | ```bash 19 | # 1. Build the image. 20 | # Depending on your setup, you may need to preceed `make` with `sudo`. 21 | make debian # or ubuntu, or alpine 22 | 23 | # 2. Run it! Use username `root` and password `root` to log in. 24 | qemu-system-x86_64 -drive file=debian.img,index=0,media=disk,format=raw -m 4096 25 | # 2. Alternate 26 | qemu-system-x86_64 -hda debian.qcow2 -m 512 27 | 28 | # 3. Clean up when you are done. 29 | make clean 30 | ``` 31 | 32 | It works! 33 | 34 | You can increase or decrease the size of the VM hard disk via the environment variable VM_DISK_SIZE_MB (1024 by default). 35 | For example for a 3GB disk: 36 | 37 | ``` 38 | make debian VM_DISK_SIZE_MB=3072 # or ubuntu, or alpine 39 | ``` 40 | 41 | Check out `Makefile` for more details or read my article on iximiuz.com. 42 | 43 | ## Features 44 | - Real quick build a bootable Linux image with a single command! 45 | - 3 target distributives: Ubuntu 20.04, Debian Bullseye, Alpine 3.13.5 46 | - Build from macOS (including M1 chips) or Linux hosts 47 | 48 | ## FAQ 49 | - Q: I'm getting an error about "read-only filesystem". How can I make it writable? 50 | 51 | A: It's Linux default behaviour to mount the / filesystem as read-only. You can always remount it with `mount -o remount,rw /`. 52 | 53 | - Q: How can I access network from the VM / How can I SSH into the VM? 54 | 55 | A: Networking is not configured at the moment. If you want to configure it yourself, search for TUN/TAP/bridge devices. Don't forget to open a PR if you come up with a working solution. 56 | 57 | 58 | ## Release notes 59 | #### 2021-05-24 60 | - Start using ext4 instead of ext3. 61 | 62 | #### 2021-05-07 63 | - Fix - Ubuntu 20.04 stopped working because of the changed path to vmlinuz and initrd files. 64 | 65 | #### 2021-05-02 66 | - Fix macOS support [#10](https://github.com/iximiuz/docker-to-linux/issues/10) (thanks to @xavigonzalvo for reporting and suggesting the fix) 67 | - move `losetup` call from Makefile to the builder container 68 | - explicitly select amd64 architecture in target distr Dockerfiles to support builds on ARM hosts (_aka_ M1) 69 | - Upgrade target distr versions 70 | - Ubuntu 18.04 -> 20.04 71 | - Debian Stretch -> Bullseye 72 | - Alpine 3.9.4 -> 3.13.5 73 | 74 | #### 2020-02-29 75 | - Improve Alpine support [#7](https://github.com/iximiuz/docker-to-linux/pull/7) (creds @monperrus) 76 | 77 | #### 2019-08-02 78 | - Fix loopback device lookup [#3](https://github.com/iximiuz/docker-to-linux/pull/3) (creds @christau) 79 | 80 | #### 2019-06-03 81 | - Initial release 82 | 83 | ## TODO 84 | - add basic networking support 85 | - support different image formats (e.g. VirtualBox VDI) 86 | - support different target architectures (e.g. ARM) 87 | 88 | ## Similar projects 89 | 90 | ...or tangential projects relying on the similar technique: 91 | 92 | - [firecracker-microvm/firecracker](https://github.com/firecracker-microvm/firecracker/blob/d2c52b882ced88be581eed4f4a804ffba5a1be86/docs/rootfs-and-kernel-setup.md) suggests a similar trick to build custom VM rootfs images. 93 | - [weaveworks/ignite](https://github.com/weaveworks/ignite/blob/e3766643ffa88ad1d98d0cb40043cd1f94b4632a/images/Makefile) actually uses the same technique to build Firecracker VM images. 94 | - [d2vm (Docker to Virtual Machine)](https://github.com/linka-cloud/d2vm) a CLI tool (written in Go) to build virtual machine images from Docker images (inspired by the docker-to-linux project). 95 | -------------------------------------------------------------------------------- /alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amd64/alpine:3.13.5 2 | LABEL com.iximiuz-project="docker-to-linux" 3 | RUN apk update 4 | RUN apk add openrc linux-virt 5 | RUN echo "root:root" | chpasswd 6 | RUN rc-update add root 7 | 8 | -------------------------------------------------------------------------------- /alpine/syslinux.cfg: -------------------------------------------------------------------------------- 1 | DEFAULT linux 2 | SAY Now booting the kernel from SYSLINUX... 3 | LABEL linux 4 | KERNEL /boot/vmlinuz-virt 5 | APPEND ro root=/dev/sda1 rootfstype=ext4 initrd=/boot/initramfs-virt 6 | 7 | -------------------------------------------------------------------------------- /create_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | UID_HOST=$1 6 | GID_HOST=$2 7 | VM_DISK_SIZE_MB=$3 8 | 9 | echo_blue() { 10 | local font_blue="\033[94m" 11 | local font_bold="\033[1m" 12 | local font_end="\033[0m" 13 | 14 | echo -e "\n${font_blue}${font_bold}${1}${font_end}" 15 | } 16 | 17 | echo_blue "[Create disk image]" 18 | [ -z "${VM_DISK_SIZE_MB}" ] && VM_DISK_SIZE_MB=1024 19 | VM_DISK_SIZE_SECTOR=$(expr $VM_DISK_SIZE_MB \* 1024 \* 1024 / 512) 20 | dd if=/dev/zero of=/os/${DISTR}.img bs=${VM_DISK_SIZE_SECTOR} count=512 21 | 22 | echo_blue "[Make partition]" 23 | echo "type=83,bootable" | sfdisk /os/${DISTR}.img 24 | 25 | echo_blue "\n[Format partition with ext4]" 26 | losetup -D 27 | LOOPDEVICE=$(losetup -f) 28 | echo -e "\n[Using ${LOOPDEVICE} loop device]" 29 | losetup -o $(expr 512 \* 2048) ${LOOPDEVICE} /os/${DISTR}.img 30 | mkfs.ext4 ${LOOPDEVICE} 31 | 32 | echo_blue "[Copy ${DISTR} directory structure to partition]" 33 | mkdir -p /os/mnt 34 | mount -t auto ${LOOPDEVICE} /os/mnt/ 35 | cp -a /os/${DISTR}.dir/. /os/mnt/ 36 | 37 | echo_blue "[Setup extlinux]" 38 | extlinux --install /os/mnt/boot/ 39 | cp /os/${DISTR}/syslinux.cfg /os/mnt/boot/syslinux.cfg 40 | rm /os/mnt/.dockerenv 41 | 42 | echo_blue "[Unmount]" 43 | umount /os/mnt 44 | losetup -D 45 | 46 | echo_blue "[Write syslinux MBR]" 47 | dd if=/usr/lib/syslinux/mbr/mbr.bin of=/os/${DISTR}.img bs=440 count=1 conv=notrunc 48 | 49 | echo_blue "[Convert to qcow2]" 50 | qemu-img convert -c /os/${DISTR}.img -O qcow2 /os/${DISTR}.qcow2 51 | 52 | [ "${UID_HOST}" -a "${GID_HOST}" ] && chown ${UID_HOST}:${GID_HOST} /os/${DISTR}.img /os/${DISTR}.qcow2 53 | 54 | rm -r /os/${DISTR}.dir -------------------------------------------------------------------------------- /debian/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amd64/debian:bullseye 2 | LABEL com.iximiuz-project="docker-to-linux" 3 | RUN apt-get -y update 4 | RUN apt-get -y install --no-install-recommends \ 5 | linux-image-amd64 \ 6 | systemd-sysv 7 | RUN echo "root:root" | chpasswd 8 | 9 | -------------------------------------------------------------------------------- /debian/syslinux.cfg: -------------------------------------------------------------------------------- 1 | DEFAULT linux 2 | SAY Now booting the kernel from SYSLINUX... 3 | LABEL linux 4 | KERNEL /vmlinuz 5 | APPEND rw root=/dev/sda1 initrd=/initrd.img 6 | 7 | -------------------------------------------------------------------------------- /ubuntu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amd64/ubuntu:20.04 2 | LABEL com.iximiuz-project="docker-to-linux" 3 | RUN apt-get update -y 4 | RUN apt-get -y install \ 5 | linux-image-virtual \ 6 | systemd-sysv 7 | RUN echo "root:root" | chpasswd 8 | 9 | -------------------------------------------------------------------------------- /ubuntu/syslinux.cfg: -------------------------------------------------------------------------------- 1 | DEFAULT linux 2 | SAY Now booting the kernel from SYSLINUX... 3 | LABEL linux 4 | KERNEL /boot/vmlinuz 5 | APPEND rw root=/dev/sda1 initrd=/boot/initrd.img 6 | 7 | --------------------------------------------------------------------------------